Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement a generation counter for the CryptoStore lock #2155

Merged
merged 3 commits into from
Jun 30, 2023

Conversation

bnjbvr
Copy link
Member

@bnjbvr bnjbvr commented Jun 26, 2023

This implements a generation counter for the CryptoStore lock, aiming at solving the following problem. Consider ElementX iOS:

  • the main app goes in the background, releasing the crypto store lock
  • a push notification shows up, NSE process is created, acquires the crypto store lock, does its thing, then releases lock
  • the main app goes back in the foreground, acquires the crypto store lock

Then it's possible that the crypto store caches in the main app are now stale, but the main app won't realize, because trying to acquire the lock may succeed on the first attempt.

The solution is to introduce a "generation" counter, maintained in both a process embedding the SDK, and the store. It is updated any time we acquire a lock and we observed a different value in the store.

Now observe what happens with the above scenario, annotated with generations:

  • the main app goes in the background, releasing the crypto store lock; generation was e.g. 1
  • a push notification shows up, NSE process is created, acquires the crypto store lock, sees that generation was 1, increments it to 2 in the store, does its thing, then releases lock
  • the main app goes back in the foreground, sees that generation in store is 2 while the one it knew about was 1; sets generation to 3 and reloads its internal caches because of the mismatch.

(When there's no mismatch, there's nothing to do, of course.)

Because of this, we don't need explicit cache reloading when the app goes back into the foreground, so it's slightly easier to use for ElementX embedders. And, it's more precise too as it won't do spurious cache reloads (in a scenario like main app goes to background / main app goes to foreground, with no NSE process involved).

@bnjbvr bnjbvr marked this pull request as ready for review June 29, 2023 14:09
@bnjbvr bnjbvr requested a review from a team as a code owner June 29, 2023 14:09
@bnjbvr bnjbvr requested review from Hywan and removed request for a team June 29, 2023 14:09
@codecov
Copy link

codecov bot commented Jun 29, 2023

Codecov Report

Patch coverage: 94.11% and project coverage change: +0.13 🎉

Comparison is base (d72fd34) 76.59% compared to head (409b757) 76.73%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2155      +/-   ##
==========================================
+ Coverage   76.59%   76.73%   +0.13%     
==========================================
  Files         164      164              
  Lines       17594    17637      +43     
==========================================
+ Hits        13477    13533      +56     
+ Misses       4117     4104      -13     
Impacted Files Coverage Δ
crates/matrix-sdk-crypto/src/store/locks.rs 98.30% <ø> (ø)
crates/matrix-sdk-ui/src/encryption_sync/mod.rs 66.03% <ø> (+1.22%) ⬆️
crates/matrix-sdk-crypto/src/machine.rs 81.05% <91.17%> (+2.28%) ⬆️
crates/matrix-sdk/src/encryption/mod.rs 47.72% <100.00%> (+6.16%) ⬆️

... and 3 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Contributor

@poljar poljar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. A real-world test run would be nice before we merge though.

/// DB over time. Observing a different value than the one read in
/// memory, when reading from the store indicates that somebody else has
/// written into the database under our feet.
pub(crate) crypto_store_generation: Arc<Mutex<Option<u64>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to use a SequenceNumber for this?

/// A numeric type that can represent an infinite ordered sequence.
///
/// It uses wrapping arithmetic to make sure we never run out of numbers. (2**64
/// should be enough for anyone, but it's easy enough just to make it wrap.)
//
/// Internally it uses a *signed* counter so that we can compare values via a
/// subtraction. For example, suppose we've just overflowed from i64::MAX to
/// i64::MIN. (i64::MAX.wrapping_sub(i64::MIN)) is -1, which tells us that
/// i64::MAX comes before i64::MIN in the sequence.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct SequenceNumber(i64);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could use it, and it does impl PartialEq/Eq, but I'm really just using a single wrapping_add here 😁

crates/matrix-sdk-crypto/src/machine.rs Show resolved Hide resolved
@bnjbvr bnjbvr enabled auto-merge (rebase) June 30, 2023 10:03
@bnjbvr bnjbvr disabled auto-merge June 30, 2023 10:05
@bnjbvr bnjbvr enabled auto-merge (rebase) June 30, 2023 10:23
@bnjbvr bnjbvr merged commit 4fbb3d2 into matrix-org:main Jun 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants