github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_sst_snapshot_storage_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 package kvserver 11 12 import ( 13 "context" 14 "io/ioutil" 15 "os" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/rditer" 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/storage" 21 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 22 "github.com/cockroachdb/cockroach/pkg/util/uuid" 23 "github.com/stretchr/testify/require" 24 "golang.org/x/time/rate" 25 ) 26 27 func TestSSTSnapshotStorage(t *testing.T) { 28 defer leaktest.AfterTest(t)() 29 30 ctx := context.Background() 31 testRangeID := roachpb.RangeID(1) 32 testSnapUUID := uuid.Must(uuid.FromBytes([]byte("foobar1234567890"))) 33 testLimiter := rate.NewLimiter(rate.Inf, 0) 34 35 cleanup, eng := newEngine(t) 36 defer cleanup() 37 defer eng.Close() 38 39 sstSnapshotStorage := NewSSTSnapshotStorage(eng, testLimiter) 40 scratch := sstSnapshotStorage.NewScratchSpace(testRangeID, testSnapUUID) 41 42 // Check that the storage lazily creates the directories on first write. 43 _, err := os.Stat(scratch.snapDir) 44 if !os.IsNotExist(err) { 45 t.Fatalf("expected %s to not exist", scratch.snapDir) 46 } 47 48 f, err := scratch.NewFile(ctx, 0) 49 require.NoError(t, err) 50 defer func() { 51 require.NoError(t, f.Close()) 52 }() 53 54 // Check that even though the files aren't created, they are still recorded in SSTs(). 55 require.Equal(t, len(scratch.SSTs()), 1) 56 57 // Check that the storage lazily creates the files on write. 58 for _, fileName := range scratch.SSTs() { 59 _, err := os.Stat(fileName) 60 if !os.IsNotExist(err) { 61 t.Fatalf("expected %s to not exist", fileName) 62 } 63 } 64 65 _, err = f.Write([]byte("foo")) 66 require.NoError(t, err) 67 68 // After writing to files, check that they have been flushed to disk. 69 for _, fileName := range scratch.SSTs() { 70 require.FileExists(t, fileName) 71 data, err := ioutil.ReadFile(fileName) 72 require.NoError(t, err) 73 require.Equal(t, data, []byte("foo")) 74 } 75 76 // Check that closing is idempotent. 77 require.NoError(t, f.Close()) 78 require.NoError(t, f.Close()) 79 80 // Check that writing to a closed file is an error. 81 _, err = f.Write([]byte("foo")) 82 require.EqualError(t, err, "file has already been closed") 83 84 // Check that closing an empty file is an error. 85 f, err = scratch.NewFile(ctx, 0) 86 require.NoError(t, err) 87 require.EqualError(t, f.Close(), "file is empty") 88 _, err = f.Write([]byte("foo")) 89 require.NoError(t, err) 90 91 // Check that Clear removes the directory. 92 require.NoError(t, scratch.Clear()) 93 _, err = os.Stat(scratch.snapDir) 94 if !os.IsNotExist(err) { 95 t.Fatalf("expected %s to not exist", scratch.snapDir) 96 } 97 require.NoError(t, sstSnapshotStorage.Clear()) 98 _, err = os.Stat(sstSnapshotStorage.dir) 99 if !os.IsNotExist(err) { 100 t.Fatalf("expected %s to not exist", sstSnapshotStorage.dir) 101 } 102 } 103 104 // TestMultiSSTWriterInitSST tests that multiSSTWriter initializes each of the 105 // SST files associated with the three replicated key ranges by writing a range 106 // deletion tombstone that spans the entire range of each respectively. 107 func TestMultiSSTWriterInitSST(t *testing.T) { 108 defer leaktest.AfterTest(t)() 109 110 ctx := context.Background() 111 testRangeID := roachpb.RangeID(1) 112 testSnapUUID := uuid.Must(uuid.FromBytes([]byte("foobar1234567890"))) 113 testLimiter := rate.NewLimiter(rate.Inf, 0) 114 115 cleanup, eng := newEngine(t) 116 defer cleanup() 117 defer eng.Close() 118 119 sstSnapshotStorage := NewSSTSnapshotStorage(eng, testLimiter) 120 scratch := sstSnapshotStorage.NewScratchSpace(testRangeID, testSnapUUID) 121 desc := roachpb.RangeDescriptor{ 122 StartKey: roachpb.RKey("d"), 123 EndKey: roachpb.RKeyMax, 124 } 125 keyRanges := rditer.MakeReplicatedKeyRanges(&desc) 126 127 msstw, err := newMultiSSTWriter(ctx, scratch, keyRanges, 0) 128 require.NoError(t, err) 129 err = msstw.Finish(ctx) 130 require.NoError(t, err) 131 132 var actualSSTs [][]byte 133 fileNames := msstw.scratch.SSTs() 134 for _, file := range fileNames { 135 sst, err := eng.ReadFile(file) 136 require.NoError(t, err) 137 actualSSTs = append(actualSSTs, sst) 138 } 139 140 // Construct an SST file for each of the key ranges and write a rangedel 141 // tombstone that spans from Start to End. 142 var expectedSSTs [][]byte 143 for _, r := range keyRanges { 144 func() { 145 sstFile := &storage.MemFile{} 146 sst := storage.MakeIngestionSSTWriter(sstFile) 147 defer sst.Close() 148 err := sst.ClearRange(r.Start, r.End) 149 require.NoError(t, err) 150 err = sst.Finish() 151 require.NoError(t, err) 152 expectedSSTs = append(expectedSSTs, sstFile.Data()) 153 }() 154 } 155 156 require.Equal(t, len(actualSSTs), len(expectedSSTs)) 157 for i := range fileNames { 158 require.Equal(t, actualSSTs[i], expectedSSTs[i]) 159 } 160 }