Skip to content

Commit 39d4b4d

Browse files
kvstorage/wag: prepare for WAG truncation
This commit sets up the premitives for WAG truncations by: 1) Adding a `Delete` function for removing WAG nodes by index. 2) Changing the WAG `Iterator.Iter` method to return `iter.Seq2[uint64, wagpb.Node]` pair. This will be useful when we iterate over the WAG and truncate the nodes that have been applied and synced. Epic: none Release note: None Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
1 parent 08a34f8 commit 39d4b4d

3 files changed

Lines changed: 75 additions & 15 deletions

File tree

pkg/kv/kvserver/kvstorage/wag/store.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,18 @@ func Write(w storage.Writer, index uint64, node wagpb.Node) error {
7676
return w.PutUnversioned(keys.StoreWAGNodeKey(index), data)
7777
}
7878

79+
// Delete removes the WAG node at the given sequence number.
80+
// TODO(ibrahim): Consider SingleClearEngineKey if we can guarantee that the
81+
// index is written only once (even after restarts).
82+
func Delete(w storage.Writer, index uint64) error {
83+
return w.ClearUnversioned(keys.StoreWAGNodeKey(index), storage.ClearOptions{})
84+
}
85+
7986
// Iterator helps to scan the WAG sequence.
8087
//
8188
// var iter wag.Iterator
82-
// for node := range iter.Iter(ctx, reader) {
83-
// // process node
89+
// for index, node := range iter.Iter(ctx, reader) {
90+
// // process index, node
8491
// }
8592
// if err := iter.Error(); err != nil {
8693
// return err
@@ -92,8 +99,9 @@ type Iterator struct {
9299
err error
93100
}
94101

95-
// Iter returns an iterator that scans the WAG sequence.
96-
func (it *Iterator) Iter(ctx context.Context, r storage.Reader) iter.Seq[wagpb.Node] {
102+
// Iter returns an iterator that scans the WAG sequence. The iterator yields a
103+
// pair containing the WAG node index and the WAG node itself.
104+
func (it *Iterator) Iter(ctx context.Context, r storage.Reader) iter.Seq2[uint64, wagpb.Node] {
97105
prefix := keys.StoreWAGPrefix()
98106
mi, err := r.NewMVCCIterator(ctx, storage.MVCCKeyIterKind, storage.IterOptions{
99107
UpperBound: prefix.PrefixEnd(),
@@ -104,13 +112,18 @@ func (it *Iterator) Iter(ctx context.Context, r storage.Reader) iter.Seq[wagpb.N
104112
}
105113
mi.SeekGE(storage.MakeMVCCMetadataKey(prefix))
106114

107-
return func(yield func(wagpb.Node) bool) {
115+
return func(yield func(uint64, wagpb.Node) bool) {
108116
defer mi.Close()
109117
for ; ; mi.Next() {
110118
if ok, err := mi.Valid(); err != nil || !ok {
111119
it.err = err
112120
return
113121
}
122+
index, err := keys.DecodeWAGNodeKey(mi.UnsafeKey().Key)
123+
if err != nil {
124+
it.err = err
125+
return
126+
}
114127
v, err := mi.UnsafeValue()
115128
if err != nil {
116129
it.err = err
@@ -120,7 +133,7 @@ func (it *Iterator) Iter(ctx context.Context, r storage.Reader) iter.Seq[wagpb.N
120133
if it.err = node.Unmarshal(v); it.err != nil { // nolint:protounmarshal
121134
return
122135
}
123-
if !yield(node) {
136+
if !yield(index, node) {
124137
return
125138
}
126139
}

pkg/kv/kvserver/kvstorage/wag/store_test.go

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func TestWrite(t *testing.T) {
4747
id := roachpb.FullReplicaID{RangeID: 123, ReplicaID: 4}
4848
rhsID := roachpb.FullReplicaID{RangeID: 567, ReplicaID: 1}
4949
write("create", func(w storage.Writer) error { return createReplica(&s, w, id) })
50+
// Intentionally introduce a gap in the sequence. We will later make sure that
51+
// the iterator correctly skips over this gap.
52+
s.seq.Next(1)
5053
write("init", func(w storage.Writer) error { return initReplica(&s, w, id, 10) })
5154
write("split", func(w storage.Writer) error { return splitReplica(&s, w, id, rhsID, 200) })
5255

@@ -55,16 +58,60 @@ func TestWrite(t *testing.T) {
5558
out = strings.ReplaceAll(out, "\n\n", "\n")
5659
echotest.Require(t, out, filepath.Join("testdata", t.Name()+".txt"))
5760

58-
// Smoke check that the iterator works.
59-
var iter Iterator
60-
count := 0
61-
for range iter.Iter(context.Background(), s.eng) {
62-
count++
61+
// readIndices returns the WAG node indices by scanning the engine.
62+
readIndices := func() []uint64 {
63+
var it Iterator
64+
var res []uint64
65+
for index := range it.Iter(context.Background(), s.eng) {
66+
res = append(res, index)
67+
}
68+
require.NoError(t, it.Error())
69+
return res
6370
}
64-
require.NoError(t, iter.Error())
71+
72+
// Verify that the iterator returns nodes with the correct indices.
6573
// 3 WAG nodes: create, init, split. The split is a single node with two
6674
// events (Split + Init) rather than two separate nodes (dep + event).
67-
require.Equal(t, 3, count)
75+
require.Equal(t, []uint64{1, 3, 4}, readIndices())
76+
}
77+
78+
func TestDelete(t *testing.T) {
79+
defer leaktest.AfterTest(t)()
80+
defer log.Scope(t).Close(t)
81+
82+
eng := storage.NewDefaultInMemForTesting()
83+
defer eng.Close()
84+
85+
id := roachpb.FullReplicaID{RangeID: 1, ReplicaID: 1}
86+
node := wagpb.Node{
87+
Events: []wagpb.Event{
88+
{Addr: wagpb.MakeAddr(id, 10), Type: wagpb.EventApply},
89+
},
90+
}
91+
92+
// Write 5 WAG nodes with indices 1 through 5.
93+
for i := uint64(1); i <= 5; i++ {
94+
require.NoError(t, Write(eng, i, node))
95+
}
96+
97+
// Read back all indices.
98+
readIndices := func() []uint64 {
99+
var it Iterator
100+
var res []uint64
101+
for index := range it.Iter(context.Background(), eng) {
102+
res = append(res, index)
103+
}
104+
require.NoError(t, it.Error())
105+
return res
106+
}
107+
require.Equal(t, []uint64{1, 2, 3, 4, 5}, readIndices())
108+
109+
// Delete nodes 2 and 4.
110+
require.NoError(t, Delete(eng, 2))
111+
require.NoError(t, Delete(eng, 4))
112+
113+
// Verify that only nodes 1, 3, 5 remain.
114+
require.Equal(t, []uint64{1, 3, 5}, readIndices())
68115
}
69116

70117
type store struct {

pkg/kv/kvserver/kvstorage/wag/testdata/TestWrite.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ echo
44
Put: 0,0 /Local/Store/wag/1 (0x01737761676e000000000000000100): (r123/4:0,EventCreate)
55
> Put: 0,0 "state-machine-key" (0x73746174652d6d616368696e652d6b657900): "state"
66
>> init
7-
Put: 0,0 /Local/Store/wag/2 (0x01737761676e000000000000000200): (r123/4:10,EventInit)
7+
Put: 0,0 /Local/Store/wag/3 (0x01737761676e000000000000000300): (r123/4:10,EventInit)
88
ingestion: SSTs:"tmp/1.sst" SSTs:"tmp/2.sst"
99
>> split
10-
Put: 0,0 /Local/Store/wag/3 (0x01737761676e000000000000000300): (r567/1:10,EventInit) (r123/4:200,EventSplit)
10+
Put: 0,0 /Local/Store/wag/4 (0x01737761676e000000000000000400): (r567/1:10,EventInit) (r123/4:200,EventSplit)
1111
> Put: 0,0 "lhs-key" (0x6c68732d6b657900): "lhs-state"
1212
> Put: 0,0 "rhs-key" (0x7268732d6b657900): "rhs-state"

0 commit comments

Comments
 (0)