Fully switch to async KVStore persistence#919
Conversation
|
👋 Thanks for assigning @joostjager as a reviewer! |
42c16e2 to
4371f6d
Compare
05ebc28 to
911c169
Compare
39fbf19 to
bad8d52
Compare
Use a temporary rust-lightning fork revision that exposes async migratable KV-store support. This lets ldk-node migrate filesystem stores without reimplementing LDK's migration logic locally. Co-Authored-By: HAL 9000
In lightningdevkit/vss-server#101 we dropped the Java version of `vss-server` and moved the Rust version to the repo root. Here we account for the updated location in our integration tests.
Read and write BDK wallet state through async KVStore helpers while keeping the current WalletPersister entry points bridged through the node runtime. This reduces the wallet persistence surface that still depends on KVStoreSync. Co-Authored-By: HAL 9000
Static invoice persistence already runs from async handlers, so use KVStore directly instead of routing those reads and writes through the blocking KVStoreSync trait. Co-Authored-By: HAL 9000
1b4d24f to
c62a503
Compare
joostjager
left a comment
There was a problem hiding this comment.
Overall happy with this simplification. Big changeset though, even though not all that much is happening. Risk nonetheless.
Bigger open question for me is what the latest is on runtime abstraction and how that is going to work in combination with potential wasm support.
Also, are there sync public api calls that can now be made async such as export_pathfinding_scores?
| }, | ||
| ) | ||
| .await | ||
| { |
There was a problem hiding this comment.
This looks like an unnecessary style change?
There was a problem hiding this comment.
Hmm, right. I have to admit I like the changed version better (which is why I left it in), but now added a fixup to keep it minimally invasive.
| locked_peers.insert(peer_info.node_id, peer_info); | ||
| PeerStoreSerWrapper(&locked_peers).encode() | ||
| }; | ||
| self.persist_peers(data).await |
There was a problem hiding this comment.
Is it safe to have already dropped the lock? If two add_peer calls run interleaved, outdated state may be written?
There was a problem hiding this comment.
Fair, would you prefer we take a mutation guard as we do in DataStore in instances like this?
| !pending_channel.is_outbound | ||
| && self.peer_store.get_peer(&counterparty_node_id).is_none() | ||
| }) | ||
| .and_then(|_| { |
There was a problem hiding this comment.
Is this full style rewrite necessary?
There was a problem hiding this comment.
Well, a refactor was somewhat necessary anyways as we needed to move out the add_peer call, and it's way cleaner to do it this way as otherwise we'd need to add at least 2 more else clauses with None anyways.
| L::Target: LdkLogger, | ||
| { | ||
| objects: Mutex<HashMap<SO::Id, SO>>, | ||
| mutation_lock: tokio::sync::Mutex<()>, |
There was a problem hiding this comment.
Can you help me remember why we have a Runtime abstraction, but also reference tokio directly?
There was a problem hiding this comment.
That's somewhat orthogonal, right now Runtime is an abstraction for the Runtime that ensures we do the right thing (detect outer runtimes, always wrap block_on in block_in_place etc). This is simply using the mutex type.
| let _guard = self.mutation_lock.lock().await; | ||
|
|
||
| self.persist(&object)?; | ||
| self.persist(&object).await?; |
There was a problem hiding this comment.
Now that this no longer holds self.objects, doesn't that, isn't this creating a window where readers get interleaved in an undesirable way?
There was a problem hiding this comment.
That's why we introduced the mutation_lock for the write paths. Or maybe I'm misunderstanding the question?
There was a problem hiding this comment.
No await here? Can that go undetected? Perhaps elsewhere too.
| signer_provider: &'a test_utils::TestKeysInterface, | ||
| } | ||
|
|
||
| impl<K: KVStore + Sync> TestMonitorUpdatePersister<'_, K> { |
There was a problem hiding this comment.
Commit message says dropping the trait, but this looks more substantial?
| pub(crate) fn create_persister<'a, K: KVStoreSync + Sync>( | ||
| store: &'a K, chanmon_cfg: &'a TestChanMonCfg, max_pending_updates: u64, | ||
| pub(crate) fn create_persister<'a, K: KVStore + Sync>( | ||
| store: &'a K, chanmon_cfg: &'a TestChanMonCfg, _max_pending_updates: u64, |
There was a problem hiding this comment.
Wasn't max pending updates exercised in a test?
| ); | ||
|
|
||
| let runtime_handle = internal_runtime.handle(); | ||
| let schema_version = tokio::task::block_in_place(|| { |
There was a problem hiding this comment.
Isn't it a problem that setup can't fail fast here anymore?
There was a problem hiding this comment.
We fail fast elsewhere via setup_schema_version.
| )?; | ||
|
|
||
| let store_key = self.build_obfuscated_key(&primary_namespace, &secondary_namespace, &key); | ||
| let schema_version = match self.schema_version().await { |
There was a problem hiding this comment.
This repetitive code doesn't look great. Maybe an explicit setup call is better?
Persist peer store updates through async KVStore operations. The synchronous node APIs keep bridging at their runtime boundary while async event handling awaits peer persistence directly. Co-Authored-By: HAL 9000
Persist DataStore mutations through async KVStore operations while keeping the existing synchronous APIs bridged through the node runtime. Async event handling now awaits payment store writes directly. Co-Authored-By: HAL 9000
Persist node metric updates through async KVStore writes and await them from the chain, gossip, and scoring tasks. This removes the remaining blocking metrics writer while keeping the helper name stable. Co-Authored-By: HAL 9000
c62a503 to
290b0a5
Compare
Avoid introducing a temporary macro when moving node metrics persistence onto async KV storage. Co-Authored-By: HAL 9000
Persist the on-chain wallet through BDK's AsyncWalletPersister so wallet state writes use the async KVStore path. Existing synchronous wallet APIs keep bridging through the node runtime until their callers are made async. Co-Authored-By: HAL 9000
Open filesystem stores through the async LDK migration helper so v1-to-v2 store migration no longer depends on the blocking KVStoreSync migration path. Co-Authored-By: HAL 9000
Move the existing in-memory test store into a shared module without changing its behavior. This lets integration tests reuse it while keeping the later async TestSyncStore change separate. Co-Authored-By: HAL 9000
Keep the shared test store move as a pure code move by restoring the original comments and spacing. Co-Authored-By: HAL 9000
Exercise async KVStore operations in TestSyncStore and filesystem migration tests while keeping the temporary sync comparison path until the final KVStoreSync removal. Also route pathfinding score export through async KVStore reads. Co-Authored-By: HAL 9000
Drop the remaining synchronous KV store trait bounds and implementations. After the preceding migrations, custom stores only need to provide async KVStore persistence. Co-Authored-By: HAL 9000
Compile the VSS persistence tests after the shared KV store helper moved to async persistence. Co-Authored-By: HAL 9000
Resolve the VSS schema through async setup on the caller runtime so VSS stores no longer create or retain a Tokio runtime. NodeBuilder preserves fail-fast setup while the VSS builder stays synchronous. Co-Authored-By: HAL 9000
Construct PostgreSQL storage on the caller runtime now that the store only exposes async KVStore operations. This removes the store-owned runtime and its shutdown path. Co-Authored-By: HAL 9000
290b0a5 to
9ad8f2d
Compare
| self.persist_peers(&*locked_peers) | ||
| pub(crate) async fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> { | ||
| let data = { | ||
| let mut locked_peers = self.peers.write().expect("lock"); |
There was a problem hiding this comment.
should we use an async lock for peers now?
| let data = locked_node_metrics.encode(); | ||
| KVStoreSync::write( | ||
| let data = { | ||
| let mut locked_node_metrics = node_metrics.write().expect("lock"); |
There was a problem hiding this comment.
should this be async lock?
|
codex seems to agree on the locks
|
We planned to make the full switch to the async
KVStorefor a while. Here we do just that: first step-by-step moving the remaining dependencies over to useKVStore, then finally droppingKVStoreSyncand all implementations.Depends on lightningdevkit/rust-lightning#4658 (will drop the
DROPMEcommit once that's merged).