...

Source file src/go.etcd.io/etcd/server/v3/mvcc/hash_test.go

Documentation: go.etcd.io/etcd/server/v3/mvcc

     1  // Copyright 2022 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 mvcc
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"go.etcd.io/etcd/pkg/v3/traceutil"
    24  	"go.etcd.io/etcd/server/v3/lease"
    25  	betesting "go.etcd.io/etcd/server/v3/mvcc/backend/testing"
    26  	"go.etcd.io/etcd/server/v3/storage/mvcc/testutil"
    27  	"go.uber.org/zap/zaptest"
    28  )
    29  
    30  // TestHashByRevValue values to ensure we don't change the output which would
    31  // have catastrophic consequences. Expected output is just hardcoded, so please
    32  // regenerate it every time you change input parameters.
    33  func TestHashByRevValue(t *testing.T) {
    34  	b, _ := betesting.NewDefaultTmpBackend(t)
    35  	s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
    36  
    37  	var totalRevisions int64 = 1210
    38  	assert.Less(t, int64(s.cfg.CompactionBatchLimit), totalRevisions)
    39  	assert.Less(t, int64(testutil.CompactionCycle*10), totalRevisions)
    40  	var rev int64
    41  	var got []KeyValueHash
    42  	for ; rev < totalRevisions; rev += testutil.CompactionCycle {
    43  		putKVs(s, rev, testutil.CompactionCycle)
    44  		hash := testHashByRev(t, s, rev+testutil.CompactionCycle/2)
    45  		got = append(got, hash)
    46  	}
    47  	putKVs(s, rev, totalRevisions)
    48  	hash := testHashByRev(t, s, rev+totalRevisions/2)
    49  	got = append(got, hash)
    50  	assert.Equal(t, []KeyValueHash{
    51  		{4082599214, -1, 35},
    52  		{2279933401, 35, 106},
    53  		{3284231217, 106, 177},
    54  		{126286495, 177, 248},
    55  		{900108730, 248, 319},
    56  		{2475485232, 319, 390},
    57  		{1226296507, 390, 461},
    58  		{2503661030, 461, 532},
    59  		{4155130747, 532, 603},
    60  		{106915399, 603, 674},
    61  		{406914006, 674, 745},
    62  		{1882211381, 745, 816},
    63  		{806177088, 816, 887},
    64  		{664311366, 887, 958},
    65  		{1496914449, 958, 1029},
    66  		{2434525091, 1029, 1100},
    67  		{3988652253, 1100, 1171},
    68  		{1122462288, 1171, 1242},
    69  		{724436716, 1242, 1883},
    70  	}, got)
    71  }
    72  
    73  func TestHashByRevValueLastRevision(t *testing.T) {
    74  	b, _ := betesting.NewDefaultTmpBackend(t)
    75  	s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
    76  
    77  	var totalRevisions int64 = 1210
    78  	assert.Less(t, int64(s.cfg.CompactionBatchLimit), totalRevisions)
    79  	assert.Less(t, int64(testutil.CompactionCycle*10), totalRevisions)
    80  	var rev int64
    81  	var got []KeyValueHash
    82  	for ; rev < totalRevisions; rev += testutil.CompactionCycle {
    83  		putKVs(s, rev, testutil.CompactionCycle)
    84  		hash := testHashByRev(t, s, 0)
    85  		got = append(got, hash)
    86  	}
    87  	putKVs(s, rev, totalRevisions)
    88  	hash := testHashByRev(t, s, 0)
    89  	got = append(got, hash)
    90  	assert.Equal(t, []KeyValueHash{
    91  		{1913897190, -1, 73},
    92  		{224860069, 73, 145},
    93  		{1565167519, 145, 217},
    94  		{1566261620, 217, 289},
    95  		{2037173024, 289, 361},
    96  		{691659396, 361, 433},
    97  		{2713730748, 433, 505},
    98  		{3919322507, 505, 577},
    99  		{769967540, 577, 649},
   100  		{2909194793, 649, 721},
   101  		{1576921157, 721, 793},
   102  		{4067701532, 793, 865},
   103  		{2226384237, 865, 937},
   104  		{2923408134, 937, 1009},
   105  		{2680329256, 1009, 1081},
   106  		{1546717673, 1081, 1153},
   107  		{2713657846, 1153, 1225},
   108  		{1046575299, 1225, 1297},
   109  		{2017735779, 1297, 2508},
   110  	}, got)
   111  }
   112  
   113  func putKVs(s *store, rev, count int64) {
   114  	for i := rev; i <= rev+count; i++ {
   115  		s.Put([]byte(testutil.PickKey(i)), []byte(fmt.Sprint(i)), 0)
   116  	}
   117  }
   118  
   119  func testHashByRev(t *testing.T, s *store, rev int64) KeyValueHash {
   120  	if rev == 0 {
   121  		rev = s.Rev()
   122  	}
   123  	hash, _, err := s.hashByRev(rev)
   124  	assert.NoError(t, err, "error on rev %v", rev)
   125  	_, err = s.Compact(traceutil.TODO(), rev)
   126  	assert.NoError(t, err, "error on compact %v", rev)
   127  	return hash
   128  }
   129  
   130  // TestCompactionHash
   131  // TODO: Change this to fuzz test
   132  func TestCompactionHash(t *testing.T) {
   133  	b, _ := betesting.NewDefaultTmpBackend(t)
   134  	s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
   135  
   136  	testutil.TestCompactionHash(context.Background(), t, hashTestCase{s}, s.cfg.CompactionBatchLimit)
   137  }
   138  
   139  type hashTestCase struct {
   140  	*store
   141  }
   142  
   143  func (tc hashTestCase) Put(ctx context.Context, key, value string) error {
   144  	tc.store.Put([]byte(key), []byte(value), 0)
   145  	return nil
   146  }
   147  
   148  func (tc hashTestCase) Delete(ctx context.Context, key string) error {
   149  	tc.store.DeleteRange([]byte(key), nil)
   150  	return nil
   151  }
   152  
   153  func (tc hashTestCase) HashByRev(ctx context.Context, rev int64) (testutil.KeyValueHash, error) {
   154  	hash, _, err := tc.store.HashStorage().HashByRev(rev)
   155  	return testutil.KeyValueHash{Hash: hash.Hash, CompactRevision: hash.CompactRevision, Revision: hash.Revision}, err
   156  }
   157  
   158  func (tc hashTestCase) Defrag(ctx context.Context) error {
   159  	return tc.store.b.Defrag()
   160  }
   161  
   162  func (tc hashTestCase) Compact(ctx context.Context, rev int64) error {
   163  	done, err := tc.store.Compact(traceutil.TODO(), rev)
   164  	if err != nil {
   165  		return err
   166  	}
   167  	select {
   168  	case <-done:
   169  	case <-ctx.Done():
   170  		return ctx.Err()
   171  	}
   172  	return nil
   173  }
   174  
   175  func TestHasherStore(t *testing.T) {
   176  	lg := zaptest.NewLogger(t)
   177  	s := newHashStorage(lg, newFakeStore())
   178  	var hashes []KeyValueHash
   179  	for i := 0; i < hashStorageMaxSize; i++ {
   180  		hash := KeyValueHash{Hash: uint32(i), Revision: int64(i) + 10, CompactRevision: int64(i) + 100}
   181  		hashes = append(hashes, hash)
   182  		s.Store(hash)
   183  	}
   184  
   185  	for _, want := range hashes {
   186  		got, _, err := s.HashByRev(want.Revision)
   187  		if err != nil {
   188  			t.Fatal(err)
   189  		}
   190  		if want.Hash != got.Hash {
   191  			t.Errorf("Expected stored hash to match, got: %d, expected: %d", want.Hash, got.Hash)
   192  		}
   193  		if want.Revision != got.Revision {
   194  			t.Errorf("Expected stored revision to match, got: %d, expected: %d", want.Revision, got.Revision)
   195  		}
   196  		if want.CompactRevision != got.CompactRevision {
   197  			t.Errorf("Expected stored compact revision to match, got: %d, expected: %d", want.CompactRevision, got.CompactRevision)
   198  		}
   199  	}
   200  }
   201  
   202  func TestHasherStoreFull(t *testing.T) {
   203  	lg := zaptest.NewLogger(t)
   204  	s := newHashStorage(lg, newFakeStore())
   205  	var minRevision int64 = 100
   206  	var maxRevision = minRevision + hashStorageMaxSize
   207  	for i := 0; i < hashStorageMaxSize; i++ {
   208  		s.Store(KeyValueHash{Revision: int64(i) + minRevision})
   209  	}
   210  
   211  	// Hash for old revision should be discarded as storage is already full
   212  	s.Store(KeyValueHash{Revision: minRevision - 1})
   213  	hash, _, err := s.HashByRev(minRevision - 1)
   214  	if err == nil {
   215  		t.Errorf("Expected an error as old revision should be discarded, got: %v", hash)
   216  	}
   217  	// Hash for new revision should be stored even when storage is full
   218  	s.Store(KeyValueHash{Revision: maxRevision + 1})
   219  	_, _, err = s.HashByRev(maxRevision + 1)
   220  	if err != nil {
   221  		t.Errorf("Didn't expect error for new revision, err: %v", err)
   222  	}
   223  }
   224  

View as plain text