110 lines
2.9 KiB
Go
110 lines
2.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
// 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)
|
|
}
|
|
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")
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
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
|
|
}
|
|
for i := int64(0); i < distinct; i++ {
|
|
if err := c.SetMetadataFlat(fmt.Sprintf("01BENCH%019d", i), rec(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
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
b.ReportMetric(float64(b.N)/b.Elapsed().Seconds(), "records/s")
|
|
}
|