forked from cerc-io/plugeth
cmd, core, eth, light, trie: dump clean cache periodically (#20391)
* cmd, core, eth, light, trie: dump clean cache periodically * eth: update config * trie: minor fix * core, trie: address comments * eth: remove useless * trie: print clean cache dump start too Co-authored-by: Péter Szilágyi <peterke@gmail.com>
This commit is contained in:
parent
79ce5537ab
commit
93da0cf8a1
@ -108,6 +108,8 @@ var (
|
|||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheTrieFlag,
|
utils.CacheTrieFlag,
|
||||||
|
utils.CacheTrieJournalFlag,
|
||||||
|
utils.CacheTrieRejournalFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
utils.CacheSnapshotFlag,
|
utils.CacheSnapshotFlag,
|
||||||
utils.CacheNoPrefetchFlag,
|
utils.CacheNoPrefetchFlag,
|
||||||
|
@ -109,6 +109,8 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheTrieFlag,
|
utils.CacheTrieFlag,
|
||||||
|
utils.CacheTrieJournalFlag,
|
||||||
|
utils.CacheTrieRejournalFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
utils.CacheSnapshotFlag,
|
utils.CacheSnapshotFlag,
|
||||||
utils.CacheNoPrefetchFlag,
|
utils.CacheNoPrefetchFlag,
|
||||||
|
@ -361,6 +361,16 @@ var (
|
|||||||
Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)",
|
Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)",
|
||||||
Value: 15,
|
Value: 15,
|
||||||
}
|
}
|
||||||
|
CacheTrieJournalFlag = cli.StringFlag{
|
||||||
|
Name: "cache.trie.journal",
|
||||||
|
Usage: "Disk journal directory for trie cache to survive node restarts",
|
||||||
|
Value: eth.DefaultConfig.TrieCleanCacheJournal,
|
||||||
|
}
|
||||||
|
CacheTrieRejournalFlag = cli.DurationFlag{
|
||||||
|
Name: "cache.trie.rejournal",
|
||||||
|
Usage: "Time interval to regenerate the trie cache journal",
|
||||||
|
Value: eth.DefaultConfig.TrieCleanCacheRejournal,
|
||||||
|
}
|
||||||
CacheGCFlag = cli.IntFlag{
|
CacheGCFlag = cli.IntFlag{
|
||||||
Name: "cache.gc",
|
Name: "cache.gc",
|
||||||
Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)",
|
Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)",
|
||||||
@ -1537,6 +1547,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) {
|
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) {
|
||||||
cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100
|
cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(CacheTrieJournalFlag.Name) {
|
||||||
|
cfg.TrieCleanCacheJournal = ctx.GlobalString(CacheTrieJournalFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(CacheTrieRejournalFlag.Name) {
|
||||||
|
cfg.TrieCleanCacheRejournal = ctx.GlobalDuration(CacheTrieRejournalFlag.Name)
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
|
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
|
||||||
cfg.TrieDirtyCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
|
cfg.TrieDirtyCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,8 @@ const (
|
|||||||
// that's resident in a blockchain.
|
// that's resident in a blockchain.
|
||||||
type CacheConfig struct {
|
type CacheConfig struct {
|
||||||
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
|
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
|
||||||
|
TrieCleanJournal string // Disk journal for saving clean cache entries.
|
||||||
|
TrieCleanRejournal time.Duration // Time interval to dump clean cache to disk periodically
|
||||||
TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
|
TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
|
||||||
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
|
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
|
||||||
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
|
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
|
||||||
@ -220,7 +222,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||||||
cacheConfig: cacheConfig,
|
cacheConfig: cacheConfig,
|
||||||
db: db,
|
db: db,
|
||||||
triegc: prque.New(nil),
|
triegc: prque.New(nil),
|
||||||
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit),
|
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit, cacheConfig.TrieCleanJournal),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
shouldPreserve: shouldPreserve,
|
shouldPreserve: shouldPreserve,
|
||||||
bodyCache: bodyCache,
|
bodyCache: bodyCache,
|
||||||
@ -328,6 +330,19 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||||||
bc.txLookupLimit = *txLookupLimit
|
bc.txLookupLimit = *txLookupLimit
|
||||||
go bc.maintainTxIndex(txIndexBlock)
|
go bc.maintainTxIndex(txIndexBlock)
|
||||||
}
|
}
|
||||||
|
// If periodic cache journal is required, spin it up.
|
||||||
|
if bc.cacheConfig.TrieCleanRejournal > 0 {
|
||||||
|
if bc.cacheConfig.TrieCleanRejournal < time.Minute {
|
||||||
|
log.Warn("Sanitizing invalid trie cache journal time", "provided", bc.cacheConfig.TrieCleanRejournal, "updated", time.Minute)
|
||||||
|
bc.cacheConfig.TrieCleanRejournal = time.Minute
|
||||||
|
}
|
||||||
|
triedb := bc.stateCache.TrieDB()
|
||||||
|
bc.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer bc.wg.Done()
|
||||||
|
triedb.SaveCachePeriodically(bc.cacheConfig.TrieCleanJournal, bc.cacheConfig.TrieCleanRejournal, bc.quit)
|
||||||
|
}()
|
||||||
|
}
|
||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,6 +934,12 @@ func (bc *BlockChain) Stop() {
|
|||||||
log.Error("Dangling trie nodes after full cleanup")
|
log.Error("Dangling trie nodes after full cleanup")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ensure all live cached entries be saved into disk, so that we can skip
|
||||||
|
// cache warmup when node restarts.
|
||||||
|
if bc.cacheConfig.TrieCleanJournal != "" {
|
||||||
|
triedb := bc.stateCache.TrieDB()
|
||||||
|
triedb.SaveCache(bc.cacheConfig.TrieCleanJournal)
|
||||||
|
}
|
||||||
log.Info("Blockchain stopped")
|
log.Info("Blockchain stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
|||||||
// We have the genesis block in database(perhaps in ancient database)
|
// We have the genesis block in database(perhaps in ancient database)
|
||||||
// but the corresponding state is missing.
|
// but the corresponding state is missing.
|
||||||
header := rawdb.ReadHeader(db, stored, 0)
|
header := rawdb.ReadHeader(db, stored, 0)
|
||||||
if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0), nil); err != nil {
|
if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0, ""), nil); err != nil {
|
||||||
if genesis == nil {
|
if genesis == nil {
|
||||||
genesis = DefaultGenesisBlock()
|
genesis = DefaultGenesisBlock()
|
||||||
}
|
}
|
||||||
|
@ -100,16 +100,16 @@ type Trie interface {
|
|||||||
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
||||||
// historical state in memory, use the NewDatabaseWithCache constructor.
|
// historical state in memory, use the NewDatabaseWithCache constructor.
|
||||||
func NewDatabase(db ethdb.Database) Database {
|
func NewDatabase(db ethdb.Database) Database {
|
||||||
return NewDatabaseWithCache(db, 0)
|
return NewDatabaseWithCache(db, 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabaseWithCache creates a backing store for state. The returned database
|
// NewDatabaseWithCache creates a backing store for state. The returned database
|
||||||
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
||||||
// large memory cache.
|
// large memory cache.
|
||||||
func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
|
func NewDatabaseWithCache(db ethdb.Database, cache int, journal string) Database {
|
||||||
csc, _ := lru.New(codeSizeCacheSize)
|
csc, _ := lru.New(codeSizeCacheSize)
|
||||||
return &cachingDB{
|
return &cachingDB{
|
||||||
db: trie.NewDatabaseWithCache(db, cache),
|
db: trie.NewDatabaseWithCache(db, cache, journal),
|
||||||
codeSizeCache: csc,
|
codeSizeCache: csc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
|||||||
|
|
||||||
// Ensure we have a valid starting state before doing any work
|
// Ensure we have a valid starting state before doing any work
|
||||||
origin := start.NumberU64()
|
origin := start.NumberU64()
|
||||||
database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16) // Chain tracing will probably start at genesis
|
database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16, "") // Chain tracing will probably start at genesis
|
||||||
|
|
||||||
if number := start.NumberU64(); number > 0 {
|
if number := start.NumberU64(); number > 0 {
|
||||||
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
||||||
@ -641,7 +641,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
|||||||
}
|
}
|
||||||
// Otherwise try to reexec blocks until we find a state or reach our limit
|
// Otherwise try to reexec blocks until we find a state or reach our limit
|
||||||
origin := block.NumberU64()
|
origin := block.NumberU64()
|
||||||
database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16)
|
database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16, "")
|
||||||
|
|
||||||
for i := uint64(0); i < reexec; i++ {
|
for i := uint64(0); i < reexec; i++ {
|
||||||
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
@ -183,6 +183,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||||||
}
|
}
|
||||||
cacheConfig = &core.CacheConfig{
|
cacheConfig = &core.CacheConfig{
|
||||||
TrieCleanLimit: config.TrieCleanCache,
|
TrieCleanLimit: config.TrieCleanCache,
|
||||||
|
TrieCleanJournal: ctx.ResolvePath(config.TrieCleanCacheJournal),
|
||||||
|
TrieCleanRejournal: config.TrieCleanCacheRejournal,
|
||||||
TrieCleanNoPrefetch: config.NoPrefetch,
|
TrieCleanNoPrefetch: config.NoPrefetch,
|
||||||
TrieDirtyLimit: config.TrieDirtyCache,
|
TrieDirtyLimit: config.TrieDirtyCache,
|
||||||
TrieDirtyDisabled: config.NoPruning,
|
TrieDirtyDisabled: config.NoPruning,
|
||||||
|
@ -62,6 +62,8 @@ var DefaultConfig = Config{
|
|||||||
UltraLightFraction: 75,
|
UltraLightFraction: 75,
|
||||||
DatabaseCache: 512,
|
DatabaseCache: 512,
|
||||||
TrieCleanCache: 256,
|
TrieCleanCache: 256,
|
||||||
|
TrieCleanCacheJournal: "triecache",
|
||||||
|
TrieCleanCacheRejournal: 60 * time.Minute,
|
||||||
TrieDirtyCache: 256,
|
TrieDirtyCache: 256,
|
||||||
TrieTimeout: 60 * time.Minute,
|
TrieTimeout: 60 * time.Minute,
|
||||||
SnapshotCache: 256,
|
SnapshotCache: 256,
|
||||||
@ -140,6 +142,8 @@ type Config struct {
|
|||||||
DatabaseFreezer string
|
DatabaseFreezer string
|
||||||
|
|
||||||
TrieCleanCache int
|
TrieCleanCache int
|
||||||
|
TrieCleanCacheJournal string `toml:",omitempty"` // Disk journal directory for trie cache to survive node restarts
|
||||||
|
TrieCleanCacheRejournal time.Duration `toml:",omitempty"` // Time interval to regenerate the journal for clean cache
|
||||||
TrieDirtyCache int
|
TrieDirtyCache int
|
||||||
TrieTimeout time.Duration
|
TrieTimeout time.Duration
|
||||||
SnapshotCache int
|
SnapshotCache int
|
||||||
|
@ -38,6 +38,8 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
DatabaseFreezer string
|
DatabaseFreezer string
|
||||||
TrieCleanCache int
|
TrieCleanCache int
|
||||||
|
TrieCleanCacheJournal string `toml:",omitempty"`
|
||||||
|
TrieCleanCacheRejournal time.Duration `toml:",omitempty"`
|
||||||
TrieDirtyCache int
|
TrieDirtyCache int
|
||||||
TrieTimeout time.Duration
|
TrieTimeout time.Duration
|
||||||
SnapshotCache int
|
SnapshotCache int
|
||||||
@ -76,6 +78,8 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
enc.DatabaseCache = c.DatabaseCache
|
enc.DatabaseCache = c.DatabaseCache
|
||||||
enc.DatabaseFreezer = c.DatabaseFreezer
|
enc.DatabaseFreezer = c.DatabaseFreezer
|
||||||
enc.TrieCleanCache = c.TrieCleanCache
|
enc.TrieCleanCache = c.TrieCleanCache
|
||||||
|
enc.TrieCleanCacheJournal = c.TrieCleanCacheJournal
|
||||||
|
enc.TrieCleanCacheRejournal = c.TrieCleanCacheRejournal
|
||||||
enc.TrieDirtyCache = c.TrieDirtyCache
|
enc.TrieDirtyCache = c.TrieDirtyCache
|
||||||
enc.TrieTimeout = c.TrieTimeout
|
enc.TrieTimeout = c.TrieTimeout
|
||||||
enc.SnapshotCache = c.SnapshotCache
|
enc.SnapshotCache = c.SnapshotCache
|
||||||
@ -118,6 +122,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
DatabaseCache *int
|
DatabaseCache *int
|
||||||
DatabaseFreezer *string
|
DatabaseFreezer *string
|
||||||
TrieCleanCache *int
|
TrieCleanCache *int
|
||||||
|
TrieCleanCacheJournal *string `toml:",omitempty"`
|
||||||
|
TrieCleanCacheRejournal *time.Duration `toml:",omitempty"`
|
||||||
TrieDirtyCache *int
|
TrieDirtyCache *int
|
||||||
TrieTimeout *time.Duration
|
TrieTimeout *time.Duration
|
||||||
SnapshotCache *int
|
SnapshotCache *int
|
||||||
@ -201,6 +207,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
if dec.TrieCleanCache != nil {
|
if dec.TrieCleanCache != nil {
|
||||||
c.TrieCleanCache = *dec.TrieCleanCache
|
c.TrieCleanCache = *dec.TrieCleanCache
|
||||||
}
|
}
|
||||||
|
if dec.TrieCleanCacheJournal != nil {
|
||||||
|
c.TrieCleanCacheJournal = *dec.TrieCleanCacheJournal
|
||||||
|
}
|
||||||
|
if dec.TrieCleanCacheRejournal != nil {
|
||||||
|
c.TrieCleanCacheRejournal = *dec.TrieCleanCacheRejournal
|
||||||
|
}
|
||||||
if dec.TrieDirtyCache != nil {
|
if dec.TrieDirtyCache != nil {
|
||||||
c.TrieDirtyCache = *dec.TrieDirtyCache
|
c.TrieDirtyCache = *dec.TrieDirtyCache
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64, dis
|
|||||||
diskdb: db,
|
diskdb: db,
|
||||||
odr: odr,
|
odr: odr,
|
||||||
trieTable: trieTable,
|
trieTable: trieTable,
|
||||||
triedb: trie.NewDatabaseWithCache(trieTable, 1), // Use a tiny cache only to keep memory down
|
triedb: trie.NewDatabaseWithCache(trieTable, 1, ""), // Use a tiny cache only to keep memory down
|
||||||
trieset: mapset.NewSet(),
|
trieset: mapset.NewSet(),
|
||||||
sectionSize: size,
|
sectionSize: size,
|
||||||
disablePruning: disablePruning,
|
disablePruning: disablePruning,
|
||||||
@ -340,7 +340,7 @@ func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uin
|
|||||||
diskdb: db,
|
diskdb: db,
|
||||||
odr: odr,
|
odr: odr,
|
||||||
trieTable: trieTable,
|
trieTable: trieTable,
|
||||||
triedb: trie.NewDatabaseWithCache(trieTable, 1), // Use a tiny cache only to keep memory down
|
triedb: trie.NewDatabaseWithCache(trieTable, 1, ""), // Use a tiny cache only to keep memory down
|
||||||
trieset: mapset.NewSet(),
|
trieset: mapset.NewSet(),
|
||||||
parentSize: parentSize,
|
parentSize: parentSize,
|
||||||
size: size,
|
size: size,
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -278,16 +279,20 @@ func expandNode(hash hashNode, n node) node {
|
|||||||
// its written out to disk or garbage collected. No read cache is created, so all
|
// its written out to disk or garbage collected. No read cache is created, so all
|
||||||
// data retrievals will hit the underlying disk database.
|
// data retrievals will hit the underlying disk database.
|
||||||
func NewDatabase(diskdb ethdb.KeyValueStore) *Database {
|
func NewDatabase(diskdb ethdb.KeyValueStore) *Database {
|
||||||
return NewDatabaseWithCache(diskdb, 0)
|
return NewDatabaseWithCache(diskdb, 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabaseWithCache creates a new trie database to store ephemeral trie content
|
// NewDatabaseWithCache creates a new trie database to store ephemeral trie content
|
||||||
// before its written out to disk or garbage collected. It also acts as a read cache
|
// before its written out to disk or garbage collected. It also acts as a read cache
|
||||||
// for nodes loaded from disk.
|
// for nodes loaded from disk.
|
||||||
func NewDatabaseWithCache(diskdb ethdb.KeyValueStore, cache int) *Database {
|
func NewDatabaseWithCache(diskdb ethdb.KeyValueStore, cache int, journal string) *Database {
|
||||||
var cleans *fastcache.Cache
|
var cleans *fastcache.Cache
|
||||||
if cache > 0 {
|
if cache > 0 {
|
||||||
|
if journal == "" {
|
||||||
cleans = fastcache.New(cache * 1024 * 1024)
|
cleans = fastcache.New(cache * 1024 * 1024)
|
||||||
|
} else {
|
||||||
|
cleans = fastcache.LoadFromFileOrNew(journal, cache*1024*1024)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &Database{
|
return &Database{
|
||||||
diskdb: diskdb,
|
diskdb: diskdb,
|
||||||
@ -867,3 +872,43 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
|
|||||||
var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2))
|
var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2))
|
||||||
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, db.preimagesSize
|
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, db.preimagesSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// saveCache saves clean state cache to given directory path
|
||||||
|
// using specified CPU cores.
|
||||||
|
func (db *Database) saveCache(dir string, threads int) error {
|
||||||
|
if db.cleans == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Info("Writing clean trie cache to disk", "path", dir, "threads", threads)
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
err := db.cleans.SaveToFileConcurrent(dir, threads)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to persist clean trie cache", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Persisted the clean trie cache", "path", dir, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveCache atomically saves fast cache data to the given dir using all
|
||||||
|
// available CPU cores.
|
||||||
|
func (db *Database) SaveCache(dir string) error {
|
||||||
|
return db.saveCache(dir, runtime.GOMAXPROCS(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveCachePeriodically atomically saves fast cache data to the given dir with
|
||||||
|
// the specified interval. All dump operation will only use a single CPU core.
|
||||||
|
func (db *Database) SaveCachePeriodically(dir string, interval time.Duration, stopCh <-chan struct{}) {
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
db.saveCache(dir, 1)
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user