@@ -12,6 +12,7 @@ import (
12
12
"sync/atomic"
13
13
"time"
14
14
15
+ lru "github.com/hashicorp/golang-lru/v2"
15
16
json "github.com/nspcc-dev/go-ordered-json"
16
17
"github.com/nspcc-dev/neo-go/pkg/config"
17
18
"github.com/nspcc-dev/neo-go/pkg/config/limits"
@@ -61,6 +62,14 @@ const (
61
62
// HeaderVerificationGasLimit is the maximum amount of GAS for block header verification.
62
63
HeaderVerificationGasLimit = 3_00000000 // 3 GAS
63
64
defaultStateSyncInterval = 40000
65
+
66
+ // defaultBlockTimesCache should be sufficient for tryRunGC() to get in
67
+ // sync with storeBlock(). Most of the time they differ by some thousands of
68
+ // blocks and GC interval is more like 10K, so this is sufficient for 80K
69
+ // deviation and should be sufficient. If it's not, it's not a big issue
70
+ // either, the next cycle will still do the job (only transfers need this,
71
+ // MPT won't notice at all).
72
+ defaultBlockTimesCache = 8
64
73
)
65
74
66
75
// stateChangeStage denotes the stage of state modification process.
@@ -156,6 +165,11 @@ type Blockchain struct {
156
165
// Current persisted block count.
157
166
persistedHeight uint32
158
167
168
+ // Index->Timestamp cache for garbage collector. Headers can be gone
169
+ // by the time it runs, so we use a tiny little cache to sync block
170
+ // removal (performed in storeBlock()) with transfer/MPT GC (tryRunGC())
171
+ gcBlockTimes * lru.Cache [uint32 , uint64 ]
172
+
159
173
// Stop synchronization mechanisms.
160
174
stopCh chan struct {}
161
175
runToExitCh chan struct {}
@@ -324,6 +338,7 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
324
338
contracts : * native .NewContracts (cfg .ProtocolConfiguration ),
325
339
}
326
340
341
+ bc .gcBlockTimes , _ = lru.New [uint32 , uint64 ](defaultBlockTimesCache ) // Never errors for positive size
327
342
bc .stateRoot = stateroot .NewModule (cfg , bc .VerifyWitness , bc .log , bc .dao .Store )
328
343
bc .contracts .Designate .StateRootService = bc .stateRoot
329
344
@@ -606,7 +621,7 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateChangeStage) erro
606
621
// After current state is updated, we need to remove outdated state-related data if so.
607
622
// The only outdated data we might have is genesis-related data, so check it.
608
623
if p - bc .config .MaxTraceableBlocks > 0 {
609
- err := cache .DeleteBlock (bc .GetHeaderHash (0 ), false )
624
+ _ , err := cache .DeleteBlock (bc .GetHeaderHash (0 ), false )
610
625
if err != nil {
611
626
return fmt .Errorf ("failed to remove outdated state data for the genesis block: %w" , err )
612
627
}
@@ -800,7 +815,7 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage)
800
815
keysCnt = new (int )
801
816
)
802
817
for i := height + 1 ; i <= currHeight ; i ++ {
803
- err := upperCache .DeleteBlock (bc .GetHeaderHash (i ), false )
818
+ _ , err := upperCache .DeleteBlock (bc .GetHeaderHash (i ), false )
804
819
if err != nil {
805
820
return fmt .Errorf ("error while removing block %d: %w" , i , err )
806
821
}
@@ -1289,15 +1304,20 @@ func appendTokenTransferInfo(transferData *state.TokenTransferInfo,
1289
1304
1290
1305
func (bc * Blockchain ) removeOldTransfers (index uint32 ) time.Duration {
1291
1306
bc .log .Info ("starting transfer data garbage collection" , zap .Uint32 ("index" , index ))
1292
- start := time .Now ()
1293
- h , err := bc .GetHeader (bc .GetHeaderHash (index ))
1294
- if err != nil {
1307
+ var (
1308
+ err error
1309
+ kept int64
1310
+ removed int64
1311
+ start = time .Now ()
1312
+ ts , ok = bc .gcBlockTimes .Get (index )
1313
+ )
1314
+
1315
+ if ! ok {
1295
1316
dur := time .Since (start )
1296
- bc .log .Error ("failed to find block header for transfer GC" , zap .Duration ("time" , dur ), zap .Error ( err ))
1317
+ bc .log .Error ("failed to get block timestamp transfer GC" , zap .Duration ("time" , dur ), zap .Uint32 ( "index" , index ))
1297
1318
return dur
1298
1319
}
1299
- var removed , kept int64
1300
- var ts = h .Timestamp
1320
+
1301
1321
prefixes := []byte {byte (storage .STNEP11Transfers ), byte (storage .STNEP17Transfers )}
1302
1322
1303
1323
for i := range prefixes {
@@ -1623,7 +1643,10 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
1623
1643
stop = start + 1
1624
1644
}
1625
1645
for index := start ; index < stop ; index ++ {
1626
- err := kvcache .DeleteBlock (bc .GetHeaderHash (index ), bc .config .Ledger .RemoveUntraceableHeaders )
1646
+ ts , err := kvcache .DeleteBlock (bc .GetHeaderHash (index ), bc .config .Ledger .RemoveUntraceableHeaders )
1647
+ if bc .config .Ledger .RemoveUntraceableHeaders && index % bc .config .Ledger .GarbageCollectionPeriod == 0 {
1648
+ _ = bc .gcBlockTimes .Add (index , ts )
1649
+ }
1627
1650
if err != nil {
1628
1651
bc .log .Warn ("error while removing old block" ,
1629
1652
zap .Uint32 ("index" , index ),
0 commit comments