Skip to content

Commit f27d3f9

Browse files
authored
test: reserve sample deterministic test + benchmarks (#5374)
1 parent 858f52c commit f27d3f9

File tree

2 files changed

+155
-3
lines changed

2 files changed

+155
-3
lines changed

pkg/accesscontrol/access_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ func getPrivKey(keyNumber int) *ecdsa.PrivateKey {
7171
}
7272

7373
func TestDecryptRef_Publisher(t *testing.T) {
74-
t.Parallel()
7574
ctx := context.Background()
7675
id1 := getPrivKey(1)
7776
s := kvsmock.New()

pkg/storer/sample_test.go

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ package storer_test
66

77
import (
88
"context"
9+
"fmt"
910
"math/rand"
1011
"testing"
1112
"time"
1213

14+
"github.com/ethersphere/bee/v2/pkg/cac"
1315
"github.com/ethersphere/bee/v2/pkg/postage"
1416

1517
postagetesting "github.com/ethersphere/bee/v2/pkg/postage/testing"
@@ -112,7 +114,6 @@ func TestReserveSampler(t *testing.T) {
112114

113115
assertSampleNoErrors(t, sample)
114116
})
115-
116117
}
117118

118119
t.Run("disk", func(t *testing.T) {
@@ -233,7 +234,6 @@ func TestReserveSamplerSisterNeighborhood(t *testing.T) {
233234
t.Fatalf("sample should not have ignored chunks")
234235
}
235236
})
236-
237237
}
238238

239239
t.Run("disk", func(t *testing.T) {
@@ -309,6 +309,63 @@ func assertValidSample(t *testing.T, sample storer.Sample, minRadius uint8, anch
309309
}
310310
}
311311

312+
// TestSampleVectorCAC is a deterministic test vector that verifies the chunk
313+
// address and transformed address produced by MakeSampleUsingChunks for a
314+
// single hardcoded CAC chunk and anchor. It guards against regressions in the
315+
// BMT hashing or sampling pipeline.
316+
func TestSampleVectorCAC(t *testing.T) {
317+
t.Parallel()
318+
319+
// Chunk content: 4096 bytes with repeating pattern i%256.
320+
chunkContent := make([]byte, swarm.ChunkSize)
321+
for i := range chunkContent {
322+
chunkContent[i] = byte(i % 256)
323+
}
324+
325+
ch, err := cac.New(chunkContent)
326+
if err != nil {
327+
t.Fatal(err)
328+
}
329+
330+
// Attach a hardcoded (but otherwise irrelevant) stamp so that
331+
// MakeSampleUsingChunks can read ch.Stamp() without panicking.
332+
batchID := make([]byte, 32)
333+
for i := range batchID {
334+
batchID[i] = byte(i + 1)
335+
}
336+
sig := make([]byte, 65)
337+
for i := range sig {
338+
sig[i] = byte(i + 1)
339+
}
340+
ch = ch.WithStamp(postage.NewStamp(batchID, make([]byte, 8), make([]byte, 8), sig))
341+
342+
// Anchor: exactly 32 bytes, constant across runs.
343+
anchor := []byte("swarm-test-anchor-deterministic!")
344+
345+
sample, err := storer.MakeSampleUsingChunks([]swarm.Chunk{ch}, anchor)
346+
if err != nil {
347+
t.Fatal(err)
348+
}
349+
350+
if len(sample.Items) != 1 {
351+
t.Fatalf("expected 1 sample item, got %d", len(sample.Items))
352+
}
353+
354+
item := sample.Items[0]
355+
356+
const (
357+
wantChunkAddr = "902406053a7a2f3a17f16097e1d0b4b6a4abeae6b84968f5503ae621f9522e16"
358+
wantTransformedAddr = "9dee91d1ed794460474ffc942996bd713176731db4581a3c6470fe9862905a60"
359+
)
360+
361+
if got := item.ChunkAddress.String(); got != wantChunkAddr {
362+
t.Errorf("chunk address mismatch:\n got: %s\n want: %s", got, wantChunkAddr)
363+
}
364+
if got := item.TransformedAddress.String(); got != wantTransformedAddr {
365+
t.Errorf("transformed address mismatch:\n got: %s\n want: %s", got, wantTransformedAddr)
366+
}
367+
}
368+
312369
func assertSampleNoErrors(t *testing.T, sample storer.Sample) {
313370
t.Helper()
314371

@@ -325,3 +382,99 @@ func assertSampleNoErrors(t *testing.T, sample storer.Sample) {
325382
t.Fatalf("got unexpected invalid stamps")
326383
}
327384
}
385+
386+
// Benchmark results:
387+
// goos: linux
388+
// goarch: amd64
389+
// pkg: github.com/ethersphere/bee/v2/pkg/storer
390+
// cpu: Intel(R) Core(TM) Ultra 7 165U
391+
// BenchmarkCachePutter-14 473118 2149 ns/op 1184 B/op 24 allocs/op
392+
// BenchmarkReservePutter-14 48109 29760 ns/op 12379 B/op 141 allocs/op
393+
// BenchmarkReserveSample1k-14 100 12392598 ns/op 9364970 B/op 161383 allocs/op
394+
// BenchmarkSampleHashing/chunks=1000-14 9 127425952 ns/op 32.14 MB/s 69386109 B/op 814005 allocs/op
395+
// BenchmarkSampleHashing/chunks=10000-14 1 1241432669 ns/op 32.99 MB/s 693843032 B/op 8140005 allocs/op
396+
// PASS
397+
// ok github.com/ethersphere/bee/v2/pkg/storer 34.319s
398+
399+
// BenchmarkReserveSample measures the end-to-end time of the ReserveSample
400+
// method, including DB iteration, chunk loading, stamp validation, and sample
401+
// assembly.
402+
func BenchmarkReserveSample1k(b *testing.B) {
403+
const chunkCountPerPO = 100
404+
const maxPO = 10
405+
406+
baseAddr := swarm.RandAddress(b)
407+
opts := dbTestOps(baseAddr, 5000, nil, nil, time.Second)
408+
opts.ValidStamp = func(ch swarm.Chunk) (swarm.Chunk, error) { return ch, nil }
409+
410+
st, err := diskStorer(b, opts)()
411+
if err != nil {
412+
b.Fatal(err)
413+
}
414+
415+
timeVar := uint64(time.Now().UnixNano())
416+
417+
putter := st.ReservePutter()
418+
for po := range maxPO {
419+
for range chunkCountPerPO {
420+
ch := chunk.GenerateValidRandomChunkAt(b, baseAddr, po).WithBatch(3, 2, false)
421+
ch = ch.WithStamp(postagetesting.MustNewStampWithTimestamp(timeVar - 1))
422+
if err := putter.Put(context.Background(), ch); err != nil {
423+
b.Fatal(err)
424+
}
425+
}
426+
}
427+
428+
var (
429+
radius uint8 = 5
430+
anchor = swarm.RandAddressAt(b, baseAddr, int(radius)).Bytes()
431+
)
432+
433+
b.ResetTimer()
434+
435+
for range b.N {
436+
_, err := st.ReserveSample(context.TODO(), anchor, radius, timeVar, nil)
437+
if err != nil {
438+
b.Fatal(err)
439+
}
440+
}
441+
}
442+
443+
// BenchmarkSampleHashing measures the time taken by MakeSampleUsingChunks to
444+
// hash a fixed set of CAC chunks.
445+
func BenchmarkSampleHashing(b *testing.B) {
446+
anchor := []byte("swarm-test-anchor-deterministic!")
447+
448+
// Shared zero-value stamp: its contents don't affect hash computation.
449+
stamp := postage.NewStamp(make([]byte, 32), make([]byte, 8), make([]byte, 8), make([]byte, 65))
450+
451+
for _, count := range []int{1_000, 10_000} {
452+
b.Run(fmt.Sprintf("chunks=%d", count), func(b *testing.B) {
453+
// Build chunks once outside the measured loop.
454+
// Content is derived deterministically from the chunk index so
455+
// that every run produces the same set of chunk addresses.
456+
chunks := make([]swarm.Chunk, count)
457+
content := make([]byte, swarm.ChunkSize)
458+
for i := range chunks {
459+
for j := range content {
460+
content[j] = byte(i + j)
461+
}
462+
ch, err := cac.New(content)
463+
if err != nil {
464+
b.Fatal(err)
465+
}
466+
chunks[i] = ch.WithStamp(stamp)
467+
}
468+
469+
// Report throughput so the output shows MB/s as well as ns/op.
470+
b.SetBytes(int64(count) * swarm.ChunkSize)
471+
b.ResetTimer()
472+
473+
for range b.N {
474+
if _, err := storer.MakeSampleUsingChunks(chunks, anchor); err != nil {
475+
b.Fatal(err)
476+
}
477+
}
478+
})
479+
}
480+
}

0 commit comments

Comments
 (0)