mirror of
https://github.com/hanzoai/replicate.git
synced 2026-10-30 18:23:20 +00:00
Base embeds replicate into commerce's CGO=1 (SQLCipher) binary. replicate
unconditionally imported modernc.org/sqlite (driver registration + FileControl +
modernc-syntax DSN), so under cgo hanzoai/sqlite registered mattn as "sqlite"
AND modernc.init registered "sqlite" again -> panic: sql: Register called twice
for driver sqlite. Fix: import ONLY github.com/hanzoai/sqlite (the canonical
dual-backend driver; mattn under cgo, modernc otherwise); never modernc directly.
- replicate.go: _ modernc -> _ hanzoai/sqlite (sole driver registration).
- db.go setPersistWAL: driverConn.(sqlite.FileControl)+FileControlPersistWAL ->
sqlite.SetPersistWAL(driverConn, true) (backend-neutral PERSIST_WAL bridge).
- db.go init: modernc-syntax DSN + sql.Open -> sqlite.OpenPragma("file:"+path,
{busy_timeout, wal_autocheckpoint=0}). mattn SILENTLY DROPS _wal_autocheckpoint
from a DSN; OpenPragma applies it per pooled connection via PRAGMA, so
checkpoint control holds under BOTH backends (no lost WAL frames under cgo).
- cmd/replicate/main.go + 4 root _test.go files: _ modernc -> _ hanzoai/sqlite.
- go.mod: require hanzoai/sqlite v0.2.3; modernc now // indirect.
Verified: CGO=0 build+tests green (incl -race); CGO=1 build+tests green, the
Register-twice panic is GONE, WAL/replication/checkpoint/restore round-trip
(incl encrypted e2e) all pass. Pre-existing (fail identically on origin/main):
7 *Metrics tests (replicate_db_size=0). CGO=1 -race
TestDB_CheckpointPageGapWithConcurrentWrites hangs — a mattn+race artifact of the
test's second busy_timeout=0 pool (persists with a plain sql.Open, so NOT this
change); never ran under mattn before (main panicked at init).
164 lines
21 KiB
Go
164 lines
21 KiB
Go
package replicate_test
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hanzoai/ltx"
|
|
_ "github.com/hanzoai/sqlite"
|
|
|
|
"github.com/hanzoai/replicate"
|
|
)
|
|
|
|
func TestChecksum(t *testing.T) {
|
|
// Ensure a WAL header, frame header, & frame data can be checksummed in one pass.
|
|
t.Run("OnePass", func(t *testing.T) {
|
|
input, err := hex.DecodeString("377f0682002de218000010000000000052382eac857b1a4e00000002000000020d000000080fe0000ffc0ff80ff40ff00fec0fe80fe40fe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000208020902070209020602090205020902040209020302090202020902010209")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s0, s1 := replicate.Checksum(binary.LittleEndian, 0, 0, input)
|
|
if got, want := [2]uint32{s0, s1}, [2]uint32{0xdc2f3e84, 0x540488d3}; got != want {
|
|
t.Fatalf("Checksum()=%x, want %x", got, want)
|
|
}
|
|
})
|
|
|
|
// Ensure we get the same result as OnePass even if we split up into multiple calls.
|
|
t.Run("Incremental", func(t *testing.T) {
|
|
// Compute checksum for beginning of WAL header.
|
|
s0, s1 := replicate.Checksum(binary.LittleEndian, 0, 0, MustDecodeHexString("377f0682002de218000010000000000052382eac857b1a4e"))
|
|
if got, want := [2]uint32{s0, s1}, [2]uint32{0x81153b65, 0x87178e8f}; got != want {
|
|
t.Fatalf("Checksum()=%x, want %x", got, want)
|
|
}
|
|
|
|
// Continue checksum with WAL frame header & frame contents.
|
|
s0a, s1a := replicate.Checksum(binary.LittleEndian, s0, s1, MustDecodeHexString("0000000200000002"))
|
|
s0b, s1b := replicate.Checksum(binary.LittleEndian, s0a, s1a, MustDecodeHexString(`0d000000080fe0000ffc0ff80ff40ff00fec0fe80fe40fe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000208020902070209020602090205020902040209020302090202020902010209`))
|
|
if got, want := [2]uint32{s0b, s1b}, [2]uint32{0xdc2f3e84, 0x540488d3}; got != want {
|
|
t.Fatalf("Checksum()=%x, want %x", got, want)
|
|
}
|
|
})
|
|
}
|
|
func TestLTXDir(t *testing.T) {
|
|
if got, want := replicate.LTXDir("foo"), "foo/ltx"; got != want {
|
|
t.Fatalf("LTXDir()=%v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLTXLevelDir(t *testing.T) {
|
|
if got, want := replicate.LTXLevelDir("foo", 0), "foo/ltx/0"; got != want {
|
|
t.Fatalf("LTXLevelDir()=%v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func LTXFilePath(t *testing.T) {
|
|
t.Helper()
|
|
if got, want := replicate.LTXFilePath("foo", 0, ltx.TXID(100), ltx.TXID(200)), "-"; got != want {
|
|
t.Fatalf("LTXPath()=%v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func MustDecodeHexString(s string) []byte {
|
|
b, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TestNewLTXError(t *testing.T) {
|
|
t.Run("MissingFile", func(t *testing.T) {
|
|
err := replicate.NewLTXError("open", "/path/to/file.ltx", 0, 1, 1, os.ErrNotExist)
|
|
if err.Hint == "" {
|
|
t.Fatal("expected hint for missing file error")
|
|
}
|
|
if !strings.Contains(err.Hint, "missing") {
|
|
t.Errorf("hint should mention missing file, got: %s", err.Hint)
|
|
}
|
|
if !strings.Contains(err.Hint, "replicate reset") {
|
|
t.Errorf("hint should mention reset command, got: %s", err.Hint)
|
|
}
|
|
})
|
|
|
|
t.Run("CorruptedFile", func(t *testing.T) {
|
|
err := replicate.NewLTXError("decode", "/path/to/file.ltx", 0, 1, 1, replicate.ErrLTXCorrupted)
|
|
if err.Hint == "" {
|
|
t.Fatal("expected hint for corrupted file error")
|
|
}
|
|
if !strings.Contains(err.Hint, "corrupted") {
|
|
t.Errorf("hint should mention corruption, got: %s", err.Hint)
|
|
}
|
|
})
|
|
|
|
t.Run("ChecksumMismatch", func(t *testing.T) {
|
|
err := replicate.NewLTXError("validate", "/path/to/file.ltx", 0, 1, 1, replicate.ErrChecksumMismatch)
|
|
if err.Hint == "" {
|
|
t.Fatal("expected hint for checksum mismatch error")
|
|
}
|
|
})
|
|
|
|
t.Run("ErrorString", func(t *testing.T) {
|
|
err := replicate.NewLTXError("open", "/path/to/file.ltx", 0, 1, 1, os.ErrNotExist)
|
|
errStr := err.Error()
|
|
if !strings.Contains(errStr, "open") {
|
|
t.Errorf("error should contain operation, got: %s", errStr)
|
|
}
|
|
if !strings.Contains(errStr, "/path/to/file.ltx") {
|
|
t.Errorf("error should contain path, got: %s", errStr)
|
|
}
|
|
})
|
|
|
|
t.Run("Unwrap", func(t *testing.T) {
|
|
underlying := errors.New("underlying error")
|
|
err := replicate.NewLTXError("read", "/path/to/file.ltx", 0, 1, 1, underlying)
|
|
if !errors.Is(err, underlying) {
|
|
t.Error("LTXError should unwrap to underlying error")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLTXErrorHints(t *testing.T) {
|
|
// Test that ErrLTXMissing also triggers appropriate hints
|
|
t.Run("ErrLTXMissing", func(t *testing.T) {
|
|
err := replicate.NewLTXError("open", "/path/to/file.ltx", 0, 1, 1, replicate.ErrLTXMissing)
|
|
if err.Hint == "" {
|
|
t.Fatal("expected hint for ErrLTXMissing")
|
|
}
|
|
if !strings.Contains(err.Hint, "replicate reset") {
|
|
t.Errorf("hint should mention reset command, got: %s", err.Hint)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLTXError_IsAutoRecoverable(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err error
|
|
recoverable bool
|
|
}{
|
|
{"NotExist", os.ErrNotExist, true},
|
|
{"LTXMissing", replicate.ErrLTXMissing, true},
|
|
{"LTXCorrupted", replicate.ErrLTXCorrupted, true},
|
|
{"ChecksumMismatch", replicate.ErrChecksumMismatch, true},
|
|
{"WrappedCorrupted", fmt.Errorf("%w: bad data", replicate.ErrLTXCorrupted), true},
|
|
{"PermissionDenied", os.ErrPermission, false},
|
|
{"GenericError", errors.New("something went wrong"), false},
|
|
{"IOError", fmt.Errorf("disk failure: %w", errors.New("EIO")), false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ltxErr := replicate.NewLTXError("open", "/path/to/file.ltx", 0, 1, 1, tt.err)
|
|
if got := ltxErr.IsAutoRecoverable(); got != tt.recoverable {
|
|
t.Fatalf("IsAutoRecoverable()=%v, want %v (err=%v)", got, tt.recoverable, tt.err)
|
|
}
|
|
})
|
|
}
|
|
}
|