Skip to content

Commit 0e2d62c

Browse files
committed
test: sweep purge chunk size the way a node runs it
1 parent 5299946 commit 0e2d62c

1 file changed

Lines changed: 51 additions & 33 deletions

File tree

internal/db/collection_purge_bench_test.go

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"testing"
1818

1919
badgerds "github.com/dgraph-io/badger/v4"
20+
badgeropts "github.com/dgraph-io/badger/v4/options"
2021
"github.com/stretchr/testify/require"
2122

2223
"github.com/sourcenetwork/corekv/badger"
@@ -25,17 +26,24 @@ import (
2526
acpDB "github.com/sourcenetwork/defradb/internal/db/acp"
2627
)
2728

28-
// newOnDiskDB opens a DB on a badger directory with stock options. It returns a close func
29-
// rather than registering cleanup so a caller can release it per iteration. An in-memory
30-
// store has no value log and different write amplification, so it cannot stand in for a
31-
// deployed node when the cost of a write path is what is being measured.
29+
// newOnDiskDB opens a DB on a badger directory with the options a node uses, so the value
30+
// threshold and compression match a deployment. It returns a close func rather than
31+
// registering cleanup so a caller can release it per iteration. An in-memory store has no
32+
// value log and different write amplification, so it cannot stand in for a node when the
33+
// cost of a write path is what is being measured.
3234
func newOnDiskDB(b *testing.B, ctx context.Context) (*DB, func()) {
3335
b.Helper()
3436

3537
dir, err := os.MkdirTemp("", "purgebench")
3638
require.NoError(b, err)
3739

38-
rootstore, err := badger.NewDatastore(dir, badgerds.DefaultOptions(dir))
40+
// Mirrors node/store_badger.go. ZSTDCompressionLevel is left alone because badger
41+
// already defaults it to the value a node sets.
42+
opts := badgerds.DefaultOptions(dir)
43+
opts.ValueThreshold = 1 << 8
44+
opts.Compression = badgeropts.ZSTD
45+
46+
rootstore, err := badger.NewDatastore(dir, opts)
3947
require.NoError(b, err)
4048

4149
adminInfo, err := acpDB.NewNACInfo(ctx, "", false)
@@ -80,7 +88,8 @@ func setupIndexedCollection(b *testing.B, ctx context.Context, db *DB) client.Co
8088
}
8189

8290
// addRecords writes n documents and returns their IDs. The payload field is unindexed and
83-
// exists only to give each document enough size to reach the value log.
91+
// exists to carry each document past the value threshold, so writes reach the value log the
92+
// way they do on a node.
8493
func addRecords(b *testing.B, ctx context.Context, col client.Collection, n int) []client.DocID {
8594
b.Helper()
8695

@@ -98,37 +107,46 @@ func addRecords(b *testing.B, ctx context.Context, col client.Collection, n int)
98107
}
99108

100109
// BenchmarkPurgeByDocIDsChunkSize measures how long it takes to purge a fixed set of
101-
// documents as the number of them sharing a transaction changes, so purgeChunkSize can be
102-
// chosen from a measurement rather than assumed.
110+
// documents as the number of them sharing a transaction changes.
111+
//
112+
// Both pruneHistory branches are swept. The CLI and HTTP defaults are false and true adds
113+
// the per-document DAG walk, so a size measured under one branch is not automatically right
114+
// for the other. Documents are written locally, so their DAGs are one commit deep; a
115+
// document built up over many merges walks further and costs more than this measures.
103116
//
104-
// pruneHistory is on because that is the deployed setting and it adds the per-document DAG
105-
// walk. Documents are written locally, so their DAGs are one commit deep; a document built
106-
// up over many merges walks further and costs more than this measures.
117+
// The sweep runs below purgeChunkSize so the low end is measured rather than assumed to be
118+
// the endpoint.
119+
//
120+
// This purges single-threaded against an idle store, so it shows how cost scales with chunk
121+
// size and not how a size holds up under concurrent merge traffic, which is what the choice
122+
// of constant turns on.
107123
func BenchmarkPurgeByDocIDsChunkSize(b *testing.B) {
108124
const docs = 2000
109125

110-
for _, chunkSize := range []int{8, 25, 50, 100, 200} {
111-
b.Run(fmt.Sprintf("chunk=%d", chunkSize), func(b *testing.B) {
112-
ctx := context.Background()
113-
114-
for b.Loop() {
115-
b.StopTimer()
116-
db, closeDB := newOnDiskDB(b, ctx)
117-
col := setupIndexedCollection(b, ctx, db)
118-
docIDs := addRecords(b, ctx, col, docs)
119-
concrete, ok := col.(*collection)
120-
require.True(b, ok)
121-
b.StartTimer()
122-
123-
for i := 0; i < len(docIDs); i += chunkSize {
124-
end := min(i+chunkSize, len(docIDs))
125-
require.NoError(b, concrete.purgeChunk(ctx, docIDs[i:end], true))
126+
for _, pruneHistory := range []bool{true, false} {
127+
for _, chunkSize := range []int{1, 2, 4, 8, 25, 50, 100, 200} {
128+
b.Run(fmt.Sprintf("prune=%v/chunk=%d", pruneHistory, chunkSize), func(b *testing.B) {
129+
ctx := context.Background()
130+
131+
for b.Loop() {
132+
b.StopTimer()
133+
db, closeDB := newOnDiskDB(b, ctx)
134+
col := setupIndexedCollection(b, ctx, db)
135+
docIDs := addRecords(b, ctx, col, docs)
136+
concrete, ok := col.(*collection)
137+
require.True(b, ok)
138+
b.StartTimer()
139+
140+
for i := 0; i < len(docIDs); i += chunkSize {
141+
end := min(i+chunkSize, len(docIDs))
142+
require.NoError(b, concrete.purgeChunk(ctx, docIDs[i:end], pruneHistory))
143+
}
144+
145+
b.StopTimer()
146+
closeDB()
147+
b.StartTimer()
126148
}
127-
128-
b.StopTimer()
129-
closeDB()
130-
b.StartTimer()
131-
}
132-
})
149+
})
150+
}
133151
}
134152
}

0 commit comments

Comments
 (0)