Skip to content

Commit bf29d4e

Browse files
committed
feat: enhance Backup functionality to support datastore unwrapping and improve error handling
1 parent 05fe34a commit bf29d4e

2 files changed

Lines changed: 80 additions & 19 deletions

File tree

pkg/store/backup.go

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77

8+
ds "github.com/ipfs/go-datastore"
89
badger4 "github.com/ipfs/go-ds-badger4"
910
)
1011

@@ -16,28 +17,51 @@ func (s *DefaultStore) Backup(ctx context.Context, writer io.Writer, since uint6
1617
return 0, err
1718
}
1819

19-
// Try to leverage a native backup implementation if the underlying datastore exposes one.
20-
type backupable interface {
21-
Backup(io.Writer, uint64) (uint64, error)
22-
}
23-
if dsBackup, ok := s.db.(backupable); ok {
24-
version, err := dsBackup.Backup(writer, since)
25-
if err != nil {
26-
return 0, fmt.Errorf("datastore backup failed: %w", err)
27-
}
28-
return version, nil
29-
}
30-
31-
// Default Badger datastore used across ev-node.
32-
badgerDatastore, ok := s.db.(*badger4.Datastore)
20+
visited := make(map[ds.Datastore]struct{})
21+
current, ok := any(s.db).(ds.Datastore)
3322
if !ok {
3423
return 0, fmt.Errorf("backup is not supported by the configured datastore")
3524
}
3625

37-
// `badger.DB.Backup` internally orchestrates a consistent snapshot without pausing writes.
38-
version, err := badgerDatastore.DB.Backup(writer, since)
39-
if err != nil {
40-
return 0, fmt.Errorf("badger backup failed: %w", err)
26+
for {
27+
// Try to leverage a native backup implementation if the underlying datastore exposes one.
28+
type backupable interface {
29+
Backup(io.Writer, uint64) (uint64, error)
30+
}
31+
if dsBackup, ok := current.(backupable); ok {
32+
version, err := dsBackup.Backup(writer, since)
33+
if err != nil {
34+
return 0, fmt.Errorf("datastore backup failed: %w", err)
35+
}
36+
return version, nil
37+
}
38+
39+
// Default Badger datastore used across ev-node.
40+
if badgerDatastore, ok := current.(*badger4.Datastore); ok {
41+
// `badger.DB.Backup` internally orchestrates a consistent snapshot without pausing writes.
42+
version, err := badgerDatastore.DB.Backup(writer, since)
43+
if err != nil {
44+
return 0, fmt.Errorf("badger backup failed: %w", err)
45+
}
46+
return version, nil
47+
}
48+
49+
// Attempt to unwrap shimmed datastores (e.g., prefix or mutex wrappers) to reach the backing store.
50+
if _, seen := visited[current]; seen {
51+
break
52+
}
53+
visited[current] = struct{}{}
54+
55+
shim, ok := current.(ds.Shim)
56+
if !ok {
57+
break
58+
}
59+
children := shim.Children()
60+
if len(children) == 0 {
61+
break
62+
}
63+
current = children[0]
4164
}
42-
return version, nil
65+
66+
return 0, fmt.Errorf("backup is not supported by the configured datastore")
4367
}

scripts/reth-backup/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,40 @@ Additional flags:
3838

3939
The script outputs the height at the end so you can coordinate other backups
4040
with the same block number.
41+
42+
## End-to-end workflow with `apps/evm/single`
43+
44+
The `evm/single` docker-compose setup expects the backup image to reuse the
45+
`ghcr.io/evstack/ev-reth:latest` tag. To capture both the MDBX and ev-node
46+
Badger backups end-to-end:
47+
48+
1. Build the helper image so `ev-reth` has the MDBX tooling preinstalled.
49+
```bash
50+
docker build -t ghcr.io/evstack/ev-reth:latest scripts/reth-backup
51+
```
52+
2. Build the `ev-node-evm-single` image so it includes the latest CLI backup
53+
command (optional if you already pushed the binary and re-tagged it).
54+
```bash
55+
docker build -t ghcr.io/evstack/ev-node-evm-single:main -f apps/evm/single/Dockerfile .
56+
```
57+
3. Start the stack.
58+
```bash
59+
(cd apps/evm/single && docker compose up -d)
60+
```
61+
4. Run the MDBX snapshot script (adjust the destination as needed).
62+
```bash
63+
./scripts/reth-backup/backup.sh backups/full-run/reth
64+
```
65+
5. Stream a Badger backup from the ev-node into the container filesystem and
66+
copy it to the host.
67+
```bash
68+
docker compose exec ev-node-evm-single \
69+
evm-single backup --output /tmp/evnode-backup.badger --force
70+
docker cp ev-node-evm-single:/tmp/evnode-backup.badger backups/full-run/ev-node/
71+
```
72+
6. When finished, tear the stack down.
73+
```bash
74+
(cd apps/evm/single && docker compose down)
75+
```
76+
77+
Both backups can then be found under `backups/full-run/`.

0 commit comments

Comments
 (0)