Files
zapdb/badger/cmd/write_bench.go
Ibrahim Jarif 3fad1299eb Add option to show badger logs in write benchmark tool (#967)
The current implementation of write benchmark tool doesn't show any logs
from badger. This commits adds a `--logs` flag which allows the user to
enable badger logs.

(cherry picked from commit be4ddd1530)
2020-03-11 18:05:52 +05:30

220 lines
5.2 KiB
Go

/*
* Copyright 2019 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"encoding/binary"
"fmt"
"log"
"math/rand"
"sync"
"sync/atomic"
"time"
humanize "github.com/dustin/go-humanize"
"github.com/spf13/cobra"
"github.com/dgraph-io/badger"
"github.com/dgraph-io/badger/pb"
"github.com/dgraph-io/badger/y"
)
var writeBenchCmd = &cobra.Command{
Use: "write",
Short: "Writes random data to Badger to benchmark write speed.",
Long: `
This command writes random data to Badger to benchmark write speed. Useful for testing and
performance analysis.
`,
RunE: writeBench,
}
var (
keySz int
valSz int
numKeys float64
force bool
sorted bool
showLogs bool
sizeWritten uint64
entriesWritten uint64
)
const (
mil float64 = 1e6
)
func init() {
benchCmd.AddCommand(writeBenchCmd)
writeBenchCmd.Flags().IntVarP(&keySz, "key-size", "k", 32, "Size of key")
writeBenchCmd.Flags().IntVarP(&valSz, "val-size", "v", 128, "Size of value")
writeBenchCmd.Flags().Float64VarP(&numKeys, "keys-mil", "m", 10.0,
"Number of keys to add in millions")
writeBenchCmd.Flags().BoolVarP(&force, "force-compact", "f", true,
"Force compact level 0 on close.")
writeBenchCmd.Flags().BoolVarP(&sorted, "sorted", "s", false, "Write keys in sorted order.")
writeBenchCmd.Flags().BoolVarP(&showLogs, "logs", "l", false, "Show Badger logs.")
}
func writeRandom(db *badger.DB, num uint64) error {
value := make([]byte, valSz)
y.Check2(rand.Read(value))
es := uint64(keySz + valSz) // entry size is keySz + valSz
batch := db.NewWriteBatch()
for i := uint64(1); i <= num; i++ {
key := make([]byte, keySz)
y.Check2(rand.Read(key))
if err := batch.Set(key, value); err != nil {
return err
}
atomic.AddUint64(&entriesWritten, 1)
atomic.AddUint64(&sizeWritten, es)
}
return batch.Flush()
}
func writeSorted(db *badger.DB, num uint64) error {
value := make([]byte, valSz)
y.Check2(rand.Read(value))
es := 8 + valSz // key size is 8 bytes and value size is valSz
writer := db.NewStreamWriter()
if err := writer.Prepare(); err != nil {
return err
}
wg := &sync.WaitGroup{}
writeCh := make(chan *pb.KVList, 3)
writeRange := func(start, end uint64, streamId uint32) {
// end is not included.
defer wg.Done()
kvs := &pb.KVList{}
var sz int
for i := start; i < end; i++ {
key := make([]byte, 8)
binary.BigEndian.PutUint64(key, i)
kvs.Kv = append(kvs.Kv, &pb.KV{
Key: key,
Value: value,
Version: 1,
StreamId: streamId,
})
sz += es
atomic.AddUint64(&entriesWritten, 1)
atomic.AddUint64(&sizeWritten, uint64(es))
if sz >= 4<<20 { // 4 MB
writeCh <- kvs
kvs = &pb.KVList{}
sz = 0
}
}
writeCh <- kvs
}
// Let's create some streams.
width := num / 16
streamID := uint32(0)
for start := uint64(0); start < num; start += width {
end := start + width
if end > num {
end = num
}
streamID++
wg.Add(1)
go writeRange(start, end, streamID)
}
go func() {
wg.Wait()
close(writeCh)
}()
log.Printf("Max StreamId used: %d. Width: %d\n", streamID, width)
for kvs := range writeCh {
if err := writer.Write(kvs); err != nil {
panic(err)
}
}
log.Println("DONE streaming. Flushing...")
return writer.Flush()
}
func writeBench(cmd *cobra.Command, args []string) error {
opt := badger.DefaultOptions(sstDir).
WithValueDir(vlogDir).
WithTruncate(truncate).
WithSyncWrites(false).
WithCompactL0OnClose(force)
if !showLogs {
opt = opt.WithLogger(nil)
}
db, err := badger.Open(opt)
if err != nil {
return err
}
defer func() {
start := time.Now()
err := db.Close()
log.Printf("DB.Close. Error: %v. Time taken to close: %s", err, time.Since(start))
}()
fmt.Println("*********************************************************")
fmt.Println("Starting to benchmark Writes")
fmt.Println("*********************************************************")
startTime = time.Now()
num := uint64(numKeys * mil)
c := y.NewCloser(1)
go reportStats(c)
if sorted {
err = writeSorted(db, num)
} else {
err = writeRandom(db, num)
}
c.SignalAndWait()
return err
}
func reportStats(c *y.Closer) {
defer c.Done()
t := time.NewTicker(time.Second)
defer t.Stop()
for {
select {
case <-c.HasBeenClosed():
return
case <-t.C:
dur := time.Since(startTime)
sz := atomic.LoadUint64(&sizeWritten)
entries := atomic.LoadUint64(&entriesWritten)
bytesRate := sz / uint64(dur.Seconds())
entriesRate := entries / uint64(dur.Seconds())
fmt.Printf("Time elapsed: %s, bytes written: %s, speed: %s/sec, "+
"entries written: %d, speed: %d/sec\n", y.FixedDuration(time.Since(startTime)),
humanize.Bytes(sz), humanize.Bytes(bytesRate), entries, entriesRate)
}
}
}