Skip to content

Commit

Permalink
feat(GODT-1728): Store benchmarks
Browse files Browse the repository at this point in the history
Add a collection of benchmarks for the message storage cache. All
operations can be benchmarked.

The number of workers set via `store-workers` will determine the amount
of the parallel requests used by the benchmark and `store-item-size`
controls the size of the data written to the store instance.

Finally the number of items to write to storage is controlled via the
`store-item-count flag`.
  • Loading branch information
LBeernaertProton committed Aug 10, 2022
1 parent 13db986 commit 73a7d85
Show file tree
Hide file tree
Showing 12 changed files with 483 additions and 2 deletions.
8 changes: 8 additions & 0 deletions benchmarks/gluon_bench/flags/store_benchmarks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package flags

import "flag"

var Store = flag.String("store", "default", "Name of the storage implementation to benchmark. Defaults to regular on disk storage by default.")
var StoreWorkers = flag.Uint("store-workers", 1, "Number of concurrent workers for store operations.")
var StoreItemCount = flag.Uint("store-item-count", 1000, "Number of items to generate in the store benchmarks.")
var StoreItemSize = flag.Uint("store-item-size", 15*1024*1024, "Number of items to generate in the store benchmarks.")
5 changes: 3 additions & 2 deletions benchmarks/gluon_bench/imap_benchmarks/imap_benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package imap_benchmarks
import (
"context"
"fmt"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/profiling"
"net"
"strings"
"time"

"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/profiling"
)

// IMAPBenchmark is intended to be used to build benchmarks which bench IMAP commands on a given server.
Expand Down
1 change: 1 addition & 0 deletions benchmarks/gluon_bench/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark"
_ "github.com/ProtonMail/gluon/benchmarks/gluon_bench/gluon_benchmarks"
_ "github.com/ProtonMail/gluon/benchmarks/gluon_bench/imap_benchmarks"
_ "github.com/ProtonMail/gluon/benchmarks/gluon_bench/store_benchmarks"
)

func main() {
Expand Down
51 changes: 51 additions & 0 deletions benchmarks/gluon_bench/store_benchmarks/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package store_benchmarks

import (
"context"
"math/rand"

"github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/flags"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/utils"
"github.com/ProtonMail/gluon/store"
"github.com/google/uuid"
)

type Create struct{}

func (*Create) Name() string {
return "store-create"
}

func (*Create) Setup(ctx context.Context, store store.Store) error {
return nil
}

func (*Create) TearDown(ctx context.Context, store store.Store) error {
return nil
}

func (*Create) Run(ctx context.Context, st store.Store) (*reporter.BenchmarkRun, error) {
return RunStoreWorkers(ctx, st, func(ctx context.Context, s store.Store, dc *utils.DurationCollector, u uint) error {
messages := []string{utils.MessageAfterNoonMeeting, utils.MessageMultiPartMixed, utils.MessageEmbedded}
messagesLen := len(messages)

for i := uint(0); i < *flags.StoreItemCount; i++ {
dc.Start()
err := s.Set(uuid.NewString(), []byte(messages[rand.Intn(messagesLen)]))
dc.Stop()

if err != nil {
return err
}

}

return nil
}), nil
}

func init() {
benchmark.RegisterBenchmark(NewStoreBenchmarkRunner(&Create{}))
}
16 changes: 16 additions & 0 deletions benchmarks/gluon_bench/store_benchmarks/default_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package store_benchmarks

import (
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/flags"
"github.com/ProtonMail/gluon/store"
)

type DefaultStoreBuilder struct{}

func (*DefaultStoreBuilder) New(path string) (store.Store, error) {
return store.NewOnDiskStore(path, []byte(*flags.UserPassword))
}

func init() {
RegisterStoreBuilder("default", &DefaultStoreBuilder{})
}
54 changes: 54 additions & 0 deletions benchmarks/gluon_bench/store_benchmarks/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package store_benchmarks

import (
"context"

"github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/flags"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/utils"
"github.com/ProtonMail/gluon/store"
)

type Delete struct {
uuids []string
}

func (*Delete) Name() string {
return "store-delete"
}

func (d *Delete) Setup(ctx context.Context, s store.Store) error {
uuids, err := CreateRandomState(s, *flags.StoreItemCount)
if err != nil {
return err
}

d.uuids = uuids

return nil
}

func (*Delete) TearDown(ctx context.Context, store store.Store) error {
return nil
}

func (d *Delete) Run(ctx context.Context, st store.Store) (*reporter.BenchmarkRun, error) {
return RunStoreWorkersSplitRange(ctx, st, uint(len(d.uuids)), func(ctx context.Context, s store.Store, dc *utils.DurationCollector, start, end uint) error {
for i := start; i < end; i++ {
dc.Start()
err := s.Delete(d.uuids[i])
dc.Stop()

if err != nil {
panic(err)
}
}

return nil
}), nil
}

func init() {
benchmark.RegisterBenchmark(NewStoreBenchmarkRunner(&Delete{}))
}
60 changes: 60 additions & 0 deletions benchmarks/gluon_bench/store_benchmarks/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package store_benchmarks

import (
"context"
"math/rand"

"github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/flags"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/utils"
"github.com/ProtonMail/gluon/store"
)

type Get struct {
uuids []string
}

func (*Get) Name() string {
return "store-get"
}

func (g *Get) Setup(ctx context.Context, s store.Store) error {
uuids, err := CreateRandomState(s, *flags.StoreItemCount)
if err != nil {
return err
}

g.uuids = uuids

return nil
}

func (*Get) TearDown(ctx context.Context, store store.Store) error {
return nil
}

func (g *Get) Run(ctx context.Context, st store.Store) (*reporter.BenchmarkRun, error) {
uuidLen := len(g.uuids)

return RunStoreWorkers(ctx, st, func(ctx context.Context, s store.Store, dc *utils.DurationCollector, u uint) error {
for i := 0; i < uuidLen; i++ {
index := rand.Intn(uuidLen)

dc.Start()
_, err := s.Get(g.uuids[index])
dc.Stop()

if err != nil {
panic(err)
}

}

return nil
}), nil
}

func init() {
benchmark.RegisterBenchmark(NewStoreBenchmarkRunner(&Get{}))
}
61 changes: 61 additions & 0 deletions benchmarks/gluon_bench/store_benchmarks/rename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package store_benchmarks

import (
"context"

"github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/flags"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/utils"
"github.com/ProtonMail/gluon/store"
"github.com/google/uuid"
)

type Rename struct {
uuids []string
uuidsRenamed []string
}

func (*Rename) Name() string {
return "store-rename"
}

func (r *Rename) Setup(ctx context.Context, s store.Store) error {
uuids, err := CreateRandomState(s, *flags.StoreItemCount)
if err != nil {
return err
}

r.uuids = uuids
r.uuidsRenamed = make([]string, len(r.uuids))

for i := 0; i < len(r.uuids); i++ {
r.uuidsRenamed[i] = uuid.NewString()
}

return nil
}

func (*Rename) TearDown(ctx context.Context, store store.Store) error {
return nil
}

func (r *Rename) Run(ctx context.Context, st store.Store) (*reporter.BenchmarkRun, error) {
return RunStoreWorkersSplitRange(ctx, st, uint(len(r.uuids)), func(ctx context.Context, s store.Store, dc *utils.DurationCollector, start, end uint) error {
for i := start; i < end; i++ {
dc.Start()
err := s.Update(r.uuids[i], r.uuidsRenamed[i])
dc.Stop()

if err != nil {
panic(err)
}
}

return nil
}), nil
}

func init() {
benchmark.RegisterBenchmark(NewStoreBenchmarkRunner(&Rename{}))
}
81 changes: 81 additions & 0 deletions benchmarks/gluon_bench/store_benchmarks/store_benchmark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package store_benchmarks

import (
"context"
"os"

"github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/flags"
"github.com/ProtonMail/gluon/benchmarks/gluon_bench/reporter"
"github.com/ProtonMail/gluon/store"
)

type StoreBenchmark interface {
// Name returns benchmark's name.
Name() string

// Setup should prepare the benchmark.
Setup(ctx context.Context, store store.Store) error

// TearDown should clean the benchmark.
TearDown(ctx context.Context, store store.Store) error

// Run the benchmark.
Run(ctx context.Context, store store.Store) (*reporter.BenchmarkRun, error)
}

type StoreBenchmarkRunner struct {
benchmark StoreBenchmark
benchmarkDir string
store store.Store
}

func (s *StoreBenchmarkRunner) Name() string {
return s.benchmark.Name()
}

func (s *StoreBenchmarkRunner) Setup(ctx context.Context, benchmarkDir string) error {
store, err := NewStore(*flags.Store, benchmarkDir)
if err != nil {
return err
}

s.store = store
s.benchmarkDir = benchmarkDir

if err := s.benchmark.Setup(ctx, s.store); err != nil {
return err
}

return nil
}

func (s *StoreBenchmarkRunner) Run(ctx context.Context) (*reporter.BenchmarkRun, error) {
benchRuns, err := s.benchmark.Run(ctx, s.store)

if err != nil {
return nil, err
}

return benchRuns, nil
}

func (s *StoreBenchmarkRunner) TearDown(ctx context.Context) error {
if err := s.benchmark.TearDown(ctx, s.store); err != nil {
return err
}

if err := s.store.Close(); err != nil {
return err
}

if err := os.RemoveAll(s.benchmarkDir); err != nil {
return err
}

return nil
}

func NewStoreBenchmarkRunner(bench StoreBenchmark) benchmark.Benchmark {
return &StoreBenchmarkRunner{benchmark: bench}
}
Loading

0 comments on commit 73a7d85

Please sign in to comment.