v2.0.0: claims model - signed MMUR2 votes in a truth DB, merged view in a rebuildable cache DB; POST /api/claims, claims and merge routes, unsigned writes answer 410
Build and Push Docker Image / build (push) Successful in 5m1s
Build and Push Docker Image / build (push) Successful in 5m1s
This commit is contained in:
@@ -1,109 +1,66 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/metazla/meta-core/internal/claim"
|
||||
)
|
||||
|
||||
// BenchmarkConcurrentRecordWrites is the ingest shape the single-writer model
|
||||
// has to absorb: many goroutines (feeder flushes, gateway persists, plugin
|
||||
// callbacks) each writing a ~40-field record. SQLite serialises them; this
|
||||
// measures what that costs. Reported as records/s.
|
||||
func BenchmarkConcurrentRecordWrites(b *testing.B) {
|
||||
c := NewClient()
|
||||
if err := c.Connect(filepath.Join(b.TempDir(), "bench.db")); err != nil {
|
||||
b.Fatal(err)
|
||||
// benchRecord is a 40-field signed record, the shape gateway hits persist.
|
||||
func benchRecord(b *testing.B, c *Client, i int) []claim.Claim {
|
||||
t := &testing.T{}
|
||||
id := signer(t, byte(1+i%7))
|
||||
cid := fmt.Sprintf("bafkbench%012d", i)
|
||||
out := make([]claim.Claim, 0, 40)
|
||||
for f := 0; f < 40; f++ {
|
||||
out = append(out, vote(t, id, cid, fmt.Sprintf("field%02d", f), fmt.Sprintf("value-%d-%d", i, f), 1000))
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
record := func(i int64) map[string]string {
|
||||
m := map[string]string{
|
||||
"title": fmt.Sprintf("Some Show S01E%02d", i%40),
|
||||
"fileName": fmt.Sprintf("some.show.s01e%02d.1080p.mkv", i%40),
|
||||
"filePath": fmt.Sprintf("/files/watch/some.show.%d.mkv", i),
|
||||
"fileType": "video",
|
||||
"tmdbid": fmt.Sprintf("%d", 1000+i%500),
|
||||
fmt.Sprintf("cids/bafkbench%020d", i): "true",
|
||||
}
|
||||
for f := 0; len(m) < 40; f++ {
|
||||
m[fmt.Sprintf("categories/%d", f)] = "true"
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
const writers = 16
|
||||
var next int64
|
||||
b.ResetTimer()
|
||||
var wg sync.WaitGroup
|
||||
for w := 0; w < writers; w++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
i := atomic.AddInt64(&next, 1)
|
||||
if i > int64(b.N) {
|
||||
return
|
||||
}
|
||||
if err := c.SetMetadataFlat(fmt.Sprintf("01BENCH%019d", i), record(i)); err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "records/s")
|
||||
return out
|
||||
}
|
||||
|
||||
// BenchmarkConcurrentRepersistUnchanged is the dominant write on a permanent
|
||||
// store: a gateway re-persisting hits it already holds. The kv layer skips
|
||||
// every unchanged field, so this should cost reads only.
|
||||
func BenchmarkConcurrentRepersistUnchanged(b *testing.B) {
|
||||
c := NewClient()
|
||||
if err := c.Connect(filepath.Join(b.TempDir(), "bench.db")); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
// BenchmarkConcurrentClaimWrites measures new 40-claim records, 16 writers.
|
||||
func BenchmarkConcurrentClaimWrites(b *testing.B) {
|
||||
c := openClient(&testing.T{}, b.TempDir())
|
||||
defer c.Close()
|
||||
const distinct = 2000
|
||||
rec := func(i int64) map[string]string {
|
||||
m := map[string]string{
|
||||
"title": fmt.Sprintf("Some Show S01E%02d", i%40),
|
||||
"fileType": "video",
|
||||
fmt.Sprintf("cids/bafkbench%020d", i): "true",
|
||||
}
|
||||
for f := 0; len(m) < 40; f++ {
|
||||
m[fmt.Sprintf("categories/%d", f)] = "true"
|
||||
}
|
||||
return m
|
||||
recs := make([][]claim.Claim, b.N)
|
||||
for i := range recs {
|
||||
recs[i] = benchRecord(b, c, i)
|
||||
}
|
||||
for i := int64(0); i < distinct; i++ {
|
||||
if err := c.SetMetadataFlat(fmt.Sprintf("01BENCH%019d", i), rec(i)); err != nil {
|
||||
b.ResetTimer()
|
||||
b.SetParallelism(16)
|
||||
var next int64
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
i := int(atomicAdd(&next) - 1)
|
||||
if _, err := c.WriteClaims(context.Background(), recs[i]); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkRepersistUnchangedClaims measures the 94% case: the same signed
|
||||
// claims again (skip-identical before verification).
|
||||
func BenchmarkRepersistUnchangedClaims(b *testing.B) {
|
||||
c := openClient(&testing.T{}, b.TempDir())
|
||||
defer c.Close()
|
||||
const distinct = 200
|
||||
recs := make([][]claim.Claim, distinct)
|
||||
for i := range recs {
|
||||
recs[i] = benchRecord(b, c, i)
|
||||
if _, err := c.WriteClaims(context.Background(), recs[i]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
const writers = 16
|
||||
var next int64
|
||||
b.ResetTimer()
|
||||
var wg sync.WaitGroup
|
||||
for w := 0; w < writers; w++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
i := atomic.AddInt64(&next, 1)
|
||||
if i > int64(b.N) {
|
||||
return
|
||||
}
|
||||
if err := c.SetMetadataFlat(fmt.Sprintf("01BENCH%019d", i%distinct), rec(i%distinct)); err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := c.WriteClaims(context.Background(), recs[i%distinct]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "records/s")
|
||||
}
|
||||
|
||||
func atomicAdd(p *int64) int64 { return atomic.AddInt64(p, 1) }
|
||||
|
||||
Reference in New Issue
Block a user