Skip to content

Commit 5bfda36

Browse files
committed
raftengine: add legacy boltdb and nil-safety tests, document offline migration requirement
Address @claude review (Test Coverage + Data Loss): - Add TestRejectLegacyBoltDB: verify factory rejects dirs with legacy files - Add TestEngineCloseNilSafe: verify nil engine/raft Close does not panic - Document that MigrateFSMStore requires the source engine to be stopped
1 parent f21a00f commit 5bfda36

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

internal/raftengine/hashicorp/engine_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package hashicorp_test
22

33
import (
4+
"os"
5+
"path/filepath"
46
"testing"
57
"time"
68

9+
"github.com/bootjp/elastickv/internal/raftengine"
710
hashicorpraftengine "github.com/bootjp/elastickv/internal/raftengine/hashicorp"
811
"github.com/bootjp/elastickv/internal/raftengine/raftenginetest"
12+
"github.com/stretchr/testify/require"
913
)
1014

1115
func TestConformance(t *testing.T) {
@@ -18,3 +22,46 @@ func TestConformance(t *testing.T) {
1822
})
1923
raftenginetest.RunConformanceSuite(t, factory)
2024
}
25+
26+
func TestEngineCloseNilSafe(t *testing.T) {
27+
t.Parallel()
28+
29+
t.Run("nil_engine", func(t *testing.T) {
30+
var e *hashicorpraftengine.Engine
31+
require.NoError(t, e.Close())
32+
})
33+
34+
t.Run("nil_raft_via_New", func(t *testing.T) {
35+
e := hashicorpraftengine.New(nil)
36+
require.Nil(t, e)
37+
})
38+
}
39+
40+
func TestRejectLegacyBoltDB(t *testing.T) {
41+
t.Parallel()
42+
43+
factory := hashicorpraftengine.NewFactory(hashicorpraftengine.FactoryConfig{
44+
CommitTimeout: 50 * time.Millisecond,
45+
HeartbeatTimeout: 200 * time.Millisecond,
46+
ElectionTimeout: 2000 * time.Millisecond,
47+
LeaderLeaseTimeout: 100 * time.Millisecond,
48+
SnapshotRetainCount: 3,
49+
})
50+
51+
for _, legacy := range []string{"logs.dat", "stable.dat"} {
52+
t.Run(legacy, func(t *testing.T) {
53+
dir := t.TempDir()
54+
require.NoError(t, os.WriteFile(filepath.Join(dir, legacy), []byte("legacy"), 0o600))
55+
56+
_, err := factory.Create(raftengine.FactoryConfig{
57+
LocalID: "n1",
58+
LocalAddress: "127.0.0.1:0",
59+
DataDir: dir,
60+
Bootstrap: true,
61+
StateMachine: &raftenginetest.TestStateMachine{},
62+
})
63+
require.Error(t, err)
64+
require.Contains(t, err.Error(), "legacy boltdb")
65+
})
66+
}
67+
}

internal/raftengine/hashicorp/migrate.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ func ParsePeers(raw string) ([]MigrationPeer, error) {
6868
//
6969
// The source FSM store (fsm.db) is read-only and shared between both engines;
7070
// this function only creates the hashicorp-specific artifacts.
71+
//
72+
// IMPORTANT: The source engine must be fully stopped before running this
73+
// tool. Running it against a live engine may produce an inconsistent
74+
// snapshot that is missing recently applied entries.
7175
func MigrateFSMStore(storePath string, destDataDir string, peers []MigrationPeer) (*MigrationStats, error) {
7276
destDataDir, tempDir, err := prepareMigrationDest(storePath, destDataDir, peers)
7377
if err != nil {

0 commit comments

Comments
 (0)