Skip to content

Commit 1ae425b

Browse files
Create Live Snapshot without shutting down node
1 parent 91a7a31 commit 1ae425b

File tree

13 files changed

+120
-0
lines changed

13 files changed

+120
-0
lines changed

arbitrum/recordingdb.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ func newRecordingKV(inner *triedb.Database, diskDb ethdb.KeyValueStore) *Recordi
3939
return &RecordingKV{inner, diskDb, make(map[common.Hash][]byte), false}
4040
}
4141

42+
func (db *RecordingKV) CreateDBSnapshot(dir string) error {
43+
return errors.New("createDBSnapshot method is not supported")
44+
}
45+
4246
func (db *RecordingKV) Has(key []byte) (bool, error) {
4347
return false, errors.New("recording KV doesn't support Has")
4448
}

core/rawdb/database.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ func (frdb *freezerdb) Freeze() error {
8686
return nil
8787
}
8888

89+
func (db *freezerdb) CreateDBSnapshot(dir string) error {
90+
if err := db.KeyValueStore.CreateDBSnapshot(dir); err != nil {
91+
return err
92+
}
93+
if err := db.AncientStore.CreateDBSnapshot(dir); err != nil {
94+
return err
95+
}
96+
return nil
97+
}
98+
8999
// nofreezedb is a database wrapper that disables freezer data retrievals.
90100
type nofreezedb struct {
91101
ethdb.KeyValueStore

core/rawdb/freezer.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package rawdb
1919
import (
2020
"errors"
2121
"fmt"
22+
"io"
2223
"math"
2324
"os"
2425
"path/filepath"
@@ -73,6 +74,9 @@ type Freezer struct {
7374
tables map[string]*freezerTable // Data tables for storing everything
7475
instanceLock FileLock // File-system lock to prevent double opens
7576
closeOnce sync.Once
77+
78+
datadir string
79+
createSnapshotMutex sync.Mutex
7680
}
7781

7882
// NewFreezer creates a freezer instance for maintaining immutable ordered
@@ -115,6 +119,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
115119
readonly: readonly,
116120
tables: make(map[string]*freezerTable),
117121
instanceLock: lock,
122+
datadir: datadir,
118123
}
119124

120125
// Create the tables.
@@ -316,6 +321,8 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
316321

317322
// Sync flushes all data tables to disk.
318323
func (f *Freezer) Sync() error {
324+
f.createSnapshotMutex.Lock()
325+
defer f.createSnapshotMutex.Unlock()
319326
var errs []error
320327
for _, table := range f.tables {
321328
if err := table.Sync(); err != nil {
@@ -328,6 +335,63 @@ func (f *Freezer) Sync() error {
328335
return nil
329336
}
330337

338+
func (f *Freezer) CreateDBSnapshot(dir string) error {
339+
f.createSnapshotMutex.Lock()
340+
defer f.createSnapshotMutex.Unlock()
341+
snapshotDir := filepath.Join(dir, "l2chaindata", "ancient", "chain") // Format currently used
342+
if err := os.MkdirAll(snapshotDir, os.ModePerm); err != nil {
343+
return fmt.Errorf("failed to create snapshot of ancient directory: %w", err)
344+
}
345+
// Manually copy contents of ancient to the snapshotDir, createSnapshotMutex makes sure ancient is not updated while copying contents
346+
err := filepath.Walk(f.datadir, func(path string, info os.FileInfo, err error) error {
347+
if err != nil {
348+
return fmt.Errorf("error accessing path %s: %w", path, err)
349+
}
350+
relPath, err := filepath.Rel(f.datadir, path)
351+
if err != nil {
352+
return fmt.Errorf("error calculating relative path: %w", err)
353+
}
354+
destPath := filepath.Join(snapshotDir, relPath)
355+
if info.IsDir() {
356+
if err := os.MkdirAll(destPath, info.Mode()); err != nil {
357+
return fmt.Errorf("failed to create directory %s: %w", destPath, err)
358+
}
359+
} else {
360+
if err := copyFile(path, destPath); err != nil {
361+
return fmt.Errorf("failed to copy file %s: %w", path, err)
362+
}
363+
}
364+
return nil
365+
})
366+
return err
367+
}
368+
369+
func copyFile(src, dest string) error {
370+
srcFile, err := os.Open(src)
371+
if err != nil {
372+
return fmt.Errorf("failed to open source file: %w", err)
373+
}
374+
defer srcFile.Close()
375+
destFile, err := os.Create(dest)
376+
if err != nil {
377+
return fmt.Errorf("failed to create destination file: %w", err)
378+
}
379+
defer destFile.Close()
380+
_, err = io.Copy(destFile, srcFile)
381+
if err != nil {
382+
return fmt.Errorf("failed to copy contents: %w", err)
383+
}
384+
srcInfo, err := os.Stat(src)
385+
if err != nil {
386+
return fmt.Errorf("failed to stat source file: %w", err)
387+
}
388+
err = os.Chmod(dest, srcInfo.Mode())
389+
if err != nil {
390+
return fmt.Errorf("failed to set permissions: %w", err)
391+
}
392+
return nil
393+
}
394+
331395
// validate checks that every table has the same boundary.
332396
// Used instead of `repair` in readonly mode.
333397
func (f *Freezer) validate() error {

core/rawdb/freezer_memory.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ func NewMemoryFreezer(readonly bool, tableName map[string]bool) *MemoryFreezer {
230230
}
231231
}
232232

233+
func (f *MemoryFreezer) CreateDBSnapshot(dir string) error {
234+
return errors.New("createDBSnapshot method is not supported by MemoryFreezer")
235+
}
236+
233237
// HasAncient returns an indicator whether the specified data exists.
234238
func (f *MemoryFreezer) HasAncient(kind string, number uint64) (bool, error) {
235239
f.lock.RLock()

core/rawdb/freezer_resettable.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package rawdb
1818

1919
import (
20+
"errors"
2021
"os"
2122
"path/filepath"
2223
"sync"
@@ -92,6 +93,10 @@ func (f *resettableFreezer) Reset() error {
9293
return nil
9394
}
9495

96+
func (f *resettableFreezer) CreateDBSnapshot(dir string) error {
97+
return errors.New("createDBSnapshot method is not supported by resettableFreezer")
98+
}
99+
95100
// Close terminates the chain freezer, unmapping all the data files.
96101
func (f *resettableFreezer) Close() error {
97102
f.lock.RLock()

core/rawdb/table.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package rawdb
1818

1919
import (
20+
"errors"
21+
2022
"github.com/ethereum/go-ethereum/ethdb"
2123
)
2224

@@ -35,6 +37,10 @@ func NewTable(db ethdb.Database, prefix string) ethdb.Database {
3537
}
3638
}
3739

40+
func (db *table) CreateDBSnapshot(dir string) error {
41+
return errors.New("createDBSnapshot method is not supported")
42+
}
43+
3844
// Close is a noop to implement the Database interface.
3945
func (t *table) Close() error {
4046
return nil

ethdb/database.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ type AncientStore interface {
180180
AncientReader
181181
AncientWriter
182182
io.Closer
183+
CreateDBSnapshot(dir string) error
183184
}
184185

185186
type WasmTarget string

ethdb/leveldb/leveldb.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ func configureOptions(customizeFn func(*opt.Options)) *opt.Options {
166166
return options
167167
}
168168

169+
func (db *Database) CreateDBSnapshot(dir string) error {
170+
return errors.New("createDBSnapshot method is not supported by leveldb")
171+
}
172+
169173
// Close stops the metrics collection, flushes any pending data to disk and closes
170174
// all io accesses to the underlying key-value store.
171175
func (db *Database) Close() error {

ethdb/memorydb/memorydb.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ func NewWithCap(size int) *Database {
6565
}
6666
}
6767

68+
func (db *Database) CreateDBSnapshot(dir string) error {
69+
return errors.New("createDBSnapshot method is not supported by memorydb")
70+
}
71+
6872
// Close deallocates the internal map and ensures any consecutive data access op
6973
// fails with an error.
7074
func (db *Database) Close() error {

ethdb/pebble/pebble.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package pebble
2222
import (
2323
"bytes"
2424
"fmt"
25+
"path/filepath"
2526
"runtime"
2627
"sync"
2728
"sync/atomic"
@@ -116,6 +117,11 @@ type Database struct {
116117
writeOptions *pebble.WriteOptions
117118
}
118119

120+
func (d *Database) CreateDBSnapshot(dir string) error {
121+
snapshotDir := filepath.Join(dir, filepath.Base(d.Path()))
122+
return d.db.Checkpoint(snapshotDir, pebble.WithFlushedWAL())
123+
}
124+
119125
func (d *Database) onCompactionBegin(info pebble.CompactionInfo) {
120126
if d.activeComp == 0 {
121127
d.compStartTime = time.Now()

0 commit comments

Comments
 (0)