Commit Graph

348 Commits

Author SHA1 Message Date
Benjamin Bouvier
7cdfb0d1c0 chore(sqlite): revert the busy_timeout pragmas
Internal Sentry reports tell us that enabling the busy_timeout seems to
have *increased* the number of "database is busy" errors, instead of
lowering those. As a result, we're going to disable the pragmas in all
the places where we enabled it before, and observe how the number of
"database is busy" errors evolves.
2025-06-10 11:13:28 +02:00
Benjamin Bouvier
ebcb74a86d refactor!(event cache): introduce LinkedChunkId in the backends (#5182)
In a "soon" future, threads have their own linked chunk. All our code
has been written with the fact that a linked chunk belong to *a room* in
mind, so it needs some biggish update. Fortunately, most of the changes
are mechanical, so they should be rather easy to review.

Part of #4869, namely #5122.
2025-06-09 13:26:46 +00:00
Benjamin Bouvier
672bb9f460 feat: add the busy timeout pragma to the event cache store acquire() method too
It will tell us if this is sufficient to avoid locking the event cache
store database, now that we have some proof that this is happening in
the wild.
2025-06-03 16:17:36 +02:00
Daniel Salinas
c6e55c1a36 Mechanical move from target_arch="wasm32" to target_family="wasm" 2025-06-02 17:27:34 +02:00
Jonas Platte
8eec683793 refactor: Use inline format arguments more
Automated with cargo clippy --fix --workspace --all-targets.
2025-05-29 13:19:59 +02:00
Damir Jelić
064fd6cb0b fix(sqlite): Use the correct column name for the sender of bundled room keys 2025-05-27 17:46:10 +02:00
Benjamin Bouvier
9e1ea5d7d3 feat(sdk): expose the state store database name 2025-05-27 08:59:59 +02:00
Jonas Platte
3aa356dcd6 chore: Use shorter syntax for workspace inheritance where possible 2025-05-23 10:23:36 +02:00
Benjamin Bouvier
56082f93d0 chore(sdk): forward "database is busy" errors to sentry 2025-05-22 11:50:13 +02:00
Benjamin Bouvier
341f9c267d feat(state store): enable the busy timeout to automatically retry operations on busy dbs
Under some very particular circumstances, the "database is locked" error
can still happen, even in WAL mode, even if the database connection is
not being upgraded from a read transaction to a write transaction.

We *think* this might be the reason behind errors like
github.com/element-hq/element-x-ios/issues/3582, so we're enabling the
sqlite busy_timeout, which will retry the operation after a short sleep,
until the busy timeout is being hit.
2025-05-14 13:27:54 +02:00
Ivan Enderlin
f042084bd2 doc: Generate doc with --generate-link-to-definition.
This patch adds the `--generate-link-to-definition`
argument to `rustdoc` for `docs.rs`. This is using
https://github.com/rust-lang/rust/pull/84176 to add links in the source
code page.
2025-05-08 13:08:32 +02:00
Denis Kasak
fc071bafb2 docs: Various fixes for store-related comments.
- Doc comment for the SQLite-based state store incorrectly referred to
  it as a "cryptostore".
- Consistent capitalisation of SQLite.
- Consistent use of indefinite article "an" before SQLite.
- Fix line length.
2025-05-06 13:55:03 +02:00
Ivan Enderlin
3461b13ec7 doc(sqlite): Add entry in the CHANGELOG.md. 2025-05-06 09:17:54 +02:00
Ivan Enderlin
83e4314645 fix(sqlite): Fix a UNIQUE constraint violation with Update::RemoveItem.
Imagine we have the following events:

| event_id | room_id | chunk_id | position |
|----------|---------|----------|----------|
| $ev0     | !r0     | 42       | 0        |
| $ev1     | !r0     | 42       | 1        |
| $ev2     | !r0     | 42       | 2        |
| $ev3     | !r0     | 42       | 3        |
| $ev4     | !r0     | 42       | 4        |

`$ev2` has been removed, then we end up in this state:

| event_id | room_id | chunk_id | position |
|----------|---------|----------|----------|
| $ev0     | !r0     | 42       | 0        |
| $ev1     | !r0     | 42       | 1        |
|          |         |          |          | <- no more `$ev2`
| $ev3     | !r0     | 42       | 3        |
| $ev4     | !r0     | 42       | 4        |

We need to shift the `position` of `$ev3` and `$ev4` to `position - 1`,
like so:

| event_id | room_id | chunk_id | position |
|----------|---------|----------|----------|
| $ev0     | !r0     | 42       | 0        |
| $ev1     | !r0     | 42       | 1        |
| $ev3     | !r0     | 42       | 2        |
| $ev4     | !r0     | 42       | 3        |

Usually, it boils down to run the following query:

```sql
UPDATE event_chunks
SET position = position - 1
WHERE position > 2 AND …
```

Okay. But `UPDATE` runs on rows in no particular order. It means that
it can update `$ev4` before `$ev3` for example. What happens in this
particular case? The `position` of `$ev4` becomes `3`, however `$ev3`
already has `position = 3`. Because there is a `UNIQUE` constraint
on `(room_id, chunk_id, position)`, it will result in a constraint
violation.

There is **no way** to control the execution order of `UPDATE` in
SQLite. To persuade yourself, try:

```sql
UPDATE event_chunks
SET position = position - 1
FROM (
    SELECT event_id
    FROM event_chunks
    WHERE position > 2 AND …
    ORDER BY position ASC
) as ordered
WHERE event_chunks.event_id = ordered.event_id
```

It will fail the same way.

Thus, we have 2 solutions:

1. Remove the `UNIQUE` constraint,
2. Be creative.

The `UNIQUE` constraint is a safe belt. Normally, we have
`event_cache::Deduplicator` that is responsible to ensure there is no
duplicated event. However, relying on this is “fragile” in the sense it
can contain bugs. Relying on the `UNIQUE` constraint from SQLite is more
robust. It's “braces and belt” as we say here.

So. We need to be creative.

Many solutions exist. Amongst the most popular, we see _dropping and
re-creating the index_, which is no-go for us, it's too expensive. I
(@hywan) have adopted the following one:

- Do `position = position - 1` but in the negative space, so
 `position = -(position - 1)`. A position cannot be negative; we are
  sure it is unique!
- Once all candidate rows are updated, do `position = -position` to move
  back to the positive space.

'told you it's gonna be creative.

This solution is a hack, **but** it is a small number of operations, and
we can keep the `UNIQUE` constraint in place.

This patch updates the `test_linked_chunk_remove_item` to handle
6 events. On _my_ system, with _my_ SQLite version, it triggers the
`UNIQUE` constraint violation without the bug fix.
2025-05-06 09:17:54 +02:00
Richard van der Hoff
a3cb1cd6b5 Merge branch 'main' into rav/history_sharing/save_key_bundle_data 2025-04-24 12:07:21 +01:00
Johannes Marbach
1554e05d8a refactor(send_queue): generalize SentRequestKey::Media and DependentQueuedRequestKind::UploadFileWithThumbnail to prepare for MSC4274 gallery uploads (#4897)
This was broken out of
https://github.com/matrix-org/matrix-rust-sdk/pull/4838 and is a
preliminary step towards implementing
[MSC4274](https://github.com/matrix-org/matrix-spec-proposals/pull/4274).
`SentRequestKey::Media` and
`DependentQueuedRequestKind::UploadFileWithThumbnail` are generalized to
allow chaining dependent media uploads and accumulating sent media
sources.

- [x] Public API changes documented in changelogs (optional)

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Benjamin Bouvier <benjamin@bouvier.cc>
2025-04-24 09:52:33 +00:00
Richard van der Hoff
e89c45ba42 sqlite: store data on received room key bundles 2025-04-23 19:59:24 +01:00
Richard van der Hoff
00364d95af crypto: add methods for room key bundles to store traits 2025-04-23 19:59:24 +01:00
Ivan Enderlin
5d55bb4955 chore: Release matrix-sdk version 0.11.0 2025-04-11 10:51:30 +02:00
Damir Jelić
511cf78d51 chore: Bump the rusqlite version 2025-04-09 16:37:40 +02:00
Ivan Enderlin
ee879354b7 doc(sqlite,ffi): Add #4894 in the CHANGELOG.mds. 2025-04-07 14:05:40 +02:00
Ivan Enderlin
52ec6a4539 feat(sqlite): Add SqliteStoreConfig::with_low_memory_config.
This patch adds a new constructor for `SqliteStoreConfig`, which sets
some defaults tailored for low memory usage.

This patch adds tests asserting the defaults for `new` and
`with_low_memory_config`.
2025-04-07 14:05:40 +02:00
Benjamin Bouvier
d30dc7177f refactor(sqlite): rename gaps to gap_chunks / events_chunks to event_chunks
And some comments have been tweaked too.
2025-04-02 13:26:15 +02:00
Benjamin Bouvier
e42be87798 test(event cache): add tests for save_event() and find_event_relations()
And fix the sql backend \o/
2025-04-02 13:26:15 +02:00
Benjamin Bouvier
524040b33c refactor(event cache): have EventCacheStore::clear_all_rooms_chunks delete all the events' contents 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
0227b3f554 refactor(event cache): regroup code to compute the filter strings 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
6a076b0989 refactor(event cache): don't return the event itself, in find_event_with_relations
And rename it `find_event_relations`.
2025-04-02 13:26:15 +02:00
Benjamin Bouvier
3489cbd5d7 feat(event cache): allow retrieving an event and all its relations 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
65be779bb0 refactor(event cache): move relation extraction into common store helpers 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
45f1dca6a3 refactor(event cache): don't return a position in find_event
Getting the position when reading an event is no longer required:

- the only use case for reading the position out of the event cache was
when we wanted to replace a redacted item into the linked chunk; now
with save_event(), we can replace it without having to know its
position.

As an extra measure of caution, I've also included the room_id in the
`events` table, next to the event_id, so that looking for an event is
still restricted to a single room.
2025-04-02 13:26:15 +02:00
Benjamin Bouvier
913b2a5f78 feat(event cache): allow to persist an out-of-band event into storage 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
3d8af1b972 feat(event cache): extract an event's relationship before inserting it into the database 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
03f5d0222e refactor(event cache): store the events' content independently of their position in a chunk 2025-04-02 13:26:15 +02:00
Benjamin Bouvier
231073c9c3 chore(sqlite): log underlying errors in many OpenStoreError variants
This would help understanding what's the underlying error each time.
2025-04-01 14:51:32 +02:00
Ivan Enderlin
1a1310e205 doc(ffi,sqlite,sdk): Update CHANGELOG.mds. 2025-04-01 11:50:20 +02:00
Ivan Enderlin
bb87a728ac doc(sqlite): Fix a broken link. 2025-04-01 11:50:20 +02:00
Ivan Enderlin
4112162092 feat(sqlite): Add SqliteStoreConfig::path() to override the path.
This patch adds `SqliteStoreConfig::path()` to override the path passed
to the constructor `new`.
2025-04-01 11:50:20 +02:00
Ivan Enderlin
f341572616 feat(sqlite): SqliteStoreConfig implements Clone and Debug.
This patch implements `Clone` and `Debug` for `SqliteStoreConfig`.
2025-04-01 11:50:20 +02:00
Ivan Enderlin
2a67d7472a feat(base,sqlite,indexeddb): Use RoomLoadSettings to load all or one room.
This patch updates `BaseStateStore` and the `StateStore` trait along
with its implementors, to return all rooms or a single room from
`StateStore::get_room_infos`.

See the previous patch for more context.
2025-03-31 16:47:58 +02:00
Ivan Enderlin
8738c4dbfd test(sqlite): Add test for cache_size and journal_size_limit.
This patch adds tests for checking the `PRAGMA cache_size` and `PRAGMA
journal_size_limit`.
2025-03-28 13:36:32 +01:00
Ivan Enderlin
99436f8e79 test(sqlite): Test the new RuntimeConfig type. 2025-03-28 13:36:32 +01:00
Ivan Enderlin
339b220488 feat(sqlite): Introduce RuntimeConfig which includes cache_size
This patch updates `StoreOpenConfig` to hold a new type: `RuntimeConfig`.

This `RuntimeConfig` type is passed to a new `SqliteAsyncConnExt`
method, named `apply_runtime_config`. Depending on the values passed
here, the `optimize`, `cache_size` (new!) and `journal_size_limit`
methods will be called automatically.

The goal of this type is to automate a flow we keep repeating in
all the stores. This is error-prone. This type brings uniformity and
consistency.

This patch also makes all `open_with_pool` methods on the stores private
(they were public before):

1. they were never used as far as I know because getting a `SqlitePool`
   isn't possible since the `pool` attribute is private…
2. it's better to keep control of this flow.
2025-03-28 13:36:32 +01:00
Ivan Enderlin
cfc839f71b doc(sqlite) Add entry to the changelog. 2025-03-26 11:01:52 +01:00
Ivan Enderlin
660d4e7ccb feat(sqlite): Add StoreOpenConfig and open_with_config for all stores.
This patch adds a new `StoreOpenConfing` type to configure the store
when opening it and when creating the pool of connections to SQLite via
`deadpool_sqlite`.

This patch also adds a new `open_with_config` constructor on all
stores, namely `SqliteCryptoStore`, `SqliteEventCacheStore` and
`SqliteStateStore`.
2025-03-26 11:01:52 +01:00
Benjamin Bouvier
06d5fdb5ff fix(event cache): enable foreign keys on a connection basis
As opposed to WAL mode, foreign keys must be enabled for each database
connection, according to
https://www.sqlite.org/foreignkeys.html#fk_enable

Unfortunately, we can't track which connection objects have already
executed the pragma, so the safer we can do is enable it everytime we
try to acquire a connection from the pool.

Fixes #4785.
2025-03-11 15:03:42 +01:00
Benjamin Bouvier
961a893b8c test(event cache): double-check cascading happened in the clear linked chunk test 2025-03-11 15:03:42 +01:00
Ivan Enderlin
3d653d3fdc fix(sqlite): Design a new schema to get faster insertions.
This patch is twofold. First off, it provides a new schema allowing to
improve the performance of `SqliteEventCacheStore` for 100_000 events
from 6.7k events/sec to 284k events/sec on my machine.

Second, it now assumes that `EventCacheStore` does NOT store invalid
events. It was already the case, but the SQLite schema was not rejecting
invalid event in case some were handled. It's now explicitely forbidden.
2025-03-05 13:57:08 +01:00
Ivan Enderlin
b22bb3ee9f fix(sqlite): Use a prepared statement to insert events.
This patch uses a prepared statement to insert events in the linked
chunks. It offers more predictable performance, and SQLite prefers that.
2025-03-05 13:57:08 +01:00
Ivan Enderlin
892c99f0f3 test(sqlite): Improve a test to check uniqueness constraint. 2025-03-05 12:02:30 +01:00
Benjamin Bouvier
ac3250c58b refactor(event cache): use u64 instead of usize in MediaCachePolicy
This is more predictible and we're still far from 128-bits wide cpu,
right? RIGHT?
2025-03-04 18:10:59 +01:00