Skip to content

Commit ca473b8

Browse files
authored
core: use finalized block as the chain freeze indicator (#28683)
* core: use finalized block as the chain freeze indicator * core/rawdb: use max(finality, head-90k) as chain freezing threshold * core/rawdb: fix tests * core/rawdb: fix lint * core/rawdb: address comments from peter * core/rawdb: fix typo
1 parent a97d622 commit ca473b8

File tree

4 files changed

+84
-55
lines changed

4 files changed

+84
-55
lines changed

core/blockchain_repair_test.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -1757,7 +1757,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
17571757

17581758
func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
17591759
// It's hard to follow the test case, visualize the input
1760-
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
1760+
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
17611761
// fmt.Println(tt.dump(true))
17621762

17631763
// Create a temporary persistent database
@@ -1830,10 +1830,14 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
18301830
}
18311831
// Force run a freeze cycle
18321832
type freezer interface {
1833-
Freeze(threshold uint64) error
1833+
Freeze() error
18341834
Ancients() (uint64, error)
18351835
}
1836-
db.(freezer).Freeze(tt.freezeThreshold)
1836+
if tt.freezeThreshold < uint64(tt.canonicalBlocks) {
1837+
final := uint64(tt.canonicalBlocks) - tt.freezeThreshold
1838+
chain.SetFinalized(canonblocks[int(final)-1].Header())
1839+
}
1840+
db.(freezer).Freeze()
18371841

18381842
// Set the simulated pivot block
18391843
if tt.pivotBlock != nil {

core/blockchain_sethead_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -2044,10 +2044,14 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
20442044

20452045
// Force run a freeze cycle
20462046
type freezer interface {
2047-
Freeze(threshold uint64) error
2047+
Freeze() error
20482048
Ancients() (uint64, error)
20492049
}
2050-
db.(freezer).Freeze(tt.freezeThreshold)
2050+
if tt.freezeThreshold < uint64(tt.canonicalBlocks) {
2051+
final := uint64(tt.canonicalBlocks) - tt.freezeThreshold
2052+
chain.SetFinalized(canonblocks[int(final)-1].Header())
2053+
}
2054+
db.(freezer).Freeze()
20512055

20522056
// Set the simulated pivot block
20532057
if tt.pivotBlock != nil {

core/rawdb/chain_freezer.go

+70-43
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
package rawdb
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"sync"
22-
"sync/atomic"
2323
"time"
2424

2525
"github.com/ethereum/go-ethereum/common"
@@ -43,8 +43,6 @@ const (
4343
// The background thread will keep moving ancient chain segments from key-value
4444
// database to flat files for saving space on live database.
4545
type chainFreezer struct {
46-
threshold atomic.Uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests)
47-
4846
*Freezer
4947
quit chan struct{}
5048
wg sync.WaitGroup
@@ -57,13 +55,11 @@ func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFre
5755
if err != nil {
5856
return nil, err
5957
}
60-
cf := chainFreezer{
58+
return &chainFreezer{
6159
Freezer: freezer,
6260
quit: make(chan struct{}),
6361
trigger: make(chan chan struct{}),
64-
}
65-
cf.threshold.Store(params.FullImmutabilityThreshold)
66-
return &cf, nil
62+
}, nil
6763
}
6864

6965
// Close closes the chain freezer instance and terminates the background thread.
@@ -77,6 +73,57 @@ func (f *chainFreezer) Close() error {
7773
return f.Freezer.Close()
7874
}
7975

76+
// readHeadNumber returns the number of chain head block. 0 is returned if the
77+
// block is unknown or not available yet.
78+
func (f *chainFreezer) readHeadNumber(db ethdb.KeyValueReader) uint64 {
79+
hash := ReadHeadBlockHash(db)
80+
if hash == (common.Hash{}) {
81+
log.Error("Head block is not reachable")
82+
return 0
83+
}
84+
number := ReadHeaderNumber(db, hash)
85+
if number == nil {
86+
log.Error("Number of head block is missing")
87+
return 0
88+
}
89+
return *number
90+
}
91+
92+
// readFinalizedNumber returns the number of finalized block. 0 is returned
93+
// if the block is unknown or not available yet.
94+
func (f *chainFreezer) readFinalizedNumber(db ethdb.KeyValueReader) uint64 {
95+
hash := ReadFinalizedBlockHash(db)
96+
if hash == (common.Hash{}) {
97+
return 0
98+
}
99+
number := ReadHeaderNumber(db, hash)
100+
if number == nil {
101+
log.Error("Number of finalized block is missing")
102+
return 0
103+
}
104+
return *number
105+
}
106+
107+
// freezeThreshold returns the threshold for chain freezing. It's determined
108+
// by formula: max(finality, HEAD-params.FullImmutabilityThreshold).
109+
func (f *chainFreezer) freezeThreshold(db ethdb.KeyValueReader) (uint64, error) {
110+
var (
111+
head = f.readHeadNumber(db)
112+
final = f.readFinalizedNumber(db)
113+
headLimit uint64
114+
)
115+
if head > params.FullImmutabilityThreshold {
116+
headLimit = head - params.FullImmutabilityThreshold
117+
}
118+
if final == 0 && headLimit == 0 {
119+
return 0, errors.New("freezing threshold is not available")
120+
}
121+
if final > headLimit {
122+
return final, nil
123+
}
124+
return headLimit, nil
125+
}
126+
80127
// freeze is a background thread that periodically checks the blockchain for any
81128
// import progress and moves ancient data from the fast database into the freezer.
82129
//
@@ -114,60 +161,39 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
114161
return
115162
}
116163
}
117-
// Retrieve the freezing threshold.
118-
hash := ReadHeadBlockHash(nfdb)
119-
if hash == (common.Hash{}) {
120-
log.Debug("Current full block hash unavailable") // new chain, empty database
164+
threshold, err := f.freezeThreshold(nfdb)
165+
if err != nil {
121166
backoff = true
167+
log.Debug("Current full block not old enough to freeze", "err", err)
122168
continue
123169
}
124-
number := ReadHeaderNumber(nfdb, hash)
125-
threshold := f.threshold.Load()
126170
frozen := f.frozen.Load()
127-
switch {
128-
case number == nil:
129-
log.Error("Current full block number unavailable", "hash", hash)
130-
backoff = true
131-
continue
132171

133-
case *number < threshold:
134-
log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
135-
backoff = true
136-
continue
137-
138-
case *number-threshold <= frozen:
139-
log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", frozen)
172+
// Short circuit if the blocks below threshold are already frozen.
173+
if frozen != 0 && frozen-1 >= threshold {
140174
backoff = true
175+
log.Debug("Ancient blocks frozen already", "threshold", threshold, "frozen", frozen)
141176
continue
142177
}
143-
head := ReadHeader(nfdb, hash, *number)
144-
if head == nil {
145-
log.Error("Current full block unavailable", "number", *number, "hash", hash)
146-
backoff = true
147-
continue
148-
}
149-
150178
// Seems we have data ready to be frozen, process in usable batches
151179
var (
152-
start = time.Now()
153-
first, _ = f.Ancients()
154-
limit = *number - threshold
180+
start = time.Now()
181+
first = frozen // the first block to freeze
182+
last = threshold // the last block to freeze
155183
)
156-
if limit-first > freezerBatchLimit {
157-
limit = first + freezerBatchLimit
184+
if last-first+1 > freezerBatchLimit {
185+
last = freezerBatchLimit + first - 1
158186
}
159-
ancients, err := f.freezeRange(nfdb, first, limit)
187+
ancients, err := f.freezeRange(nfdb, first, last)
160188
if err != nil {
161189
log.Error("Error in block freeze operation", "err", err)
162190
backoff = true
163191
continue
164192
}
165-
166193
// Batch of blocks have been frozen, flush them before wiping from leveldb
167194
if err := f.Sync(); err != nil {
168195
log.Crit("Failed to flush frozen tables", "err", err)
169196
}
170-
171197
// Wipe out all data from the active database
172198
batch := db.NewBatch()
173199
for i := 0; i < len(ancients); i++ {
@@ -250,8 +276,11 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
250276
}
251277
}
252278

279+
// freezeRange moves a batch of chain segments from the fast database to the freezer.
280+
// The parameters (number, limit) specify the relevant block range, both of which
281+
// are included.
253282
func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
254-
hashes = make([]common.Hash, 0, limit-number)
283+
hashes = make([]common.Hash, 0, limit-number+1)
255284

256285
_, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
257286
for ; number <= limit; number++ {
@@ -293,11 +322,9 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
293322
if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil {
294323
return fmt.Errorf("can't write td to Freezer: %v", err)
295324
}
296-
297325
hashes = append(hashes, hash)
298326
}
299327
return nil
300328
})
301-
302329
return hashes, err
303330
}

core/rawdb/database.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,10 @@ func (frdb *freezerdb) Close() error {
6666
// Freeze is a helper method used for external testing to trigger and block until
6767
// a freeze cycle completes, without having to sleep for a minute to trigger the
6868
// automatic background run.
69-
func (frdb *freezerdb) Freeze(threshold uint64) error {
69+
func (frdb *freezerdb) Freeze() error {
7070
if frdb.AncientStore.(*chainFreezer).readonly {
7171
return errReadOnly
7272
}
73-
// Set the freezer threshold to a temporary value
74-
defer func(old uint64) {
75-
frdb.AncientStore.(*chainFreezer).threshold.Store(old)
76-
}(frdb.AncientStore.(*chainFreezer).threshold.Load())
77-
frdb.AncientStore.(*chainFreezer).threshold.Store(threshold)
78-
7973
// Trigger a freeze cycle and block until it's done
8074
trigger := make(chan struct{}, 1)
8175
frdb.AncientStore.(*chainFreezer).trigger <- trigger

0 commit comments

Comments
 (0)