go.etcd.io/etcd@v3.3.27+incompatible/mvcc/backend/backend_test.go (about)

     1  // Copyright 2015 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package backend
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	bolt "github.com/coreos/bbolt"
    26  )
    27  
    28  func TestBackendClose(t *testing.T) {
    29  	b, tmpPath := NewTmpBackend(time.Hour, 10000)
    30  	defer os.Remove(tmpPath)
    31  
    32  	// check close could work
    33  	done := make(chan struct{})
    34  	go func() {
    35  		err := b.Close()
    36  		if err != nil {
    37  			t.Errorf("close error = %v, want nil", err)
    38  		}
    39  		done <- struct{}{}
    40  	}()
    41  	select {
    42  	case <-done:
    43  	case <-time.After(10 * time.Second):
    44  		t.Errorf("failed to close database in 10s")
    45  	}
    46  }
    47  
    48  func TestBackendSnapshot(t *testing.T) {
    49  	b, tmpPath := NewTmpBackend(time.Hour, 10000)
    50  	defer cleanup(b, tmpPath)
    51  
    52  	tx := b.BatchTx()
    53  	tx.Lock()
    54  	tx.UnsafeCreateBucket([]byte("test"))
    55  	tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
    56  	tx.Unlock()
    57  	b.ForceCommit()
    58  
    59  	// write snapshot to a new file
    60  	f, err := ioutil.TempFile(os.TempDir(), "etcd_backend_test")
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	snap := b.Snapshot()
    65  	defer snap.Close()
    66  	if _, err := snap.WriteTo(f); err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	f.Close()
    70  
    71  	// bootstrap new backend from the snapshot
    72  	bcfg := DefaultBackendConfig()
    73  	bcfg.Path, bcfg.BatchInterval, bcfg.BatchLimit = f.Name(), time.Hour, 10000
    74  	nb := New(bcfg)
    75  	defer cleanup(nb, f.Name())
    76  
    77  	newTx := b.BatchTx()
    78  	newTx.Lock()
    79  	ks, _ := newTx.UnsafeRange([]byte("test"), []byte("foo"), []byte("goo"), 0)
    80  	if len(ks) != 1 {
    81  		t.Errorf("len(kvs) = %d, want 1", len(ks))
    82  	}
    83  	newTx.Unlock()
    84  }
    85  
    86  func TestBackendBatchIntervalCommit(t *testing.T) {
    87  	// start backend with super short batch interval so
    88  	// we do not need to wait long before commit to happen.
    89  	b, tmpPath := NewTmpBackend(time.Nanosecond, 10000)
    90  	defer cleanup(b, tmpPath)
    91  
    92  	pc := b.Commits()
    93  
    94  	tx := b.BatchTx()
    95  	tx.Lock()
    96  	tx.UnsafeCreateBucket([]byte("test"))
    97  	tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
    98  	tx.Unlock()
    99  
   100  	for i := 0; i < 10; i++ {
   101  		if b.Commits() >= pc+1 {
   102  			break
   103  		}
   104  		time.Sleep(time.Duration(i*100) * time.Millisecond)
   105  	}
   106  
   107  	// check whether put happens via db view
   108  	b.db.View(func(tx *bolt.Tx) error {
   109  		bucket := tx.Bucket([]byte("test"))
   110  		if bucket == nil {
   111  			t.Errorf("bucket test does not exit")
   112  			return nil
   113  		}
   114  		v := bucket.Get([]byte("foo"))
   115  		if v == nil {
   116  			t.Errorf("foo key failed to written in backend")
   117  		}
   118  		return nil
   119  	})
   120  }
   121  
   122  func TestBackendDefrag(t *testing.T) {
   123  	b, tmpPath := NewDefaultTmpBackend()
   124  	defer cleanup(b, tmpPath)
   125  
   126  	tx := b.BatchTx()
   127  	tx.Lock()
   128  	tx.UnsafeCreateBucket([]byte("test"))
   129  	for i := 0; i < defragLimit+100; i++ {
   130  		tx.UnsafePut([]byte("test"), []byte(fmt.Sprintf("foo_%d", i)), []byte("bar"))
   131  	}
   132  	tx.Unlock()
   133  	b.ForceCommit()
   134  
   135  	// remove some keys to ensure the disk space will be reclaimed after defrag
   136  	tx = b.BatchTx()
   137  	tx.Lock()
   138  	for i := 0; i < 50; i++ {
   139  		tx.UnsafeDelete([]byte("test"), []byte(fmt.Sprintf("foo_%d", i)))
   140  	}
   141  	tx.Unlock()
   142  	b.ForceCommit()
   143  
   144  	size := b.Size()
   145  
   146  	// shrink and check hash
   147  	oh, err := b.Hash(nil)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	err = b.Defrag()
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  
   157  	nh, err := b.Hash(nil)
   158  	if err != nil {
   159  		t.Fatal(err)
   160  	}
   161  	if oh != nh {
   162  		t.Errorf("hash = %v, want %v", nh, oh)
   163  	}
   164  
   165  	nsize := b.Size()
   166  	if nsize >= size {
   167  		t.Errorf("new size = %v, want < %d", nsize, size)
   168  	}
   169  
   170  	// try put more keys after shrink.
   171  	tx = b.BatchTx()
   172  	tx.Lock()
   173  	tx.UnsafeCreateBucket([]byte("test"))
   174  	tx.UnsafePut([]byte("test"), []byte("more"), []byte("bar"))
   175  	tx.Unlock()
   176  	b.ForceCommit()
   177  }
   178  
   179  // TestBackendWriteback ensures writes are stored to the read txn on write txn unlock.
   180  func TestBackendWriteback(t *testing.T) {
   181  	b, tmpPath := NewDefaultTmpBackend()
   182  	defer cleanup(b, tmpPath)
   183  
   184  	tx := b.BatchTx()
   185  	tx.Lock()
   186  	tx.UnsafeCreateBucket([]byte("key"))
   187  	tx.UnsafePut([]byte("key"), []byte("abc"), []byte("bar"))
   188  	tx.UnsafePut([]byte("key"), []byte("def"), []byte("baz"))
   189  	tx.UnsafePut([]byte("key"), []byte("overwrite"), []byte("1"))
   190  	tx.Unlock()
   191  
   192  	// overwrites should be propagated too
   193  	tx.Lock()
   194  	tx.UnsafePut([]byte("key"), []byte("overwrite"), []byte("2"))
   195  	tx.Unlock()
   196  
   197  	keys := []struct {
   198  		key   []byte
   199  		end   []byte
   200  		limit int64
   201  
   202  		wkey [][]byte
   203  		wval [][]byte
   204  	}{
   205  		{
   206  			key: []byte("abc"),
   207  			end: nil,
   208  
   209  			wkey: [][]byte{[]byte("abc")},
   210  			wval: [][]byte{[]byte("bar")},
   211  		},
   212  		{
   213  			key: []byte("abc"),
   214  			end: []byte("def"),
   215  
   216  			wkey: [][]byte{[]byte("abc")},
   217  			wval: [][]byte{[]byte("bar")},
   218  		},
   219  		{
   220  			key: []byte("abc"),
   221  			end: []byte("deg"),
   222  
   223  			wkey: [][]byte{[]byte("abc"), []byte("def")},
   224  			wval: [][]byte{[]byte("bar"), []byte("baz")},
   225  		},
   226  		{
   227  			key:   []byte("abc"),
   228  			end:   []byte("\xff"),
   229  			limit: 1,
   230  
   231  			wkey: [][]byte{[]byte("abc")},
   232  			wval: [][]byte{[]byte("bar")},
   233  		},
   234  		{
   235  			key: []byte("abc"),
   236  			end: []byte("\xff"),
   237  
   238  			wkey: [][]byte{[]byte("abc"), []byte("def"), []byte("overwrite")},
   239  			wval: [][]byte{[]byte("bar"), []byte("baz"), []byte("2")},
   240  		},
   241  	}
   242  	rtx := b.ReadTx()
   243  	for i, tt := range keys {
   244  		rtx.Lock()
   245  		k, v := rtx.UnsafeRange([]byte("key"), tt.key, tt.end, tt.limit)
   246  		rtx.Unlock()
   247  		if !reflect.DeepEqual(tt.wkey, k) || !reflect.DeepEqual(tt.wval, v) {
   248  			t.Errorf("#%d: want k=%+v, v=%+v; got k=%+v, v=%+v", i, tt.wkey, tt.wval, k, v)
   249  		}
   250  	}
   251  }
   252  
   253  // TestBackendWritebackForEach checks that partially written / buffered
   254  // data is visited in the same order as fully committed data.
   255  func TestBackendWritebackForEach(t *testing.T) {
   256  	b, tmpPath := NewTmpBackend(time.Hour, 10000)
   257  	defer cleanup(b, tmpPath)
   258  
   259  	tx := b.BatchTx()
   260  	tx.Lock()
   261  	tx.UnsafeCreateBucket([]byte("key"))
   262  	for i := 0; i < 5; i++ {
   263  		k := []byte(fmt.Sprintf("%04d", i))
   264  		tx.UnsafePut([]byte("key"), k, []byte("bar"))
   265  	}
   266  	tx.Unlock()
   267  
   268  	// writeback
   269  	b.ForceCommit()
   270  
   271  	tx.Lock()
   272  	tx.UnsafeCreateBucket([]byte("key"))
   273  	for i := 5; i < 20; i++ {
   274  		k := []byte(fmt.Sprintf("%04d", i))
   275  		tx.UnsafePut([]byte("key"), k, []byte("bar"))
   276  	}
   277  	tx.Unlock()
   278  
   279  	seq := ""
   280  	getSeq := func(k, v []byte) error {
   281  		seq += string(k)
   282  		return nil
   283  	}
   284  	rtx := b.ReadTx()
   285  	rtx.Lock()
   286  	rtx.UnsafeForEach([]byte("key"), getSeq)
   287  	rtx.Unlock()
   288  
   289  	partialSeq := seq
   290  
   291  	seq = ""
   292  	b.ForceCommit()
   293  
   294  	tx.Lock()
   295  	tx.UnsafeForEach([]byte("key"), getSeq)
   296  	tx.Unlock()
   297  
   298  	if seq != partialSeq {
   299  		t.Fatalf("expected %q, got %q", seq, partialSeq)
   300  	}
   301  }
   302  
   303  func cleanup(b Backend, path string) {
   304  	b.Close()
   305  	os.Remove(path)
   306  }