Files
zapdb/table/builder_test.go
Ibrahim Jarif 58a11fabb6 Compress/Encrypt Blocks in the background (#1227)
This commit adds support for compression/encryption in the background.
All incoming writes are saved to a common buffer and then worker go
routine pick up each block (a block is just a start and end point in
the main buffer). The worker goroutines also fix the table start and
end points. We fix the interleaving space (which might be caused because
the block was compressed) in the builder.Finish() call. All the blocks
are copied over to their correct position.

Master branch vs This commit (new) Benchmarks.
```
benchstat master.txt new.txt
name                              old time/op    new time/op  delta (-ve is better. Reduction in time)
no_compression-16                177ms ± 1%      177ms ± 2%      ~     (p=0.690 n=5+5)
encryption-16                    313ms ± 4%      245ms ± 3%   -21.69%  (p=0.008 n=5+5)
zstd_compression/level_1-16      330ms ± 1%      227ms ± 2%   -31.22%  (p=0.008 n=5+5)
zstd_compression/level_3-16      345ms ± 2%      227ms ± 3%   -34.39%  (p=0.008 n=5+5)
zstd_compression/level_15-16     10.7s ± 1%       1.2s ± 0%   -88.96%  (p=0.008 n=5+5)

name                           old speed       new speed       delta (+ve is better. Speed improvement)
no_compression-16              471MB/s ± 1%    471MB/s ± 2%      ~     (p=0.690 n=5+5)
encryption-16                  266MB/s ± 4%    340MB/s ± 2%   +27.67%  (p=0.008 n=5+5)
zstd_compression/level_1-16    252MB/s ± 1%    367MB/s ± 2%   +45.40%  (p=0.008 n=5+5)
zstd_compression/level_3-16    241MB/s ± 2%    367MB/s ± 3%   +52.44%  (p=0.008 n=5+5)
zstd_compression/level_15-16  7.80MB/s ± 1%  70.62MB/s ± 0%  +805.33%  (p=0.008 n=5+5)
``` 

Please look at https://github.com/dgraph-io/badger/pull/1227#issue-378982789
for the code used for benchmarking.

(cherry picked from commit b13b927102)
2020-03-24 15:46:12 +05:30

207 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 table
import (
"fmt"
"math/rand"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/dgraph-io/badger/v2/options"
"github.com/dgraph-io/badger/v2/pb"
"github.com/dgraph-io/badger/v2/y"
)
func TestTableIndex(t *testing.T) {
rand.Seed(time.Now().Unix())
keysCount := 100000
key := make([]byte, 32)
_, err := rand.Read(key)
require.NoError(t, err)
subTest := []struct {
name string
opts Options
}{
{
name: "No encyption/compression",
opts: Options{
BlockSize: 4 * 1024,
BloomFalsePositive: 0.01,
TableSize: 30 << 20,
},
},
{
// Encryption mode.
name: "Only encryption",
opts: Options{
BlockSize: 4 * 1024,
BloomFalsePositive: 0.01,
TableSize: 30 << 20,
DataKey: &pb.DataKey{Data: key},
},
},
{
// Compression mode.
name: "Only compression",
opts: Options{
BlockSize: 4 * 1024,
BloomFalsePositive: 0.01,
TableSize: 30 << 20,
Compression: options.ZSTD,
ZSTDCompressionLevel: 3,
},
},
{
// Compression mode and encryption.
name: "Compression and encryption",
opts: Options{
BlockSize: 4 * 1024,
BloomFalsePositive: 0.01,
TableSize: 30 << 20,
Compression: options.ZSTD,
ZSTDCompressionLevel: 3,
DataKey: &pb.DataKey{Data: key},
},
},
}
for _, tt := range subTest {
t.Run(tt.name, func(t *testing.T) {
opt := tt.opts
builder := NewTableBuilder(opt)
filename := fmt.Sprintf("%s%c%d.sst", os.TempDir(), os.PathSeparator, rand.Uint32())
f, err := y.OpenSyncedFile(filename, true)
require.NoError(t, err)
blockFirstKeys := make([][]byte, 0)
blockCount := 0
for i := 0; i < keysCount; i++ {
k := []byte(fmt.Sprintf("%016x", i))
v := fmt.Sprintf("%d", i)
vs := y.ValueStruct{Value: []byte(v)}
if i == 0 { // This is first key for first block.
blockFirstKeys = append(blockFirstKeys, k)
blockCount = 1
} else if builder.shouldFinishBlock(k, vs) {
blockCount++
blockFirstKeys = append(blockFirstKeys, k)
}
builder.Add(k, vs, 0)
}
_, err = f.Write(builder.Finish())
require.NoError(t, err, "unable to write to file")
tbl, err := OpenTable(f, opt)
require.NoError(t, err, "unable to open table")
if opt.DataKey == nil {
// key id is zero if there is no datakey.
require.Equal(t, tbl.KeyID(), uint64(0))
}
// Ensure index is built correctly
require.Equal(t, blockCount, len(tbl.blockIndex))
for i, ko := range tbl.blockIndex {
require.Equal(t, ko.Key, blockFirstKeys[i])
}
f.Close()
require.NoError(t, os.RemoveAll(filename))
})
}
}
func TestInvalidCompression(t *testing.T) {
keyPrefix := "key"
opts := Options{Compression: options.ZSTD}
f := buildTestTable(t, keyPrefix, 1000, opts)
t.Run("with correct decompression algo", func(t *testing.T) {
_, err := OpenTable(f, opts)
require.NoError(t, err)
})
t.Run("with incorrect decompression algo", func(t *testing.T) {
// Set incorrect compression algorithm.
opts.Compression = options.Snappy
_, err := OpenTable(f, opts)
require.Error(t, err)
})
}
func BenchmarkBuilder(b *testing.B) {
rand.Seed(time.Now().Unix())
key := func(i int) []byte {
return []byte(fmt.Sprintf("%032d", i))
}
val := make([]byte, 32)
rand.Read(val)
vs := y.ValueStruct{Value: []byte(val)}
keysCount := 1300000 // This number of entries consumes ~64MB of memory.
var keyList [][]byte
for i := 0; i < keysCount; i++ {
keyList = append(keyList, key(i))
}
bench := func(b *testing.B, opt *Options) {
b.SetBytes(int64(keysCount) * (32 + 32))
opt.BlockSize = 4 * 1024
opt.BloomFalsePositive = 0.01
opt.TableSize = 5 << 20
b.ResetTimer()
for i := 0; i < b.N; i++ {
builder := NewTableBuilder(*opt)
for j := 0; j < keysCount; j++ {
builder.Add(keyList[j], vs, 0)
}
_ = builder.Finish()
}
}
b.Run("no compression", func(b *testing.B) {
var opt Options
opt.Compression = options.None
bench(b, &opt)
})
b.Run("encryption", func(b *testing.B) {
var opt Options
key := make([]byte, 32)
rand.Read(key)
opt.DataKey = &pb.DataKey{Data: key}
bench(b, &opt)
})
b.Run("zstd compression", func(b *testing.B) {
var opt Options
opt.Compression = options.ZSTD
b.Run("level 1", func(b *testing.B) {
opt.ZSTDCompressionLevel = 1
bench(b, &opt)
})
b.Run("level 3", func(b *testing.B) {
opt.ZSTDCompressionLevel = 3
bench(b, &opt)
})
b.Run("level 15", func(b *testing.B) {
opt.ZSTDCompressionLevel = 15
bench(b, &opt)
})
})
}