-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add and use LedgerEntryScope and ScopedLedgerEntry #5051
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
base: master
Are you sure you want to change the base?
Conversation
39cade4 to
3dac3dd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new ledger entry scoping system to prevent stale reads during parallel transaction application. It wraps LedgerEntry objects in ScopedLedgerEntry<S> templates where S is a static scope type (GLOBAL, THREAD, or TX), and requires static and dynamic validation when reading or modifying entries.
- Implements
LedgerEntryScopeandScopedLedgerEntryclasses with scope tracking and validation - Renames operation-level classes from "Op" to "Tx" prefix to better reflect transaction-level scope
- Updates parallel apply infrastructure to use scoped entries with adoption patterns between scope levels
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ledger/LedgerEntryScope.h | Defines core scoping infrastructure including ScopedLedgerEntry, LedgerEntryScope, and DeactivateScopeGuard templates |
| src/ledger/LedgerEntryScope.cpp | Implements scoping logic including scope validation, entry adoption, and scope ID management |
| src/transactions/TransactionFrameBase.h | Updates type aliases and structures to use scoped entries; renames Op* types to Tx* to reflect transaction-level scope |
| src/transactions/ParallelApplyUtils.h | Updates method signatures and class names from Op to Tx prefix; adds scope inheritance to state classes |
| src/transactions/ParallelApplyUtils.cpp | Implements scope adoption patterns for moving entries between global, thread, and transaction scopes |
| src/transactions/TransactionFrame.cpp | Updates transaction application to use scoped return values with proper scope IDs |
| src/transactions/TransactionMeta.cpp | Updates meta builder to read entries through scope validation |
| src/transactions/InvokeHostFunctionOpFrame.cpp | Updates operation frame to use renamed Tx state classes |
| src/transactions/RestoreFootprintOpFrame.cpp | Updates operation frame to use renamed Tx state classes |
| src/transactions/ExtendFootprintTTLOpFrame.cpp | Updates operation frame to use renamed Tx state classes and return proper scoped values |
| src/ledger/LedgerManagerImpl.cpp | Implements proper scope deactivation patterns when launching parallel threads |
3dac3dd to
fa9fe12
Compare
SirTyson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on this! It looks like a fairly tricky change, but it looks like we ended up with some nice checks without being too intrusive to the callers.
Most of my comments are more like feature requests I thought of while reviewing and are probably outside of scope, but wanted to note for future followups. The one comment I do think is worth addressing is not attaching scope info to null entries, which seems like a potential footgun.
SirTyson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Last comment is about the modify flow, which can still be hardened a bit I think, but overall it's looking very nice!
| bool operator<(ScopedLedgerEntryOpt const& other) const; | ||
| }; | ||
|
|
||
| #define SCOPE_ALIAS(SCOPE) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a comment here describing this macro generates types like TxParApplyLedgerEntryOpt would be helpful, which also references back to the types in FOREACH_STATIC_LEDGER_ENTRY_SCOPE. Reason being is whenever I click on a generated type name, my IDE brings me to this macro, which is confusing to someone who hasn't been starting at this file for a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
ae480ec to
ccab465
Compare
|
Ok I've addressed all review comments and added the big block comment I meant to get around to. I think this is ready. |
SirTyson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One nitpick on the interface, but otherwise LGTM, thanks for all the back and forth!
This is an initial implementation of the idea described in #4971 -- what I'm now calling "ledger entry scopes"
At a high level, the idea here is to:
LedgerEntriesin helper classes calledScopedLedgerEntry<S>whereSis one of a static set of known scope-types (GLOBAL_PAR_APPLY_STATE,THREAD_PAR_APPLY_STATE,TX_PAR_APPLY_STATEand so forth).LedgerEntryScope<S>that is inherited by each class we use to store "random hashtables-full-of-LedgerEntries" likeThreadParallelApplyLedgerStateorGlobalParallelApplyLedgerStateor such)ScopedLedgerEntryto read or modify the containedLedgerEntry, you provide the scope you're reading or writing it in, and statically require theSparameters match.ScopedLedgerEntrymatch the scope: the ledger number and a generic "index" number which can be used to encode either the thread-cluster number or the bucket list number or similar things where there are "a bunch of similar groups of LEs that should nonetheless be kept separate".All told this should provide a similar level of protection from stale reads as we're getting with the LTX, but in a sort of "decentralized" way that doesn't require uniformly using an LTX for everything. Neither system provides perfect protection against stale reads -- you can hold on to an unwrapped LedgerEntry longer than you should or pass it somewhere you shouldn't -- but it at least gives us a bit more structure to lean on to prevent such errors.
Caveats
It's not wired-in to the bucket list or in-memory soroban state yet. I mean to do so but that'll come later.
I tried to be a little more explicit than the methods in the LTX: to not use operator* or operator(bool) or such, and not rely on destructors more than necessary (there's one guard object type to help with exception safety). I also named all the scope-mixin-provided methods with
scope_as a prefix, which might make it a little more verbose but I think clarity should be the main priority with this code.