-
Notifications
You must be signed in to change notification settings - Fork 8
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
Make storage async #120
Make storage async #120
Conversation
This is an unmodified copy of twenty_first::storage at twenty-first revision 890c451e4e513018d8500bedcd5bf76dd0bafdd9 (master) It is not yet incorporated into the build.
fixes error: In order to be transferred, a Block must have a non-None proof field. ProofType enum enables specifying/transferring an unimplemented Proof. This is only temporary. BlockType enum enables specifying Genesis vs Standard block. A Standard block has a ProofType The Genesis block has no ProofType
Under crate::locks we previously had: - sync - tokio Each contains an impl of AtomicRw and AtomicMutex, with basically the same API. Yet: - sync refers to synchronous locks, ie sync vs async. - tokio refers to the tokio::sync lock implementation. So the names are referring to different things. Instead we change it to: - std - tokio Now each refers to a lock implementation, ie std::sync and tokio::sync. note: we could instead have changed `tokio` to `async`, but then there might be multiple async lock impls to choose from. So it seems cleanest to use the name of each impl.
Moved this benchmark over from twenty_first. Presently unable to build the twenty_first db_* bench tests because the storage layer is now async and the divan bench crate doesn't yet support async. However, it may soon, see: nvzqz/divan#39
Regarding ArchivalMmr and the Mmr traitDecoupling ArchivalMmr from the (non-async) Mmr trait seems complex. That trait is used in a number of places, such as:
It is instantiated in other locations as both ArchivalMmr (async) and MmrAccumulator (sync, from twenty_first). It has several complex methods, and is non-trivial. Presently, I have ArchivalMmr implementing Mmr trait by spawning a new OS thread for each trait-method call which creates a new tokio runtime, which calls the ArchivalMmr async method that actually implements the functionality (and interacts with storage layer). Example:
This works, but seems quite inefficient. Unfortunately though the only way I know of to call an async method from a sync method already executing in an async runtime is to spawn another thread with a new runtime. Worse though than inefficient, is that this actually makes the async calling fn block until the thread is finished. So for ArchivalMmr, this is no better (actually worse) than before this PR with regards to blocking on storage calls. Since ArchivalMmr is used heavily by MutatorSetKernel, this seems a big problem. However, It may be adequate for merging this PR, and then I can optimize/improve in a follow-up. Optimizing: Getting rid of the thread::spawn()Brainstorming approaches.
The difficulty here is that all MutatorSetKernel methods must use a ton of match statements like:
This is unwieldy. (though probably could be made less ugly with a macro) Both approaches 2 and 3 require MutatorSetKernel to become async, which means that anything using it must also be async. Approach 3 allows MmrAccumulator to remain sync, is the primary advantage, at cost of making MutatorSetKernel more complex.
The drawback is that all data must be loaded up-front and remain in mem. I'm unsure of how much data we are talking about, but my guess is that it may be huge eventually, and not possible to load/store all of it in RAM. Specifically, ArchivalMmr is used for MutatorSetKernel::aocl and MutatorSetKernel::swbf_inactive fields. All this considered, approach 2 seems to me the most straight-forward and the one I intend to pursue. |
Ok, I've made branch make_storage_and_mmr_trait_async with approach 2 (async Mmr trait) that builds cleanly, though not tests yet. ArchivalMmr no longer needs to spawn a thread plus runtime for each trait method invocation. So that's a win. I do have to spawn a thread in one other place though: RemovalRecordsIntegrity::rust_shadow(). This is in the In order to get the program to build, I had to comment out several impls of tasm-lib traits. These seem mainly related to testing, and I think they really belong within test module anyway. I'm not certain they can be made to build again unless the tasm traits are made async as well. my next task will be to get the tests building again. |
tests are building cleanly and passing in branch make_storage_and_mmr_trait_async. except for some proptests I had to comment-out for now because proptest crate/macro is not async compatible. I see somebody created a proptest_async crate recently (yesterday?) but it only works with async_std, not tokio yet. The author states it would be easy to add tokio support. The thread spawn in RemovalRecordsIntegrity::rust_shadow() remains. I don't know how often that fn will be called, or what impact it might have on performance, if any. For now I don't have a better solution, but it may be possible to refactor it out for those who understand tasm-lib well. Anyway, I am happier with the code in make_storage_and_mmr_trait_async, so after a little more cleanup/review I will open a new PR for that branch that will obsolete this PR. |
This error
indicates that somewhere the mutator set removal records or membership proofs are being updated incorrectly. The following propositions are probably true (quantified over unknown details):
|
closing in favor of #124. |
This is a first cut at making the storage layer async in neptune-core.
I am making it a draft PR because I have a few remaining todos to polish things up:
and storage[1] benches from twenty-first, modified as needed (easy)impl Mmr for ArchivalMmr
. done, see writeup.stretch goal:
[1] storage benches put aside for now because they would require async support in divan which appears to be in progress, or else a re-write without divan.
Present status: