Files
zeekayandHanzo Dev 293d0338c8 database: optional Sequencer / PrefixDropper, so a consumer can leave the backend swappable
A consumer that needs crash-safe ids or a whole-range delete currently has to
import github.com/luxfi/zapdb directly and hold a *zapdb.DB — which pins it to
one backend and puts a second embedded engine in reach of the same directory.
hanzoai/git's queue is exactly that case, and having two engines able to open
one directory is what took git.hanzo.ai down: each engine's recovery deletes
the files the other's manifest does not list.

Database stays the floor — the set every backend can honour, so memdb, leveldb,
pebbledb and the wrappers are untouched. The extras are their own small
interfaces, reached by assertion:

	if s, ok := db.(database.Sequencer); ok { ... }

Widening Database instead would force every backend to grow a method it cannot
honour and move the failure to runtime. This way the consumer asks for exactly
what it needs at compile time, and a backend that cannot provide it simply does
not assert.

Sequence is the crash-safe alternative to an in-memory counter: a counter
rebuilt from "highest key seen" can re-issue an id that is already on disk
after an unclean shutdown and silently overwrite the record under it. The test
asserts that property directly — ids handed out after a reopen are strictly
above every id handed out before it, without re-reading the data.

zapdb implements both by delegating to the native handle it already holds, with
the same closeMu/ErrClosed guard as every other method, and carries
compile-time assertions so dropping one breaks the build here rather than a
consumer's type assertion.

⚠ Note for whoever runs the suite: `go test -tags test ./zapdb/` does not build
on main, and has not for reasons unrelated to this change — gpu_bench_test.go
and gpu_cache_test.go reference NewWithGPU, GPUCacheStats and
InvalidateGPUCache, none of which exist anywhere in the repo. These tests were
verified with those two files moved aside.

Hanzo Dev <[email protected]>

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-31 16:43:52 -07:00

53 lines
1.3 KiB
Go

// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zapdb
import (
"github.com/luxfi/database"
)
// Compile-time proof that this backend carries the optional capabilities, so
// dropping one is a build failure here rather than a failed type assertion in
// a consumer.
var (
_ database.Sequencer = (*Database)(nil)
_ database.PrefixDropper = (*Database)(nil)
)
// GetSequence implements database.Sequencer.
//
// The returned *zapdb.Sequence already has Next() (uint64, error) and
// Release() error, so it satisfies database.Sequence as-is.
func (d *Database) GetSequence(key []byte, bandwidth uint64) (database.Sequence, error) {
d.closeMu.RLock()
defer d.closeMu.RUnlock()
if d.closed {
return nil, database.ErrClosed
}
// Empty keys go through the same placeholder as every other operation, or a
// sequence stored under "" would collide with the empty-key record.
if len(key) == 0 {
key = emptyKeyPlaceholder
}
seq, err := d.db.GetSequence(key, bandwidth)
if err != nil {
return nil, err
}
return seq, nil
}
// DropPrefix implements database.PrefixDropper.
func (d *Database) DropPrefix(prefixes ...[]byte) error {
d.closeMu.RLock()
defer d.closeMu.RUnlock()
if d.closed {
return database.ErrClosed
}
return d.db.DropPrefix(prefixes...)
}