Skip to content

Support tiered data storage#692

Open
enigbe wants to merge 2 commits intolightningdevkit:mainfrom
enigbe:2025-10-tiered-data-storage
Open

Support tiered data storage#692
enigbe wants to merge 2 commits intolightningdevkit:mainfrom
enigbe:2025-10-tiered-data-storage

Conversation

@enigbe
Copy link
Copy Markdown
Contributor

@enigbe enigbe commented Nov 1, 2025

What this PR does

In this PR we introduce TierStore, a three-tiered (KVStore + KVStoreSync) implementation that manages data across three distinct storage layers based on criticality.

Background

As we have moved towards supporting remote storage with VssStore, we need to recognize that not all data has the same storage requirements. Currently, all data goes to a single store which creates some problems:

  • Remote storage adds latency on every read/write operation
  • Network graph and scorer data is ephemeral and easily rebuild-able from the network
  • Persisting ephemeral data to remote storage wastes bandwidth and adds unnecessary latency
  • Users persisting to remote storage have no local disaster recovery option

This PR proposes tiered storage that provides granular control over where different data types are stored. The tiers include:

  • Ephemeral store (network graph, scorer): optional fast local storage to avoid remote latency
  • Primary store (channels, payments, wallet): preferably remote storage for critical data
  • Backup store: optional (preferably local) backup for disaster recovery, with data written to (removed from) lazily to avoid blocking primary data operations

Additionally, we also permit the configuration of Node with tiered storage allowing callers to:

  • set retry parameters,
  • set backup and ephemeral stores, and to
  • build the Node with a primary store.
    These configuration options also extend to our foreign interface, allowing bindings target to build the Node with their own (KVStore + KVStoreSync) implementations. A sample Python implementation is provided and tested.

Concerns

  1. Nested Retry Logic: VssStore has built-in retry logic. Wrapping it in TierStore creates nested retries.
  2. Backup Queue Capacity: Currently hard-coded (100 in prod, 5 in tests). Ideally this should be configurable but there are concerns about exposing this to callers? What is a sensible default?
  3. Data Consistency Between Primary and Backup: Backup operates asynchronously and may lag behind or miss operations if the queue overflows. Should we consider adding a background sync task to reconcile backup with primary? Expose metrics for backup lag/queue depth? Implement catch-up logic on startup?
  4. Exposing KVStore to the FFI

Related Issues

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Nov 1, 2025

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@ldk-reviews-bot ldk-reviews-bot requested a review from tnull November 1, 2025 23:18
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch 4 times, most recently from 29f47f3 to 264aa7f Compare November 4, 2025 22:07
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 3rd Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 4th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 5th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 6th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 7th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 8th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 9th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 10th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 11th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 12th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 13th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch from 264aa7f to 493dd9a Compare December 2, 2025 19:50
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 14th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch 2 times, most recently from a30cbfb to 1e7bdbc Compare December 4, 2025 23:30
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 15th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 16th Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Copy Markdown
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Thanks for looking into this and excuse the delay here!

I did a first pass, generally this looks already pretty good, but it is a huge PR and in some areas could be simplified. For instance, we should drop the generic Retry logic as the concrete implementations would already implement that if they need it. Likewise, we shouldn't fallback to the backup for now as it's really only meant as disaster recovery (KVStore caching is out-of-scope for this PR now, even though we might want to explore that soon, too). There is also no need to replicate the write-ordering locks in TierStore, but we'll need them in ForeignKVStoreAdapter (which might be mergeable with DynStore?).

Generally, if you find opportunities to reduce the size of the changeset here it would be appreciated. It would also be cool if you could try to break up the PR into more feature commits, but feel free to leave as is if not.


/// Configuration for exponential backoff retry behavior.
#[derive(Debug, Copy, Clone)]
pub struct RetryConfig {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Retrying is pretty specific to the particular KVStore implementation. I don't think we should expose retrying params on top of what we already do for implementations internally.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure thing. This has been dropped.
I considered it because users can choose their own KVStore implementation without considering retrying. I thought to add some level of control but the added complexity and size of the changeset might not be worth it.

}

pub struct TierStoreInner {
/// For remote data.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Might also be local.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated and addressed here db1fe83

[Throws=BuildError]
Node build_with_vss_store_and_header_provider(NodeEntropy node_entropy, string vss_url, string store_id, VssHeaderProvider header_provider);
[Throws=BuildError]
Node build_with_tier_store(NodeEntropy node_entropy, DynStore primary_store);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we now also expose Builder::build_with_store?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes we can.
This is exposed here 451a392

src/ffi/types.rs Outdated
Self { inner: Arc::new(adapter) }
}

pub fn from_ldk_store(store: Arc<dyn LdkSyncAndAsyncKVStore + Send + Sync>) -> Arc<Self> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we rather make this an impl From<Arc<dyn LdkSyncAndAsyncKVStore + Send + Sync>> for Arc .., we can drop the wrap_storemacro and just use.into()` instead.

src/builder.rs Outdated
}

let store = wrap_store!(Arc::new(tier_store));
self.build_with_store(node_entropy, store)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we need to use build_with_store_internal here to make sure we're using the same Runtime etc.

Arc::clone(&outer_lock.entry(locking_key).or_default())
}

async fn execute_locked_write<
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm confused, why are we replicating all the write-ordering logic here? Given the implementations are required to fullfill LDK's requirements already, can't we just call the inner write and be done with it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Again, the reasoning here is that users might not implement the KVStore trait for their own stores as required and thus, the onus of correctness will fall to us. If we can't get it from their inner stores, the wrapper TierStore should provide some guarantees. I do agree that if the inner store already satisfies LDK's requirements, the wrapper shouldn't need to duplicate that logic. The write-ordering has been removed.

src/ffi/types.rs Outdated
) -> Pin<Box<dyn Future<Output = Result<(), lightning::io::Error>> + Send>> {
let inner = self.inner.clone();

let primary_namespace = primary_namespace.to_string();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think here (and in remove) we need to add the write-ordering logic to ensure we follow LDK's KVStore::write requirements.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This has now been added 1ba4831

src/types.rs Outdated
/// A type alias for [`SyncAndAsyncKVStore`] with `Sync`/`Send` markers;
pub type DynStore = dyn SyncAndAsyncKVStore + Sync + Send;
#[cfg(feature = "uniffi")]
pub(crate) use crate::DynStore;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is odd. Why do we need this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I had initially used the same name and the feature gating became necessary to differentiate the types at call sites when uniffi was enabled.

}
}

pub struct DelayedStore {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not quite sure what coverage we gain with DelayedStore? IMO, we might be better off dropping all this boilerplate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This has been removed here cb66b59.
I couldn't think of any way better to test backup queue overflow not impacting primary writes. I agree that the boilerplate seems too much for just that single case.

src/ffi/types.rs Outdated
) -> Result<Vec<String>, IOError>;
}

pub struct ForeignKVStoreAdapter {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, any reason this needs to be separate from DynStore? Couldn't we merge them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

At the time I believe I introduced the extra wrapper due to the limitation of foreign trait implementation on foreign types, i.e. I couldn't implement KVStore for Arc<dyn LdkSyncAndAsyncKVStore> but with the new changes, this is no longer relevant and has been removed.

src/ffi/types.rs Outdated
}
}

#[async_trait]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably not worth taking a dependency just to desugar impl Future<Output = Result<(), IOError>> + Send + Sync + 'a.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Probably not worth taking a dependency just to desugar impl Future<Output = Result<(), IOError>> + Send + Sync + 'a.

Well, it's 'the official'/supported way to do async traits with Uniffi: https://mozilla.github.io/uniffi-rs/latest/futures.html#exporting-async-trait-methods

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does uniffi require that in some way? Its just a comically-overkill way to sugar impl Future which is kinda nuts...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, worse, it looks like async_trait desugars to the Pin<Box<dyn ...>> version...which is dumb but at least for uniffi it shouldn't matter cause we'd have to do that anyway.

Copy link
Copy Markdown
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Unfortunately this will need a substantial rebase now that #696 landed, sorry for that!

Besides some tedious rebase work, we now have DynStoreWrapper which can be used on the ffi and the non-ffi side. I do wonder if we can actually just merge that wrapper with the TierStore, as well as the ForeignKVStoreAdapter/DynStore. Seems all these wrapper structs may not be needed and we could just get away with one trait and one wrapper struct, mostly?

@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch 2 times, most recently from cba29a3 to db1fe83 Compare February 24, 2026 23:03
@enigbe
Copy link
Copy Markdown
Contributor Author

enigbe commented Feb 25, 2026

Thanks @tnull and @TheBlueMatt for the initial round of reviews.

I've completed the rebase and integrated changes from #696. Given the scope of the restructuring, I recommend reviewing the new commit series afresh rather than diffing against the previous revision, as line numbers have shifted substantially.

Summary of major changes:

  1. Split the large tier-storage commit into four focused commits focused on implementation, builder integration, FFI exposure, and testing
  2. Removed all retry logic
  3. Added per-key version-locked writes to foreign store wrapper
  4. Dropped test helpers/boilerplate related to DelayedStore

For each prior review comment, I've replied inline noting whether it was addressed (with the relevant commit) or removed in the restructured series.

@enigbe enigbe requested review from TheBlueMatt and tnull February 25, 2026 00:24
Copy link
Copy Markdown
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Thanks for the rebase! Feel free to squash the fixups.

Here a a few initial comments, but this is still a huge PR. To make it a bit more manageable, I do wonder if we can split out the FFI-related changes to a follow-up and here only add the tiered store itself? We probably would make sure both land for the next release, but it might be a bit easier to review these steps separately.

src/ffi/types.rs Outdated
};

#[derive(Debug)]
pub enum IOError {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a lot of code added here, let's move it to a new ffi/io.rs module.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure.

fn read(
&self, primary_namespace: &str, secondary_namespace: &str, key: &str,
) -> io::Result<Vec<u8>> {
self.runtime.block_on(self.inner.read_internal(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this should call through to the KVStoreSync implementation of inner (which in some cases uses a different runtime), not use the same runtime.

@enigbe
Copy link
Copy Markdown
Contributor Author

enigbe commented Feb 25, 2026

Here a a few initial comments, but this is still a huge PR. To make it a bit more manageable, I do wonder if we can split out the FFI-related changes to a follow-up and here only add the tiered store itself? We probably would make sure both land for the next release, but it might be a bit easier to review these steps separately.

I will split out the FFI-related changes into a separate PR.

Copy link
Copy Markdown
Contributor

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Just looked at tier_store.rs, I'll let @tnull handle the rest.

///
/// The backup operates on a best-effort basis:
/// - Writes are queued asynchronously (non-blocking)
/// - No retry logic (We assume local store is unlikely to have transient failures).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, this is maybe something we should consider dropping for now.

If we're imagining mostly a world where primary persistence is remote but local persistence is backup, there shouldn't be any real penalty to just requiring both writes to complete, and it makes the guarantees we can provide way better.

That said, some may want to use it in the reverse - a primary local store and a remote secondary store. In that case, we indeed don't want to block primary persistence, but we also probably can't really tolerate having no ability to recover from secondary failures.

Luckily, this would also simplify the code a decent bit here and we can come back and revisit this with a more cohesive answer for the second case. Just make sure we do things in parallel with spawns instead of waiting on both writes individually.

fn is_ephemeral_cached_key(pn: &str, sn: &str, key: &str) -> bool {
matches!(
(pn, sn, key),
(NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, _, NETWORK_GRAPH_PERSISTENCE_KEY)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's also _SECONDARY_NAMESPACEs for these constants, though they're all empty anyway.

matches!(
(pn, sn, key),
(NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, _, NETWORK_GRAPH_PERSISTENCE_KEY)
| (SCORER_PERSISTENCE_PRIMARY_NAMESPACE, _, SCORER_PERSISTENCE_KEY)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, if we're using a CombinedScorer the scorer is actually very small and only local data. In a multi-device setup, it might well make sense to sync this. I don't feel strongly about this, though, just raising it as an option. @tnull what do you think?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I guess it's mostly a matter of latency, as every time the scorer is changed event handling would be delayed by the remote store RTT currently, AFAIU. The question is whether we expect that people who switch devices frequently really would benefit from recent scoring data. Does that happen very often? Not sure?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yea, probably not. In theory the background processor can run scorer persistence in parallel with other tasks, though it largely doesn't today (but maybe will soon, with Joost's work).

@tnull tnull mentioned this pull request Mar 2, 2026
@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch from db1fe83 to 35f9ec2 Compare March 9, 2026 15:47
@tnull
Copy link
Copy Markdown
Collaborator

tnull commented Mar 23, 2026

@enigbe Do you still intend to move forward here? LDK Node v0.8 beta might only be a few weeks away, would be great to get this landed in time!

@enigbe
Copy link
Copy Markdown
Contributor Author

enigbe commented Mar 30, 2026

Apologies for the delay here. I'll be addressing it today.

@tnull
Copy link
Copy Markdown
Collaborator

tnull commented Mar 30, 2026

Apologies for the delay here. I'll be addressing it today.

Please note that by now we'll want to dedup/DRY up the code here with DynStoreTrait and/or DynStoreRef as far as possible rather than adding even more wrappers/adapters on top.

@enigbe
Copy link
Copy Markdown
Contributor Author

enigbe commented Mar 30, 2026

Please note that by now we'll want to dedup/DRY up the code here with DynStoreTrait and/or DynStoreRef as far as possible rather than adding even more wrappers/adapters on top.

I am currently reviewing #782 to ensure this.

@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch 8 times, most recently from 1cd58c0 to 342f8e6 Compare April 6, 2026 19:31
enigbe added 2 commits April 7, 2026 22:35
This commit:

Adds `TierStore`, a tiered `KVStore`/`KVStoreSync` implementation that
routes node persistence across three storage roles:

- a primary store for durable, authoritative data
- an optional backup store for a second durable copy of primary-backed data
- an optional ephemeral store for rebuildable cached data such as the
  network graph and scorer

TierStore routes ephemeral cache data to the ephemeral store when
configured, while durable data remains primary+backup. Reads and lists
do not consult the backup store during normal operation.

For primary+backup writes and removals, this implementation treats the
backup store as part of the persistence success path rather than as a
best-effort background mirror. Earlier designs used asynchronous backup
queueing to avoid blocking the primary path, but that weakens the
durability contract by allowing primary success to be reported before
backup persistence has completed. TierStore now issues primary and backup
operations together and only returns success once both complete.

This gives callers a clearer persistence guarantee when a backup store is
configured: acknowledged primary+backup mutations have been attempted
against both durable stores. The tradeoff is that dual-store operations
are not atomic across stores, so an error may still be returned after one
store has already been updated.

TierStore also implements `KVStoreSync` in terms of dedicated synchronous
helpers that call the wrapped stores' sync interfaces directly. This
preserves the inner stores' synchronous semantics instead of routing sync
operations through a previously held async runtime.

Additionally, adds unit coverage for the current contract, including:
- basic read/write/remove/list persistence
- routing of ephemeral data away from the primary store
- backup participation in the foreground success path for writes and removals
Add native builder support for tiered storage by introducing
`TierStoreConfig`, backup and ephemeral store configuration methods,
and a `build_with_dynstore` path that wraps the provided store as the
primary tier and applies any configured secondary tiers.

This makes tiered storage a builder concern while keeping the Rust
`build_with_store` API ergonomic for native callers.

Note: The temporary `dead_code` allowance will be removed in a
follow-up commit once the new tier-store builder APIs are exercised.
@enigbe enigbe force-pushed the 2025-10-tiered-data-storage branch 2 times, most recently from 19a6c09 to 7720935 Compare April 8, 2026 07:12
@enigbe
Copy link
Copy Markdown
Contributor Author

enigbe commented Apr 8, 2026

Hi @tnull @TheBlueMatt,

Quick update on the rework here.

Following the review feedback, I’ve now split this into a 2-PR stack so the core storage changes can be reviewed separately from the FFI surface and binding tests:

  • #692: the Rust-side TierStore implementation and native NodeBuilder integration
  • #871: FFI exposure plus Rust/Python integration coverage on top

As part of that rework, I also incorporated a number of design changes from the earlier version of this PR:

  • The retry configuration / nested retry handling has been dropped from the core design for now
  • The old best-effort queued backup path has been removed. Backup persistence is now part of the foreground success path for primary-backed writes/removals: when a backup store is configured, the primary and backup operations are issued in parallel and success is only reported once both complete. This gives a stronger and clearer durability guarantee for the primary+backup case that motivated the feature, while also simplifying the implementation compared to the earlier best-effort async queueing approach
  • KVStoreSync now delegates to the inner stores’ sync implementations rather than sharing TierStore’s async/runtime path, so wrapped stores can preserve their own sync behavior
  • Write-ordering concerns are no longer duplicated in TierStore; where ordering matters across the FFI boundary, it is handled in the FFI-facing store wrapper instead
  • The FFI-specific surface area and end-to-end binding tests have been moved into the follow-up PR so this PR stays focused on the Rust-side storage implementation and integration

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.

4 participants