Skip to content
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

320 rounds: expose block header from txtail #3935

Merged

Conversation

algorandskiy
Copy link
Contributor

@algorandskiy algorandskiy commented Apr 28, 2022

  1. Make block header accessible from txtail with BlockHdrCached method
  2. Some fixes to in-memory data representation
  3. Added new consensus parameter to increase blocks lookback size
  4. More comments and test

ledger/tracker.go Outdated Show resolved Hide resolved
@algorandskiy algorandskiy changed the title 320 rounds: expose block timestamp and seed from txtail 320 rounds: expose block header from txtail May 4, 2022
ledger/txtail.go Outdated
@@ -207,17 +229,26 @@ func (t *txTail) committedUpTo(rnd basics.Round) (retRound, lookback basics.Roun
delete(t.lastValid, t.lowWaterMark)
}

return (rnd + 1).SubSaturate(maxlife), basics.Round(0)
deeperHistory := basics.Round(t.recent[rnd].proto.DeeperBlockHeaderHistory)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we consider removing proto from roundLeases now and switching to t.blockHeaderData[rnd]? Now we have two consensus versions/protos in the txtail row?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I tried this. Using t.blockHeaderData instead of t.recent requires taking a t.tailMu lock (t.blockHeaderData is changed in commitRound and Co and the race detector complains). t.recent is updated in newBlock and protected by l.trackerMu in ledger.
For some reason the old l.trackerMu lock does not lead to a horrible performance degradation as t.tailMu does. I need to look further, leaving as is now.

ledger/txtail.go Outdated
bufIdx := 0
t.tailMu.RLock()
lastOffset := offset + maxTxnLife
lastOffset := offset + retianSize - 1
Copy link
Contributor

Choose a reason for hiding this comment

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

check -1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the loop below is supposed to fill retainSize elements starting from offset, this means it should make retainSize iterations and lastOffset is exclusive => size of the interval [offset, lastOffset) is lastOffset - offset = retainSize. Fixed

ledger/txtail.go Outdated Show resolved Hide resolved
ledger/txtail_test.go Outdated Show resolved Hide resolved
Copy link
Contributor

@tolikzinovyev tolikzinovyev left a comment

Choose a reason for hiding this comment

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

Leaving some comments. Will continue review tomorrow.

config/consensus.go Outdated Show resolved Hide resolved
config/consensus.go Outdated Show resolved Hide resolved
ledger/accountdb.go Show resolved Hide resolved
ledger/accountdb.go Outdated Show resolved Hide resolved
ledger/ledger.go Outdated Show resolved Hide resolved
// and valid if txn.LastValid - txn.FirstValid <= MaxTxnLife
// the deepest lookup happens when txn.LastValid == current => txn.LastValid == Lastest + 1
// that gives Lastest + 1 - (MaxTxnLife + 1) = Lastest - MaxTxnLife as the first round to be accessible.
func (l *Ledger) BlockHdrCached(rnd basics.Round) (bookkeeping.BlockHeader, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use this cache in BlockHdr() instead of making a new function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the problem with differect code paths that call BlockHdr and eventually deadlock the tracker in initialization (namely ledger.(*accountUpdates).initializeFromDisk called from reloadLedger needs calls BlockHdr with trackers lock taken.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This requires deeper rework and getting rid of tr.mu or trackerMu mutexes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, it's strange that locks are needed at all during initialization.

ledger/ledger_test.go Show resolved Hide resolved
@codecov-commenter
Copy link

codecov-commenter commented May 6, 2022

Codecov Report

Merging #3935 (8cc23c1) into feature/320-rounds (1a3e310) will increase coverage by 0.06%.
The diff coverage is 83.51%.

@@                  Coverage Diff                   @@
##           feature/320-rounds    #3935      +/-   ##
======================================================
+ Coverage               50.13%   50.19%   +0.06%     
======================================================
  Files                     412      412              
  Lines                   70683    70701      +18     
======================================================
+ Hits                    35434    35488      +54     
+ Misses                  31384    31352      -32     
+ Partials                 3865     3861       -4     
Impacted Files Coverage Δ
ledger/trackerdb.go 46.72% <ø> (ø)
ledger/accountdb.go 71.76% <40.00%> (-0.02%) ⬇️
ledger/msgp_gen.go 42.02% <66.66%> (-0.04%) ⬇️
ledger/txtail.go 91.61% <90.56%> (-1.52%) ⬇️
config/consensus.go 85.71% <100.00%> (+0.04%) ⬆️
ledger/ledger.go 65.04% <100.00%> (+0.32%) ⬆️
ledger/tracker.go 77.27% <100.00%> (+1.63%) ⬆️
catchup/peerSelector.go 98.95% <0.00%> (-1.05%) ⬇️
catchup/service.go 69.38% <0.00%> (ø)
... and 7 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a3e310...8cc23c1. Read the comment docs.

config/consensus.go Outdated Show resolved Hide resolved
ledger/ledger.go Show resolved Hide resolved
// and valid if txn.LastValid - txn.FirstValid <= MaxTxnLife
// the deepest lookup happens when txn.LastValid == current => txn.LastValid == Lastest + 1
// that gives Lastest + 1 - (MaxTxnLife + 1) = Lastest - MaxTxnLife as the first round to be accessible.
func (l *Ledger) BlockHdrCached(rnd basics.Round) (bookkeeping.BlockHeader, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, it's strange that locks are needed at all during initialization.

ledger/ledger_test.go Outdated Show resolved Hide resolved
ledger/ledger_test.go Show resolved Hide resolved
// The first entry matches that current tracker database round - MaxTxnLife (tail size is MaxTxnLife + DeeperBlockHeaderHistory)
// the second to tracker database round - (MaxTxnLife - 1), and so forth, and the last element is for the latest round.
// Deltas are in-memory not-committed-yet data.
blockHeaderData map[basics.Round]bookkeeping.BlockHeader
Copy link
Contributor

Choose a reason for hiding this comment

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

why is it a map and not an array like other fields?

ledger/txtail.go Outdated
// preserve data for MaxTxnLife + DeeperBlockHeaderHistory
proto := config.Consensus[t.blockHeaderData[rnd].CurrentProtocol]
retainSize := proto.MaxTxnLife + proto.DeeperBlockHeaderHistory
newLowestRound := rnd.SubSaturate(basics.Round(retainSize)) + 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
newLowestRound := rnd.SubSaturate(basics.Round(retainSize)) + 1
newLowestRound := rnd.SubSaturate(basics.Round(retainSize) + 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no. say newBase = 10 and retain size is 3. Then the map should have 8, 9, 10 and newLowestRound = 8.
so newLowestRound = 10 - 3 + 1 = 8.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I meant newLowestRound := rnd.SubSaturate(basics.Round(retainSize) - 1). The reason is that we don't want to remove round 0 when we shouldn't.

Alternatively:

newLowestRound := 0
if rnd + 1 > retainSize {
  newLowestRound = rnd + 1 - retainSize
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this makes sense, thank you for noticing.

ledger/txtail.go Outdated
proto := config.Consensus[t.blockHeaderData[rnd].CurrentProtocol]
retainSize := proto.MaxTxnLife + proto.DeeperBlockHeaderHistory
newLowestRound := rnd.SubSaturate(basics.Round(retainSize)) + 1
if newLowestRound > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

this check seems unnecessary

ledger/txtail.go Outdated
proto := config.Consensus[t.blockHeaderData[rnd].CurrentProtocol]
retainSize := proto.MaxTxnLife + proto.DeeperBlockHeaderHistory
newLowestRound := rnd.SubSaturate(basics.Round(retainSize)) + 1
if newLowestRound > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if newLowestRound > 0 {
if newLowestRound > t.lowestBlockHeaderRound

otherwise increase in MaxTxnLife or DeeperBlockHeaderHistory can decrease t.lowestBlockHeaderRound

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right

newDeltaLength := len(t.roundTailSerializedDeltas)
firstTailIdx := len(t.roundTailHashes) - newDeltaLength - int(retainSize)
if firstTailIdx > 0 {
t.roundTailHashes = t.roundTailHashes[firstTailIdx:]
Copy link
Contributor

Choose a reason for hiding this comment

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

can't we just remove the first newLowestRound - t.lowestBlockHeaderRound elements similar to above?

@tolikzinovyev
Copy link
Contributor

A thought: could we make DeeperBlockHeaderHistory a boolean? That way we don't need to worry about it being > 1. We can change it to integer in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants