Skip to content

Commit 1a74865

Browse files
authored
Merge pull request #2018 from CortexFoundation/dev
core/rawdb: implement in-memory freezer
2 parents de54914 + b181d57 commit 1a74865

14 files changed

+562
-87
lines changed

core/blockchain.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
693693
if num+1 <= frozen {
694694
// Truncate all relative data(header, total difficulty, body, receipt
695695
// and canonical hash) from ancient store.
696-
if err := bc.db.TruncateHead(num); err != nil {
696+
if _, err := bc.db.TruncateHead(num); err != nil {
697697
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
698698
}
699699
// Remove the hash <-> number mapping from the active store.
@@ -1139,7 +1139,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
11391139
size += int64(batch.ValueSize())
11401140
if err = batch.Write(); err != nil {
11411141
fastBlock := bc.CurrentFastBlock().NumberU64()
1142-
if err := bc.db.TruncateHead(fastBlock + 1); err != nil {
1142+
if _, err := bc.db.TruncateHead(fastBlock + 1); err != nil {
11431143
log.Error("Can't truncate ancient store after failed insert", "err", err)
11441144
}
11451145
return 0, err
@@ -1157,7 +1157,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
11571157
if !updateHead(blockChain[len(blockChain)-1]) {
11581158
// We end up here if the header chain has reorg'ed, and the blocks/receipts
11591159
// don't match the canonical chain.
1160-
if err := bc.db.TruncateHead(previousFastBlock + 1); err != nil {
1160+
if _, err := bc.db.TruncateHead(previousFastBlock + 1); err != nil {
11611161
log.Error("Can't truncate ancient store after failed insert", "err", err)
11621162
}
11631163
return 0, errSideChainReceipts

core/rawdb/ancient_scheme.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616

1717
package rawdb
1818

19+
import (
20+
"path/filepath"
21+
22+
"github.com/CortexFoundation/CortexTheseus/ctxcdb"
23+
)
24+
1925
// The list of table names of chain freezer.
2026
const (
2127
// ChainFreezerHeaderTable indicates the name of the freezer header table.
@@ -66,9 +72,22 @@ var stateFreezerNoSnappy = map[string]bool{
6672

6773
// The list of identifiers of ancient stores.
6874
var (
69-
chainFreezerName = "chain" // the folder name of chain segment ancient store.
70-
stateFreezerName = "state" // the folder name of reverse diff ancient store.
75+
ChainFreezerName = "chain" // the folder name of chain segment ancient store.
76+
StateFreezerName = "state" // the folder name of reverse diff ancient store.
7177
)
7278

7379
// freezers the collections of all builtin freezers.
74-
var freezers = []string{chainFreezerName}
80+
var freezers = []string{ChainFreezerName, StateFreezerName}
81+
82+
// NewStateFreezer initializes the ancient store for state history.
83+
//
84+
// - if the empty directory is given, initializes the pure in-memory
85+
// state freezer (e.g. dev mode).
86+
// - if non-empty directory is given, initializes the regular file-based
87+
// state freezer.
88+
func NewStateFreezer(ancientDir string, readOnly bool) (ctxcdb.ResettableAncientStore, error) {
89+
if ancientDir == "" {
90+
return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil
91+
}
92+
return newResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
93+
}

core/rawdb/ancient_utils.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func inspectFreezers(db ctxcdb.Database) ([]freezerInfo, error) {
5656
var infos []freezerInfo
5757
for _, freezer := range freezers {
5858
switch freezer {
59-
case chainFreezerName:
59+
case ChainFreezerName:
6060
// Chain ancient store is a bit special. It's always opened along
6161
// with the key-value store, inspect the chain store directly.
6262
info := freezerInfo{name: freezer}
@@ -100,9 +100,9 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s
100100
tables map[string]bool
101101
)
102102
switch freezerName {
103-
case chainFreezerName:
103+
case ChainFreezerName:
104104
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
105-
case stateFreezerName:
105+
case StateFreezerName:
106106
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
107107
default:
108108
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)

core/rawdb/chain_freezer.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,40 @@ const (
3939
freezerBatchLimit = 30000
4040
)
4141

42-
// chainFreezer is a wrapper of freezer with additional chain freezing feature.
43-
// The background thread will keep moving ancient chain segments from key-value
44-
// database to flat files for saving space on live database.
42+
// chainFreezer is a wrapper of chain ancient store with additional chain freezing
43+
// feature. The background thread will keep moving ancient chain segments from
44+
// key-value database to flat files for saving space on live database.
4545
type chainFreezer struct {
46-
*Freezer
46+
ctxcdb.AncientStore // Ancient store for storing cold chain segment
47+
4748
quit chan struct{}
4849
wg sync.WaitGroup
4950
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
5051
}
5152

52-
// newChainFreezer initializes the freezer for ancient chain data.
53+
// newChainFreezer initializes the freezer for ancient chain segment.
54+
//
55+
// - if the empty directory is given, initializes the pure in-memory
56+
// state freezer (e.g. dev mode).
57+
// - if non-empty directory is given, initializes the regular file-based
58+
// state freezer.
5359
func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFreezer, error) {
54-
freezer, err := NewChainFreezer(datadir, namespace, readonly)
60+
var (
61+
err error
62+
freezer ctxcdb.AncientStore
63+
)
64+
if datadir == "" {
65+
freezer = NewMemoryFreezer(readonly, chainFreezerNoSnappy)
66+
} else {
67+
freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerNoSnappy)
68+
}
5569
if err != nil {
5670
return nil, err
5771
}
5872
return &chainFreezer{
59-
Freezer: freezer,
60-
quit: make(chan struct{}),
61-
trigger: make(chan chan struct{}),
73+
AncientStore: freezer,
74+
quit: make(chan struct{}),
75+
trigger: make(chan chan struct{}),
6276
}, nil
6377
}
6478

@@ -70,7 +84,7 @@ func (f *chainFreezer) Close() error {
7084
close(f.quit)
7185
}
7286
f.wg.Wait()
73-
return f.Freezer.Close()
87+
return f.AncientStore.Close()
7488
}
7589

7690
// readHeadNumber returns the number of chain head block. 0 is returned if the
@@ -167,7 +181,7 @@ func (f *chainFreezer) freeze(db ctxcdb.KeyValueStore) {
167181
log.Debug("Current full block not old enough to freeze", "err", err)
168182
continue
169183
}
170-
frozen := f.frozen.Load()
184+
frozen, _ := f.Ancients() // no error will occur, safe to ignore
171185

172186
// Short circuit if the blocks below threshold are already frozen.
173187
if frozen != 0 && frozen-1 >= threshold {
@@ -190,7 +204,7 @@ func (f *chainFreezer) freeze(db ctxcdb.KeyValueStore) {
190204
backoff = true
191205
continue
192206
}
193-
// Batch of blocks have been frozen, flush them before wiping from leveldb
207+
// Batch of blocks have been frozen, flush them before wiping from key-value store
194208
if err := f.Sync(); err != nil {
195209
log.Crit("Failed to flush frozen tables", "err", err)
196210
}
@@ -210,7 +224,7 @@ func (f *chainFreezer) freeze(db ctxcdb.KeyValueStore) {
210224

211225
// Wipe out side chains also and track dangling side chains
212226
var dangling []common.Hash
213-
frozen = f.frozen.Load() // Needs reload after during freezeRange
227+
frozen, _ = f.Ancients() // Needs reload after during freezeRange
214228
for number := first; number < frozen; number++ {
215229
// Always keep the genesis block in active database
216230
if number != 0 {

core/rawdb/database.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ import (
3636
"github.com/CortexFoundation/CortexTheseus/log"
3737
)
3838

39-
// freezerdb is a database wrapper that enabled freezer data retrievals.
39+
// freezerdb is a database wrapper that enables ancient chain segment freezing.
4040
type freezerdb struct {
41-
ancientRoot string
4241
ctxcdb.KeyValueStore
43-
ctxcdb.AncientStore
42+
*chainFreezer
43+
44+
readOnly bool
45+
ancientRoot string
4446
}
4547

4648
// AncientDatadir returns the path of root ancient directory.
@@ -52,7 +54,7 @@ func (frdb *freezerdb) AncientDatadir() (string, error) {
5254
// the slow ancient tables.
5355
func (frdb *freezerdb) Close() error {
5456
var errs []error
55-
if err := frdb.AncientStore.Close(); err != nil {
57+
if err := frdb.chainFreezer.Close(); err != nil {
5658
errs = append(errs, err)
5759
}
5860
if err := frdb.KeyValueStore.Close(); err != nil {
@@ -68,12 +70,12 @@ func (frdb *freezerdb) Close() error {
6870
// a freeze cycle completes, without having to sleep for a minute to trigger the
6971
// automatic background run.
7072
func (frdb *freezerdb) Freeze() error {
71-
if frdb.AncientStore.(*chainFreezer).readonly {
73+
if frdb.readOnly {
7274
return errReadOnly
7375
}
7476
// Trigger a freeze cycle and block until it's done
7577
trigger := make(chan struct{}, 1)
76-
frdb.AncientStore.(*chainFreezer).trigger <- trigger
78+
frdb.chainFreezer.trigger <- trigger
7779
<-trigger
7880
return nil
7981
}
@@ -119,13 +121,13 @@ func (db *nofreezedb) ModifyAncients(func(ctxcdb.AncientWriteOp) error) (int64,
119121
}
120122

121123
// TruncateHead returns an error as we don't have a backing chain freezer.
122-
func (db *nofreezedb) TruncateHead(items uint64) error {
123-
return errNotSupported
124+
func (db *nofreezedb) TruncateHead(items uint64) (uint64, error) {
125+
return 0, errNotSupported
124126
}
125127

126128
// TruncateTail returns an error as we don't have a backing chain freezer.
127-
func (db *nofreezedb) TruncateTail(items uint64) error {
128-
return errNotSupported
129+
func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
130+
return 0, errNotSupported
129131
}
130132

131133
// Sync returns an error as we don't have a backing chain freezer.
@@ -173,7 +175,7 @@ func resolveChainFreezerDir(ancient string) string {
173175
// sub folder, if not then two possibilities:
174176
// - chain freezer is not initialized
175177
// - chain freezer exists in legacy location (root ancient folder)
176-
freezer := path.Join(ancient, chainFreezerName)
178+
freezer := path.Join(ancient, ChainFreezerName)
177179
if !common.FileExist(freezer) {
178180
if !common.FileExist(ancient) {
179181
// The entire ancient store is not initialized, still use the sub
@@ -194,8 +196,13 @@ func resolveChainFreezerDir(ancient string) string {
194196
// storage. The passed ancient indicates the path of root ancient directory
195197
// where the chain freezer can be opened.
196198
func NewDatabaseWithFreezer(db ctxcdb.KeyValueStore, ancient string, namespace string, readonly bool) (ctxcdb.Database, error) {
197-
// Create the idle freezer instance
198-
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly)
199+
// Create the idle freezer instance. If the given ancient directory is empty,
200+
// in-memory chain freezer is used (e.g. dev mode); otherwise the regular
201+
// file-based freezer is created.
202+
if ancient != "" {
203+
ancient = resolveChainFreezerDir(ancient)
204+
}
205+
frdb, err := newChainFreezer(ancient, namespace, readonly)
199206
if err != nil {
200207
printChainMetadata(db)
201208
return nil, err
@@ -279,7 +286,7 @@ func NewDatabaseWithFreezer(db ctxcdb.KeyValueStore, ancient string, namespace s
279286
}
280287
}
281288
// Freezer is consistent with the key-value database, permit combining the two
282-
if !frdb.readonly {
289+
if !readonly {
283290
frdb.wg.Add(1)
284291
go func() {
285292
frdb.freeze(db)
@@ -289,7 +296,7 @@ func NewDatabaseWithFreezer(db ctxcdb.KeyValueStore, ancient string, namespace s
289296
return &freezerdb{
290297
ancientRoot: ancient,
291298
KeyValueStore: db,
292-
AncientStore: frdb,
299+
chainFreezer: frdb,
293300
}, nil
294301
}
295302

core/rawdb/freezer.go

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const freezerTableSize = 2 * 1000 * 1000 * 1000
6363
// reserving it for CortexTheseus. This would also reduce the memory requirements
6464
// of Geth, and thus also GC overhead.
6565
type Freezer struct {
66-
frozen atomic.Uint64 // Number of blocks already frozen
66+
frozen atomic.Uint64 // Number of items already frozen
6767
tail atomic.Uint64 // Number of the first stored item in the freezer
6868

6969
// This lock synchronizes writers and the truncate operation, as well as
@@ -77,12 +77,6 @@ type Freezer struct {
7777
closeOnce sync.Once
7878
}
7979

80-
// NewChainFreezer is a small utility method around NewFreezer that sets the
81-
// default parameters for the chain storage.
82-
func NewChainFreezer(datadir string, namespace string, readonly bool) (*Freezer, error) {
83-
return NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerNoSnappy)
84-
}
85-
8680
// NewFreezer creates a freezer instance for maintaining immutable ordered
8781
// data according to the given parameters.
8882
//
@@ -280,43 +274,45 @@ func (f *Freezer) ModifyAncients(fn func(ctxcdb.AncientWriteOp) error) (writeSiz
280274
}
281275

282276
// TruncateHead discards any recent data above the provided threshold number.
283-
func (f *Freezer) TruncateHead(items uint64) error {
277+
func (f *Freezer) TruncateHead(items uint64) (uint64, error) {
284278
if f.readonly {
285-
return errReadOnly
279+
return 0, errReadOnly
286280
}
287281
f.writeLock.Lock()
288282
defer f.writeLock.Unlock()
289283

290-
if f.frozen.Load() <= items {
291-
return nil
284+
oitems := f.frozen.Load()
285+
if oitems <= items {
286+
return oitems, nil
292287
}
293288
for _, table := range f.tables {
294289
if err := table.truncateHead(items); err != nil {
295-
return err
290+
return 0, err
296291
}
297292
}
298293
f.frozen.Store(items)
299-
return nil
294+
return oitems, nil
300295
}
301296

302297
// TruncateTail discards any recent data below the provided threshold number.
303-
func (f *Freezer) TruncateTail(tail uint64) error {
298+
func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
304299
if f.readonly {
305-
return errReadOnly
300+
return 0, errReadOnly
306301
}
307302
f.writeLock.Lock()
308303
defer f.writeLock.Unlock()
309304

310-
if f.tail.Load() >= tail {
311-
return nil
305+
old := f.tail.Load()
306+
if old >= tail {
307+
return old, nil
312308
}
313309
for _, table := range f.tables {
314310
if err := table.truncateTail(tail); err != nil {
315-
return err
311+
return 0, err
316312
}
317313
}
318314
f.tail.Store(tail)
319-
return nil
315+
return old, nil
320316
}
321317

322318
// Sync flushes all data tables to disk.

0 commit comments

Comments
 (0)