-
Notifications
You must be signed in to change notification settings - Fork 1.5k
execution, db: bind StateCache fills to transaction views and reject stale fills #22444
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
Changes from all commits
ec77c91
f99e10e
421d0d3
dc3ab95
a4f833d
4a38f91
cb2cdde
fdf367f
c557351
436ae1c
e976b63
82ee60d
1ff29c1
616b269
fd1c8d1
87ddb96
e1eb581
4ff164e
576b787
7b4efea
60eecc1
748f6f8
14d5f7a
d662dcd
9fba079
04e35ec
70ffa64
ba58d75
33af7de
28e36cf
0f1b26e
57fbc1a
ca3f6ea
6b784ea
bb25293
d2ffe0f
446621d
653f289
f98dc13
6619446
25934ef
cc3066b
3f7945f
588b5f7
f53ee44
4004aaa
ba5263b
02d2eaa
dcff899
58db5ab
0d4ae69
576b086
6ace158
cc96203
30f2ac2
d34d775
91ccbe5
f3d8b0d
15756de
610e3e6
e50f801
706b091
055812b
b059321
a3cc38c
a0bd162
e3279d2
f8c965f
d426502
2b9c831
07d03c7
e8a1422
4523761
4ab8a36
bb75019
25c1e91
982cc8e
756cd2b
7af4d0f
87321bb
a880acc
6aa368e
f3cb104
3adae16
55bc6e6
ae0a59c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import ( | |
| "errors" | ||
| "fmt" | ||
| "sync" | ||
| "sync/atomic" | ||
| "time" | ||
|
|
||
| "github.com/erigontech/erigon/db/datadir" | ||
|
|
@@ -267,13 +268,71 @@ type tx struct { | |
| type Tx struct { | ||
| kv.Tx | ||
| tx | ||
| visibleEnds domainVisibleEnds | ||
| } | ||
|
|
||
| type RwTx struct { | ||
| kv.RwTx | ||
| tx | ||
| } | ||
|
|
||
| type domainVisibleEnds struct { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why Cache need to know files progress?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not files progress — it is this tx's own bound. The read view is two parts (pinned files + MDBX snapshot), so the exclusive txNum bound of what the tx can see is the max of the two ends; admission only compares that bound against |
||
| // ends is atomic so a lock-free read can overlap a reset-and-reload of | ||
| // the same slot without a data race. A torn read (state bit from one | ||
| // generation, end from another) can only be stale-low, which merely | ||
| // over-rejects fills: a view's frontier never decreases in a process that | ||
| // fills a cache — the DB component is frozen at tx begin, and a files | ||
| // reopen only extends it, an invariant the aggregator enforces once a | ||
| // fill-enabled cache is wired over it (ForbidVisibilityLowering). | ||
| ends [kv.DomainLen]atomic.Uint64 | ||
| mu sync.Mutex | ||
| state atomic.Uint32 | ||
| } | ||
|
|
||
| // state packs two bits per domain into one word so a single atomic load | ||
| // returns a consistent (loaded, ok) pair: loadedBit says ends[domain] is | ||
| // memoized, okBit is the memoized ok answer of DomainVisibleEnd. The array | ||
| // size asserts at compile time that both halves fit in uint32. | ||
| var _ [32 - 2*int(kv.DomainLen)]struct{} | ||
|
|
||
| func visibleEndBits(domain kv.Domain) (loadedBit, okBit uint32) { | ||
| loadedBit = uint32(1) << uint32(domain) | ||
| return loadedBit, loadedBit << uint32(kv.DomainLen) | ||
| } | ||
|
|
||
| func (v *domainVisibleEnds) get(tx *Tx, domain kv.Domain) (uint64, bool) { | ||
| loadedBit, okBit := visibleEndBits(domain) | ||
| state := v.state.Load() | ||
| if state&loadedBit != 0 { | ||
| return v.ends[domain].Load(), state&okBit != 0 | ||
| } | ||
| return v.load(tx, domain, loadedBit, okBit) | ||
| } | ||
|
|
||
| func (v *domainVisibleEnds) load(tx *Tx, domain kv.Domain, loadedBit, okBit uint32) (uint64, bool) { | ||
| v.mu.Lock() | ||
| defer v.mu.Unlock() | ||
|
|
||
| state := v.state.Load() | ||
| if state&loadedBit == 0 { | ||
| end, ok := tx.aggtx.DomainVisibleEnd(domain, tx.Tx) | ||
| v.ends[domain].Store(end) | ||
| state |= loadedBit | ||
| if ok { | ||
| state |= okBit | ||
| } | ||
| v.state.Store(state) | ||
| } | ||
| return v.ends[domain].Load(), state&okBit != 0 | ||
| } | ||
|
|
||
| // reset takes mu so an in-flight load can't re-store pre-reset bits. | ||
| func (v *domainVisibleEnds) reset() { | ||
| v.mu.Lock() | ||
| defer v.mu.Unlock() | ||
| v.state.Store(0) | ||
| } | ||
|
|
||
| func (tx *tx) ForceReopenUnderlyingFilesTx() { | ||
| if tx.blocktx != nil { | ||
| tx.blocktx.Close() | ||
|
|
@@ -284,6 +343,13 @@ func (tx *tx) ForceReopenUnderlyingFilesTx() { | |
| } | ||
| tx.aggtx = tx.Agg().BeginFilesRo() | ||
| } | ||
|
|
||
| // ForceReopenUnderlyingFilesTx swaps in a fresh files view, which can extend | ||
| // the visible frontier — drop the memoized ends so they are re-derived. | ||
| func (tx *Tx) ForceReopenUnderlyingFilesTx() { | ||
| tx.tx.ForceReopenUnderlyingFilesTx() | ||
| tx.visibleEnds.reset() | ||
| } | ||
| func (tx *tx) FreezeInfo() kv.FreezeInfo { return tx.aggtx } | ||
|
|
||
| func (tx *tx) AggTx() any { return tx.aggtx } | ||
|
|
@@ -724,6 +790,12 @@ func (tx *Tx) DomainProgress(domain kv.Domain) uint64 { | |
| func (tx *RwTx) DomainProgress(domain kv.Domain) uint64 { | ||
| return tx.aggtx.DomainProgress(domain, tx.RwTx) | ||
| } | ||
| func (tx *Tx) DomainVisibleEnd(domain kv.Domain) (uint64, bool) { | ||
| return tx.visibleEnds.get(tx, domain) | ||
| } | ||
| func (tx *RwTx) DomainVisibleEnd(domain kv.Domain) (uint64, bool) { | ||
| return tx.aggtx.DomainVisibleEnd(domain, tx.RwTx) | ||
| } | ||
| func (tx *Tx) IIProgress(domain kv.InvertedIdx) uint64 { | ||
| return tx.aggtx.IIProgress(domain, tx.Tx) | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.