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

Introduce a store transaction for Account #2660

Merged
merged 15 commits into from
Oct 9, 2023
Merged

Conversation

bnjbvr
Copy link
Member

@bnjbvr bnjbvr commented Oct 3, 2023

This adds a new StoreTransaction type, that wraps a StoreCache and a Store. The idea is that it will allow write access to the Store (and maintains the cache at the same time), while the Store::cache method will only give read-only access to the store's content.

Another new data type is introduced, PendingChanges, that reflects Changes but for fields that are properly maintained in the StoreCache and that one can write in the StoreTransaction. In the future, it wouldn't be possible to use save_pending_changes from the outside of a StoreTransaction context.

The layering is the following:

  • Store wraps the DynCryptoStore, contains a reference to a StoreCache.
  • When read-only access is sufficient, one can get a handle to the cache with Store::cache().
  • When a write happens, then one can create a StoreTransaction (later, only one at a time will be allowed, by putting the StoreCache behind a RwLock; this has been deferred to not make the PR grow too much).
  • Any field in the StoreCache will get a method to get a reference to the cached thing: it will either load from the DB if not cached, or return the previously cached value.
  • Any field that can be written to will get a method to get a mutable reference in the StoreTransaction: it will either load from the cache into a PendingChanges scratch pad, or return the scratchpad temporary value.
  • When a StoreTransaction::commit() happens, fields are backpropagated into the DB and the cache.

Then, this StoreTransaction is used to update a ReadOnlyAccount in multiple places (and usage of ReadOnlyAccount is minimized so as not to require a transaction or cache function call as much as possible). With this, the read-only account only exists transiently, and it's only stored long-term in the cache.

Followup PRs include:

  • making the ReadOnlyAccount not cloneable
  • remove inner mutability from the ReadOnlyAccount
  • add a RwLock on the StoreTransaction

Part of #2624 + #2000.

@bnjbvr bnjbvr requested a review from poljar October 3, 2023 17:31
@bnjbvr bnjbvr requested a review from a team as a code owner October 3, 2023 17:31
@codecov
Copy link

codecov bot commented Oct 3, 2023

Codecov Report

Attention: 7 lines in your changes are missing coverage. Please review.

Comparison is base (41de52b) 78.21% compared to head (79c4dfa) 78.25%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2660      +/-   ##
==========================================
+ Coverage   78.21%   78.25%   +0.03%     
==========================================
  Files         191      191              
  Lines       19902    19933      +31     
==========================================
+ Hits        15567    15598      +31     
  Misses       4335     4335              
Files Coverage Δ
crates/matrix-sdk-crypto/src/dehydrated_devices.rs 100.00% <100.00%> (ø)
crates/matrix-sdk-crypto/src/error.rs 0.00% <ø> (ø)
...atrix-sdk-crypto/src/file_encryption/key_export.rs 96.49% <ø> (ø)
crates/matrix-sdk-crypto/src/gossiping/machine.rs 65.26% <ø> (ø)
crates/matrix-sdk-crypto/src/identities/device.rs 75.48% <100.00%> (ø)
crates/matrix-sdk-crypto/src/identities/manager.rs 81.94% <100.00%> (ø)
crates/matrix-sdk-crypto/src/identities/user.rs 86.54% <ø> (ø)
...s/matrix-sdk-crypto/src/store/integration_tests.rs 100.00% <100.00%> (ø)
crates/matrix-sdk-crypto/src/store/memorystore.rs 80.50% <100.00%> (+0.12%) ⬆️
crates/matrix-sdk-crypto/src/store/traits.rs 74.64% <100.00%> (+0.73%) ⬆️
... and 6 more

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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.

Not completely convinced by the new interface, left some comments we should discuss.

@@ -138,6 +138,11 @@ impl CryptoStore for MemoryStore {
Ok(self.next_batch_token.read().await.clone())
}

async fn save_pending_changes(&self, _changes: PendingChanges) -> Result<()> {
// TODO(bnjbvr) why didn't save_changes save the account?
Copy link
Contributor

Choose a reason for hiding this comment

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

The account is, or perhaps was, only loaded when you create the OlmMachine. Once that's done the Account lives and dies with the OlmMachine.

This made it unnecessary for the MemoryStore to persist the account.

Copy link
Member Author

Choose a reason for hiding this comment

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

Interesting. With this new design, the ReadOnlyAccount would only live transiently:

  • either read from the database, available from the cache()
  • or we plan to mutably use it, which implies the use of a transaction()

In that case, we would need to save it for the memory store.

(Note that, at the end of the day, the memory store could be exactly the same thing as the in-memory cache 🤪 and since a cross-process lock for the memory store doesn't make sense, all the methods in the memory stub could do strictly nothing, since the cache would keep the values in memory 🧠)

crates/matrix-sdk-crypto/src/store/mod.rs Show resolved Hide resolved
// Note: bnjbvr lost against borrowck here. Ideally, the `F` parameter would take a
// `&StoreTransaction`, but callers didn't quite like that.
#[cfg(test)]
pub(crate) async fn with_transaction<
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, that's more what I had in mind, why is this for tests only? Can't we mimic the sqlx interface here? They seem to have figured out how to make the borrow checker happy: https://docs.rs/sqlx/latest/sqlx/prelude/trait.Connection.html#method.transaction.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can definitely use it outside tests too; I found it nice to use in tests because it creates a small scope, but in general code where the transaction may live longer, it was good enough to have an explicit commit. Both are fine to me, I don't have a strong opinion here.

@bnjbvr bnjbvr requested a review from poljar October 6, 2023 15:05
@bnjbvr bnjbvr changed the title Introduce a store transaction for ReadOnlyAccount Introduce a store transaction for Account Oct 9, 2023
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.

Alright, I don't have any more objections here. Thanks for the patience and discussions around this.

self.store.save_changes(changes).await?;
}
SessionType::Existing(s) => {
SessionType::New(s) | SessionType::Existing(s) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, a little bit uneasy about this one. We're now persisting the Session but not the Account. This means that the Account will contain the one-time key to create the session for a bit longer than needed.

I guess there's nothing we can do here until we move the Session to use the transaction as well.

@bnjbvr bnjbvr enabled auto-merge (squash) October 9, 2023 14:05
@bnjbvr bnjbvr merged commit c64d3b4 into main Oct 9, 2023
36 checks passed
@bnjbvr bnjbvr deleted the transactional-account branch October 9, 2023 14:16
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