mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
mvcc/backend tests: Refactor: Do not mix testing&prod code.
This commit is contained in:
@@ -23,7 +23,6 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
@@ -544,22 +543,6 @@ func (b *backend) OpenReadTxN() int64 {
|
||||
return atomic.LoadInt64(&b.openReadTxN)
|
||||
}
|
||||
|
||||
// NewTmpBackend creates a backend implementation for testing.
|
||||
func NewTmpBackend(t testing.TB, batchInterval time.Duration, batchLimit int) (*backend, string) {
|
||||
dir, err := ioutil.TempDir(t.TempDir(), "etcd_backend_test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tmpPath := filepath.Join(dir, "database")
|
||||
bcfg := DefaultBackendConfig()
|
||||
bcfg.Path, bcfg.BatchInterval, bcfg.BatchLimit = tmpPath, batchInterval, batchLimit
|
||||
return newBackend(bcfg), tmpPath
|
||||
}
|
||||
|
||||
func NewDefaultTmpBackend(t testing.TB) (*backend, string) {
|
||||
return NewTmpBackend(t, defaultBatchInterval, defaultBatchLimit)
|
||||
}
|
||||
|
||||
type snapshot struct {
|
||||
*bolt.Tx
|
||||
stopc chan struct{}
|
||||
|
||||
@@ -12,28 +12,31 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package backend
|
||||
package backend_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
betesting "go.etcd.io/etcd/server/v3/mvcc/backend/testing"
|
||||
)
|
||||
|
||||
func BenchmarkBackendPut(b *testing.B) {
|
||||
backend, tmppath := NewTmpBackend(b, 100*time.Millisecond, 10000)
|
||||
defer backend.Close()
|
||||
defer os.Remove(tmppath)
|
||||
backend, _ := betesting.NewTmpBackend(b, 100*time.Millisecond, 10000)
|
||||
defer betesting.Close(b, backend)
|
||||
|
||||
// prepare keys
|
||||
keys := make([][]byte, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
keys[i] = make([]byte, 64)
|
||||
rand.Read(keys[i])
|
||||
_, err := rand.Read(keys[i])
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
value := make([]byte, 128)
|
||||
rand.Read(value)
|
||||
_, err := rand.Read(value)
|
||||
assert.NoError(b, err)
|
||||
|
||||
batchTx := backend.BatchTx()
|
||||
|
||||
|
||||
@@ -12,22 +12,23 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package backend
|
||||
package backend_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.etcd.io/etcd/server/v3/mvcc/backend"
|
||||
betesting "go.etcd.io/etcd/server/v3/mvcc/backend/testing"
|
||||
)
|
||||
|
||||
func TestBackendClose(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer os.Remove(tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
|
||||
// check close could work
|
||||
done := make(chan struct{})
|
||||
@@ -46,8 +47,8 @@ func TestBackendClose(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBackendSnapshot(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
@@ -57,22 +58,22 @@ func TestBackendSnapshot(t *testing.T) {
|
||||
b.ForceCommit()
|
||||
|
||||
// write snapshot to a new file
|
||||
f, err := ioutil.TempFile(os.TempDir(), "etcd_backend_test")
|
||||
f, err := ioutil.TempFile(t.TempDir(), "etcd_backend_test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
snap := b.Snapshot()
|
||||
defer snap.Close()
|
||||
defer func() { assert.NoError(t, snap.Close()) }()
|
||||
if _, err := snap.WriteTo(f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// bootstrap new backend from the snapshot
|
||||
bcfg := DefaultBackendConfig()
|
||||
bcfg := backend.DefaultBackendConfig()
|
||||
bcfg.Path, bcfg.BatchInterval, bcfg.BatchLimit = f.Name(), time.Hour, 10000
|
||||
nb := New(bcfg)
|
||||
defer cleanup(nb, f.Name())
|
||||
nb := backend.New(bcfg)
|
||||
defer betesting.Close(t, nb)
|
||||
|
||||
newTx := nb.BatchTx()
|
||||
newTx.Lock()
|
||||
@@ -86,10 +87,10 @@ func TestBackendSnapshot(t *testing.T) {
|
||||
func TestBackendBatchIntervalCommit(t *testing.T) {
|
||||
// start backend with super short batch interval so
|
||||
// we do not need to wait long before commit to happen.
|
||||
b, tmpPath := NewTmpBackend(t, time.Nanosecond, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Nanosecond, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
pc := b.Commits()
|
||||
pc := backend.CommitsForTest(b)
|
||||
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
@@ -98,14 +99,14 @@ func TestBackendBatchIntervalCommit(t *testing.T) {
|
||||
tx.Unlock()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if b.Commits() >= pc+1 {
|
||||
if backend.CommitsForTest(b) >= pc+1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
||||
}
|
||||
|
||||
// check whether put happens via db view
|
||||
b.db.View(func(tx *bolt.Tx) error {
|
||||
assert.NoError(t, backend.DbFromBackendForTest(b).View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte("test"))
|
||||
if bucket == nil {
|
||||
t.Errorf("bucket test does not exit")
|
||||
@@ -116,17 +117,17 @@ func TestBackendBatchIntervalCommit(t *testing.T) {
|
||||
t.Errorf("foo key failed to written in backend")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
func TestBackendDefrag(t *testing.T) {
|
||||
b, tmpPath := NewDefaultTmpBackend(t)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewDefaultTmpBackend(t)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
tx.UnsafeCreateBucket([]byte("test"))
|
||||
for i := 0; i < defragLimit+100; i++ {
|
||||
for i := 0; i < backend.DefragLimitForTest()+100; i++ {
|
||||
tx.UnsafePut([]byte("test"), []byte(fmt.Sprintf("foo_%d", i)), []byte("bar"))
|
||||
}
|
||||
tx.Unlock()
|
||||
@@ -178,8 +179,8 @@ func TestBackendDefrag(t *testing.T) {
|
||||
|
||||
// TestBackendWriteback ensures writes are stored to the read txn on write txn unlock.
|
||||
func TestBackendWriteback(t *testing.T) {
|
||||
b, tmpPath := NewDefaultTmpBackend(t)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewDefaultTmpBackend(t)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
@@ -252,8 +253,8 @@ func TestBackendWriteback(t *testing.T) {
|
||||
|
||||
// TestConcurrentReadTx ensures that current read transaction can see all prior writes stored in read buffer
|
||||
func TestConcurrentReadTx(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
wtx1 := b.BatchTx()
|
||||
wtx1.Lock()
|
||||
@@ -282,8 +283,8 @@ func TestConcurrentReadTx(t *testing.T) {
|
||||
// TestBackendWritebackForEach checks that partially written / buffered
|
||||
// data is visited in the same order as fully committed data.
|
||||
func TestBackendWritebackForEach(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
@@ -312,7 +313,7 @@ func TestBackendWritebackForEach(t *testing.T) {
|
||||
}
|
||||
rtx := b.ReadTx()
|
||||
rtx.RLock()
|
||||
rtx.UnsafeForEach([]byte("key"), getSeq)
|
||||
assert.NoError(t, rtx.UnsafeForEach([]byte("key"), getSeq))
|
||||
rtx.RUnlock()
|
||||
|
||||
partialSeq := seq
|
||||
@@ -321,15 +322,10 @@ func TestBackendWritebackForEach(t *testing.T) {
|
||||
b.ForceCommit()
|
||||
|
||||
tx.Lock()
|
||||
tx.UnsafeForEach([]byte("key"), getSeq)
|
||||
assert.NoError(t, tx.UnsafeForEach([]byte("key"), getSeq))
|
||||
tx.Unlock()
|
||||
|
||||
if seq != partialSeq {
|
||||
t.Fatalf("expected %q, got %q", seq, partialSeq)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanup(b Backend, path string) {
|
||||
b.Close()
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package backend
|
||||
package backend_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -20,15 +20,17 @@ import (
|
||||
"time"
|
||||
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.etcd.io/etcd/server/v3/mvcc/backend"
|
||||
betesting "go.etcd.io/etcd/server/v3/mvcc/backend/testing"
|
||||
)
|
||||
|
||||
func TestBatchTxPut(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.BatchTx()
|
||||
|
||||
tx := b.batchTx
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
// create bucket
|
||||
tx.UnsafeCreateBucket([]byte("test"))
|
||||
@@ -37,21 +39,25 @@ func TestBatchTxPut(t *testing.T) {
|
||||
v := []byte("bar")
|
||||
tx.UnsafePut([]byte("test"), []byte("foo"), v)
|
||||
|
||||
tx.Unlock()
|
||||
|
||||
// check put result before and after tx is committed
|
||||
for k := 0; k < 2; k++ {
|
||||
tx.Lock()
|
||||
_, gv := tx.UnsafeRange([]byte("test"), []byte("foo"), nil, 0)
|
||||
tx.Unlock()
|
||||
if !reflect.DeepEqual(gv[0], v) {
|
||||
t.Errorf("v = %s, want %s", string(gv[0]), string(v))
|
||||
}
|
||||
tx.commit(false)
|
||||
tx.Commit()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatchTxRange(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.batchTx
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
@@ -119,33 +125,36 @@ func TestBatchTxRange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBatchTxDelete(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.batchTx
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
tx.UnsafeCreateBucket([]byte("test"))
|
||||
tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
|
||||
|
||||
tx.UnsafeDelete([]byte("test"), []byte("foo"))
|
||||
|
||||
tx.Unlock()
|
||||
|
||||
// check put result before and after tx is committed
|
||||
for k := 0; k < 2; k++ {
|
||||
tx.Lock()
|
||||
ks, _ := tx.UnsafeRange([]byte("test"), []byte("foo"), nil, 0)
|
||||
tx.Unlock()
|
||||
if len(ks) != 0 {
|
||||
t.Errorf("keys on foo = %v, want nil", ks)
|
||||
}
|
||||
tx.commit(false)
|
||||
tx.Commit()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatchTxCommit(t *testing.T) {
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 10000)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.batchTx
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
tx.UnsafeCreateBucket([]byte("test"))
|
||||
tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
|
||||
@@ -154,7 +163,7 @@ func TestBatchTxCommit(t *testing.T) {
|
||||
tx.Commit()
|
||||
|
||||
// check whether put happens via db view
|
||||
b.db.View(func(tx *bolt.Tx) error {
|
||||
backend.DbFromBackendForTest(b).View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte("test"))
|
||||
if bucket == nil {
|
||||
t.Errorf("bucket test does not exit")
|
||||
@@ -171,10 +180,10 @@ func TestBatchTxCommit(t *testing.T) {
|
||||
func TestBatchTxBatchLimitCommit(t *testing.T) {
|
||||
// start backend with batch limit 1 so one write can
|
||||
// trigger a commit
|
||||
b, tmpPath := NewTmpBackend(t, time.Hour, 1)
|
||||
defer cleanup(b, tmpPath)
|
||||
b, _ := betesting.NewTmpBackend(t, time.Hour, 1)
|
||||
defer betesting.Close(t, b)
|
||||
|
||||
tx := b.batchTx
|
||||
tx := b.BatchTx()
|
||||
tx.Lock()
|
||||
tx.UnsafeCreateBucket([]byte("test"))
|
||||
tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
|
||||
@@ -182,7 +191,7 @@ func TestBatchTxBatchLimitCommit(t *testing.T) {
|
||||
|
||||
// batch limit commit should have been triggered
|
||||
// check whether put happens via db view
|
||||
b.db.View(func(tx *bolt.Tx) error {
|
||||
backend.DbFromBackendForTest(b).View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte("test"))
|
||||
if bucket == nil {
|
||||
t.Errorf("bucket test does not exit")
|
||||
|
||||
15
server/mvcc/backend/export_test.go
Normal file
15
server/mvcc/backend/export_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package backend
|
||||
|
||||
import bolt "go.etcd.io/bbolt"
|
||||
|
||||
func DbFromBackendForTest(b Backend) *bolt.DB {
|
||||
return b.(*backend).db
|
||||
}
|
||||
|
||||
func DefragLimitForTest() int {
|
||||
return defragLimit
|
||||
}
|
||||
|
||||
func CommitsForTest(b Backend) int64 {
|
||||
return b.(*backend).Commits()
|
||||
}
|
||||
52
server/mvcc/backend/testing/betesting.go
Normal file
52
server/mvcc/backend/testing/betesting.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2021 The etcd Authors
|
||||
//
|
||||
// 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 betesting
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.etcd.io/etcd/server/v3/mvcc/backend"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
func NewTmpBackendFromCfg(t testing.TB, bcfg backend.BackendConfig) (backend.Backend, string) {
|
||||
dir, err := ioutil.TempDir(t.TempDir(), "etcd_backend_test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tmpPath := filepath.Join(dir, "database")
|
||||
bcfg.Path = tmpPath
|
||||
bcfg.Logger = zaptest.NewLogger(t)
|
||||
return backend.New(bcfg), tmpPath
|
||||
}
|
||||
|
||||
// NewTmpBackend creates a backend implementation for testing.
|
||||
func NewTmpBackend(t testing.TB, batchInterval time.Duration, batchLimit int) (backend.Backend, string) {
|
||||
bcfg := backend.DefaultBackendConfig()
|
||||
bcfg.BatchInterval, bcfg.BatchLimit = batchInterval, batchLimit
|
||||
return NewTmpBackendFromCfg(t, bcfg)
|
||||
}
|
||||
|
||||
func NewDefaultTmpBackend(t testing.TB) (backend.Backend, string) {
|
||||
return NewTmpBackendFromCfg(t, backend.DefaultBackendConfig())
|
||||
}
|
||||
|
||||
func Close(t testing.TB, b backend.Backend) {
|
||||
assert.NoError(t, b.Close())
|
||||
}
|
||||
Reference in New Issue
Block a user