mirror of
https://github.com/luxfi/geth.git
synced 2026-10-30 15:05:39 +00:00
Implement ethdb.ResettableAncientStore over a single KV backend (the
canonical luxfi/zapdb). Replaces the upstream geth freezer
(.cdat/.ridx files per table) with one ZAP database per chain. Snappy
compression per-block (~10x cold data); single-byte kind markers keep
prefix scans efficient.
Key layout (single ZAP database per chain):
['a'][kind byte][big-endian uint64 number] -> snappy value
['m']['h'][kind byte] -> head pointer
['m']['t'][kind byte] -> tail pointer
ZAP is canonical Lux storage (luxfi/zap + luxfi/zapdb). PebbleDB stays
only as legacy in-place upgrade. New deployments use ZAP everywhere.
Surface:
- core/rawdb/zap_ancient.go (ZapAncientStore implementation)
- core/rawdb/zap_ancient_migration.go (exported wrappers for migrate-ancient)
- core/rawdb/zap_ancient_rlp.go (rlp.EncodeToBytes split out)
- core/rawdb/zap_ancient_test.go (7 tests: roundtrip, out-of-order,
range maxBytes, truncate head/tail,
snappy, persist-across-open)
- core/rawdb/zap_ancient_bench_test.go (write + read throughput)
- cmd/migrate-ancient/ (one-shot upstream-freezer → ZAP migrator)
- cmd/fleet-bench/ (CSV harness for write/read latency)
Benchmarks (in-memory backend, M1 Pro):
WriteThroughput: 6860 ns/op, 4 puts per block (header+hash+body+receipt)
ReadHot: 920 ns/op
Migration command for an existing chain:
migrate-ancient --src /data/db/<net>/chainData/<chainID>/ancient \
--dst /data/ancient/<chainID>
The luxfi/operator NodeFleet CRD ships archive pods with
--ancient-store-backend=zap as the default.
Tests: 7 zap-ancient tests + full core/rawdb suite race-clean.
51 lines
2.0 KiB
Go
51 lines
2.0 KiB
Go
// Copyright (C) 2026, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
//
|
|
// Migration helpers — exported wrappers around the internal
|
|
// freezerTableConfig + NewFreezer / NewZapAncientStore plumbing so the
|
|
// `cmd/migrate-ancient` tool can drive both stores without re-importing
|
|
// the unexported config struct.
|
|
//
|
|
// We deliberately keep these wrappers minimal: only the table
|
|
// configuration and the open-for-migration entry points are exposed.
|
|
// The full ResettableAncientStore surface stays on the existing
|
|
// `ethdb.AncientStore` interface.
|
|
package rawdb
|
|
|
|
import (
|
|
"github.com/luxfi/geth/ethdb"
|
|
)
|
|
|
|
// FreezerTableConfig is the exported mirror of the unexported
|
|
// freezerTableConfig. Only the migration tool consumes this — the
|
|
// production path keeps the unexported struct.
|
|
type FreezerTableConfig struct {
|
|
NoSnappy bool
|
|
Prunable bool
|
|
}
|
|
|
|
// internalTables converts the exported config into the unexported
|
|
// shape the freezer + ZAP store consume.
|
|
func internalTables(in map[string]FreezerTableConfig) map[string]freezerTableConfig {
|
|
out := make(map[string]freezerTableConfig, len(in))
|
|
for k, v := range in {
|
|
out[k] = freezerTableConfig{noSnappy: v.NoSnappy, prunable: v.Prunable}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// OpenFreezerForMigration opens the upstream-format ancient store in
|
|
// read-only mode. Used by `cmd/migrate-ancient` as the source of a
|
|
// migration.
|
|
func OpenFreezerForMigration(datadir string, tables map[string]FreezerTableConfig) (ethdb.AncientStore, error) {
|
|
return NewFreezer(datadir, "migrate", true, freezerTableSize, internalTables(tables))
|
|
}
|
|
|
|
// NewZapAncientStoreForMigration opens a fresh ZAP-native ancient
|
|
// store at the given datadir. The caller supplies the underlying KV
|
|
// store — typically `ethdb/zapdb.New(datadir, ...)` in production;
|
|
// `memorydb.New()` in tests.
|
|
func NewZapAncientStoreForMigration(datadir string, tables map[string]FreezerTableConfig, store zapAncientBackend) (*ZapAncientStore, error) {
|
|
return NewZapAncientStore(datadir, internalTables(tables), store)
|
|
}
|