Commit Graph

288 Commits

Author SHA1 Message Date
multisme a66b2c5123 feat(test): add a test utils crate to make log initialization possible everywhere
This PR allows `init_tracing_for_test` to be called by any other crate in the sdk

Signed-off-by: multi [multiestunhappydev@gmail.com](mailto:multiestunhappydev@gmail.com)
2025-08-14 05:24:03 +00:00
Damir Jelić 139673810f Remember the public Curve25519 key of the sender of the historic room key bundle 2025-08-08 09:19:19 +02:00
Benjamin Bouvier 0cdec9d912 refactor(threads): flatten the ThreadStatus enum 2025-08-06 15:28:43 +02:00
Benjamin Bouvier d180d49c07 refactor(threads): do not store the unsubscribed state in the DB 2025-08-06 15:28:43 +02:00
Jakob Lachermeier 540a11e7a8 fix(sqlite): made open_with_pool public again. 2025-07-31 09:15:47 +02:00
Benjamin Bouvier f37bf2f5d1 feat(store): also delete thread subscriptions when deleting a room in db 2025-07-30 12:07:07 +02:00
Benjamin Bouvier 1a5cb2beb8 feat(stores): allow saving thread subscriptions 2025-07-30 12:07:07 +02:00
Kévin Commaille 0e622cc5a1 Upgrade Ruma (phase 3)
This upgrade introduces support for room version 12[1].

[1]: https://matrix.org/blog/2025/07/security-predisclosure/)

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-07-23 14:32:05 +00:00
Kévin Commaille 4931c0749e Upgrade Ruma again
This patch updates our `Raw` API usage since the newly added `JsonCastable` that disallows Raw casts that are known to fail deserialization. 

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-07-22 12:59:26 +00:00
Kévin Commaille 37626b5ad9 Bump Ruma
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-07-22 14:00:53 +02:00
Benjamin Bouvier b482ccd318 feat(sqlite): make sqlite's implementation of load_all_chunks_metadata even faster
See the updated code comment.
2025-07-21 17:41:06 +02:00
Ivan Enderlin 6f42210d6a feat(sqlite): Improve throughput of load_all_chunks_metadata by 1140%.
This patch changes the query used by
`SqliteEventCacheStore::load_all_chunks_metadata`. It was the cause of
severe slowness. The new query improves the throughput by +1140% and the
time by -91.916%. The benchmark will follow in the next patch.

Metrics for 10'000 events (with 1 gap every 80 events).

- Before:
  - throughput: 20.686 Kelem/s,
  - time: 483.43 ms.
- After:
  - throughput: 253.52 Kelem/s,
  - time: 39.478 ms.

This query will visit all chunks of a linked chunk with ID
`hashed_linked_chunk_id`. For each chunk, it collects its ID
(`ChunkIdentifier`), previous chunk, next chunk, and number of
events (`num_events`). If it's a gap, `num_events` is equal to 0,
otherwise it counts the number of events in `event_chunks` where
`event_chunks.chunk_id = linked_chunks.id`.

Why not using a `(LEFT) JOIN` + `COUNT`? Because for gaps, the entire
`event_chunks` will be traversed every time. It's extremely inefficient.
To speed that up, we could use an `INDEX` but it will consume more
storage space. Finally, traversing an `INDEX` boils down to traverse a
B-tree, which is O(log n), whilst this `CASE` approach is O(1). This
solution is nice trade-off and offers great performance.
2025-07-21 10:31:44 +02:00
Ivan Enderlin d73a02c608 feat(sqlite): Add more timer! logs in each EventCacheStore methods.
This patch adds `timer!` logs in each method from `EventCacheStore` for
`SqliteEventCacheStore`. It will help to know the execution duration of
each of these methods.
2025-07-14 10:34:17 +02:00
Ivan Enderlin f73199b472 feat(sqlite): Instrument SqliteEventCacheStore::open_with_config. 2025-07-14 10:34:17 +02:00
Ivan Enderlin 420d373144 feat(sqlite): Add #[instrument] around all SqliteEventCacheStore methods. 2025-07-14 10:34:17 +02:00
Ivan Enderlin a79e9130e6 feat(sqlite): Add timer! tracings in read and write's SqliteEventCacheStore. 2025-07-14 10:34:17 +02:00
Ivan Enderlin 7b73311de5 feat(sqlite): Add logs around read and write. 2025-07-14 10:34:17 +02:00
Ivan Enderlin f03934bc4f feat(sqlite): SqliteEventCacheStore has 1 write connection.
Until now, `SqliteEventCacheStore` manages a pool of connections. A
connection is fetched from this pool and operations are executed on it,
regardless whether these are read operations or write operations.

We are seeing more and more _database is busy_ errors. We believe this
is because too many write operations are executed concurrently.

The solution to solve this is to use multiple connections for read
operations, and a single connection for write operations. That way,
concurrent writings are no longer a thing, and we hope it will reduce
the number of _database is busy_ errors to zero. That's our guess.

This patch does that. When the pool of connections is created, a
connection is elected as the `write_connection`. To get a connection for
read operations, one has to use the new `SqliteEventCacheStore::read`
method (it replaces the `acquire` method). To get a connection for
write operations, one has to use the new `SQliteEventCacheStore::write`
method. It returns a `OwnedMutexGuard` from an async `Mutex`. All
callers that want to do write operations on this store have to wait
their turn, this `Mutex` is fair, and the first to wait on the lock is
the first that will take the lock (FIFO). It guarantees the execution
ordering the code expects.

The rest of the patch updates all spots where `acquire` was used and
replaces them by `read()` or `write()`. A particular care was made to
see if other places are using `SqliteEventCacheStore::pool` directly. No
place remains except in `read()` and `write()`.
2025-07-14 10:34:17 +02:00
Ivan Enderlin 014ee98fb7 feat(sqlite): SqliteStoreConfig::pool_size sets a minimum to 2.
This patch updates `SqliteStoreConfig::pool_size` to be at least 2. We
need 2 connections: one for write operations, one for read operations.
This behaviour is coming in the next patches.
2025-07-14 10:34:17 +02:00
Damir Jelić b9ce4059fb refactor(sqlite): Move the transaction in find_event_relations into a function 2025-07-10 14:23:10 +02:00
Damir Jelić d0c01006e4 fix(sqlite): Fix a SQL injection issue in the find_event_relations function
The SQLite implementation for the
EventCache::find_event_with_relations() the relation type list isn't
inserted using SQL placeholders.

The relation types are inserted manually using a format!() call. The
usage of the format!() call can lead to SQL injection if a
RelationType::Custom variant is used which contains SQL expressions.

This patch modifies the, query logic which retrieves the related events,
to use two separate queries which use SQL placeholders to insert all
the dynamic variables.

Security-Impact: Moderate
CVE: CVE-2025-53549
GitHub-Advisory: GHSA-275g-g844-73jh
2025-07-10 13:55:02 +02:00
Damir Jelić dc98bf7633 test: Add a test for GHSA-275g-g844-73jh 2025-07-10 13:55:02 +02:00
Benjamin Bouvier c92a89d571 chore(sqlite): reorder methods and add doc comment for encode_key
This reorders methods so that they're grouped in "dual" pairs
(encode/decode, serialize/deserialize). Also adds a doc comment to
`encode_key`, as I've wondered in the past what it did.
2025-07-09 12:37:34 +02:00
Benjamin Bouvier 684f228e70 refactor(sqlite): share implementations of the encode/decode/serialize/deserialize sqlite store helpers 2025-07-09 12:37:34 +02:00
Jonas Platte 07808b4301 refactor: Enable extra code style clippy lints 2025-07-05 21:20:34 +02:00
Benjamin Bouvier 39d1ed9bc6 chore: exclude the room id / event id from the data to be sent to sentry
These are not included in Element's main privacy policy, and may
constitute PII (because the homeserver may include the name of some
user). We keep the information as separate log lines, so that
rageshakes/manual reports still include those.
2025-07-03 16:07:46 +02:00
Benjamin Bouvier 3d1d1c8f6d feat(state store): send deserialization errors to sentry \o/ 2025-07-03 16:07:46 +02:00
Benjamin Bouvier 5ad958722f feat(state store): include serde json error path when failing to deserialize in the state store 2025-07-03 16:07:46 +02:00
Benjamin Bouvier 31df84f5a1 feat(event cache): return the event's positions in find_event_relations 2025-06-30 16:10:49 +02:00
Benjamin Bouvier 1f89efb88d feat(event cache store): add a method to return the chunks' metadata 2025-06-30 11:09:11 +02:00
Kévin Commaille 06732ca71a refactor(common): Use a constant for the room version to use as a fallback
It avoids using different versions in several places for consistency. It
also allows to be able to change it in a single place when needed.

This also bumps the fallback to v11 everywhere, since it is the default
version for new rooms since Matrix 1.14 and it has the sanest redaction
rules.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-06-29 16:59:37 +02:00
Doug c74295c604 chore: Refactor ServerCapabilities into ServerInfo.
It has nothing to do with /capabilities so is confusing. We can use this new struct to combine the well-known response into a single cache too.
2025-06-19 17:40:04 +02:00
Stefan Ceriu f4e612ca9e feat: add thread support to the room message draft facilities
This patch adds optional thread root event id parameters to the drafting functions exposed on the room level
allowing unfinished messages to be managed for the main room as well as any inner thread.

Internally it uses the room id or a tuple of the room id and the thread as keys for the various backing stores.
2025-06-13 14:41:10 +03:00
Damir Jelić 0e9ce0271e refactor: Create a store/types submodule 2025-06-10 13:53:09 +02:00
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
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
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 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
Damir Jelić 511cf78d51 chore: Bump the rusqlite version 2025-04-09 16:37:40 +02:00