github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/snapshot_test.go (about) 1 // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package pebble 6 7 import ( 8 "bytes" 9 "fmt" 10 "math/rand" 11 "reflect" 12 "runtime" 13 "strings" 14 "sync" 15 "testing" 16 "time" 17 18 "github.com/cockroachdb/datadriven" 19 "github.com/cockroachdb/errors" 20 "github.com/cockroachdb/pebble/vfs" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestSnapshotListToSlice(t *testing.T) { 25 testCases := []struct { 26 vals []uint64 27 }{ 28 {nil}, 29 {[]uint64{1}}, 30 {[]uint64{1, 2, 3}}, 31 {[]uint64{3, 2, 1}}, 32 } 33 for _, c := range testCases { 34 t.Run("", func(t *testing.T) { 35 var l snapshotList 36 l.init() 37 for _, v := range c.vals { 38 l.pushBack(&Snapshot{seqNum: v}) 39 } 40 slice := l.toSlice() 41 if !reflect.DeepEqual(c.vals, slice) { 42 t.Fatalf("expected %d, but got %d", c.vals, slice) 43 } 44 }) 45 } 46 } 47 48 func testSnapshotImpl(t *testing.T, newSnapshot func(d *DB) Reader) { 49 var d *DB 50 var snapshots map[string]Reader 51 52 close := func() { 53 for _, s := range snapshots { 54 require.NoError(t, s.Close()) 55 } 56 snapshots = nil 57 if d != nil { 58 require.NoError(t, d.Close()) 59 d = nil 60 } 61 } 62 defer close() 63 64 randVersion := func() FormatMajorVersion { 65 minVersion := formatUnusedPrePebblev1MarkedCompacted 66 return FormatMajorVersion(int(minVersion) + rand.Intn( 67 int(internalFormatNewest)-int(minVersion)+1)) 68 } 69 datadriven.RunTest(t, "testdata/snapshot", func(t *testing.T, td *datadriven.TestData) string { 70 switch td.Cmd { 71 case "define": 72 close() 73 74 var err error 75 options := &Options{ 76 FS: vfs.NewMem(), 77 FormatMajorVersion: randVersion(), 78 } 79 if td.HasArg("block-size") { 80 var blockSize int 81 td.ScanArgs(t, "block-size", &blockSize) 82 options.Levels = make([]LevelOptions, 1) 83 options.Levels[0].BlockSize = blockSize 84 options.Levels[0].IndexBlockSize = blockSize 85 } 86 d, err = Open("", options) 87 if err != nil { 88 return err.Error() 89 } 90 snapshots = make(map[string]Reader) 91 92 for _, line := range strings.Split(td.Input, "\n") { 93 parts := strings.Fields(line) 94 if len(parts) == 0 { 95 continue 96 } 97 var err error 98 switch parts[0] { 99 case "set": 100 if len(parts) != 3 { 101 return fmt.Sprintf("%s expects 2 arguments", parts[0]) 102 } 103 err = d.Set([]byte(parts[1]), []byte(parts[2]), nil) 104 case "del": 105 if len(parts) != 2 { 106 return fmt.Sprintf("%s expects 1 argument", parts[0]) 107 } 108 err = d.Delete([]byte(parts[1]), nil) 109 case "merge": 110 if len(parts) != 3 { 111 return fmt.Sprintf("%s expects 2 arguments", parts[0]) 112 } 113 err = d.Merge([]byte(parts[1]), []byte(parts[2]), nil) 114 case "snapshot": 115 if len(parts) != 2 { 116 return fmt.Sprintf("%s expects 1 argument", parts[0]) 117 } 118 snapshots[parts[1]] = newSnapshot(d) 119 case "compact": 120 if len(parts) != 2 { 121 return fmt.Sprintf("%s expects 1 argument", parts[0]) 122 } 123 keys := strings.Split(parts[1], "-") 124 if len(keys) != 2 { 125 return fmt.Sprintf("malformed key range: %s", parts[1]) 126 } 127 err = d.Compact([]byte(keys[0]), []byte(keys[1]), false) 128 default: 129 return fmt.Sprintf("unknown op: %s", parts[0]) 130 } 131 if err != nil { 132 return err.Error() 133 } 134 } 135 return "" 136 137 case "db-state": 138 d.mu.Lock() 139 s := d.mu.versions.currentVersion().String() 140 d.mu.Unlock() 141 return s 142 143 case "iter": 144 var iter *Iterator 145 if len(td.CmdArgs) == 1 { 146 if td.CmdArgs[0].Key != "snapshot" { 147 return fmt.Sprintf("unknown argument: %s", td.CmdArgs[0]) 148 } 149 if len(td.CmdArgs[0].Vals) != 1 { 150 return fmt.Sprintf("%s expects 1 value: %s", td.CmdArgs[0].Key, td.CmdArgs[0]) 151 } 152 name := td.CmdArgs[0].Vals[0] 153 snapshot := snapshots[name] 154 if snapshot == nil { 155 return fmt.Sprintf("unable to find snapshot \"%s\"", name) 156 } 157 iter, _ = snapshot.NewIter(nil) 158 } else { 159 iter, _ = d.NewIter(nil) 160 } 161 defer iter.Close() 162 163 var b bytes.Buffer 164 for _, line := range strings.Split(td.Input, "\n") { 165 parts := strings.Fields(line) 166 if len(parts) == 0 { 167 continue 168 } 169 switch parts[0] { 170 case "first": 171 iter.First() 172 case "last": 173 iter.Last() 174 case "seek-ge": 175 if len(parts) != 2 { 176 return "seek-ge <key>\n" 177 } 178 iter.SeekGE([]byte(strings.TrimSpace(parts[1]))) 179 case "seek-lt": 180 if len(parts) != 2 { 181 return "seek-lt <key>\n" 182 } 183 iter.SeekLT([]byte(strings.TrimSpace(parts[1]))) 184 case "next": 185 iter.Next() 186 case "prev": 187 iter.Prev() 188 default: 189 return fmt.Sprintf("unknown op: %s", parts[0]) 190 } 191 if iter.Valid() { 192 fmt.Fprintf(&b, "%s:%s\n", iter.Key(), iter.Value()) 193 } else if err := iter.Error(); err != nil { 194 fmt.Fprintf(&b, "err=%v\n", err) 195 } else { 196 fmt.Fprintf(&b, ".\n") 197 } 198 } 199 return b.String() 200 201 default: 202 return fmt.Sprintf("unknown command: %s", td.Cmd) 203 } 204 }) 205 } 206 207 func TestSnapshot(t *testing.T) { 208 testSnapshotImpl(t, func(d *DB) Reader { 209 return d.NewSnapshot() 210 }) 211 } 212 213 func TestEventuallyFileOnlySnapshot(t *testing.T) { 214 testSnapshotImpl(t, func(d *DB) Reader { 215 // NB: all keys in testdata/snapshot fall within the ASCII keyrange a-z. 216 return d.NewEventuallyFileOnlySnapshot([]KeyRange{{Start: []byte("a"), End: []byte("z")}}) 217 }) 218 } 219 220 func TestSnapshotClosed(t *testing.T) { 221 d, err := Open("", &Options{ 222 FS: vfs.NewMem(), 223 }) 224 require.NoError(t, err) 225 226 catch := func(f func()) (err error) { 227 defer func() { 228 if r := recover(); r != nil { 229 err = r.(error) 230 } 231 }() 232 f() 233 return nil 234 } 235 236 snap := d.NewSnapshot() 237 require.NoError(t, snap.Close()) 238 require.True(t, errors.Is(catch(func() { _ = snap.Close() }), ErrClosed)) 239 require.True(t, errors.Is(catch(func() { _, _, _ = snap.Get(nil) }), ErrClosed)) 240 require.True(t, errors.Is(catch(func() { snap.NewIter(nil) }), ErrClosed)) 241 242 require.NoError(t, d.Close()) 243 } 244 245 func TestSnapshotRangeDeletionStress(t *testing.T) { 246 const runs = 200 247 const middleKey = runs * runs 248 249 d, err := Open("", &Options{ 250 FS: vfs.NewMem(), 251 }) 252 require.NoError(t, err) 253 254 mkkey := func(k int) []byte { 255 return []byte(fmt.Sprintf("%08d", k)) 256 } 257 v := []byte("hello world") 258 259 snapshots := make([]*Snapshot, 0, runs) 260 for r := 0; r < runs; r++ { 261 // We use a keyspace that is 2*runs*runs wide. In other words there are 262 // 2*runs sections of the keyspace, each with runs elements. On every 263 // run, we write to the r-th element of each section of the keyspace. 264 for i := 0; i < 2*runs; i++ { 265 err := d.Set(mkkey(runs*i+r), v, nil) 266 require.NoError(t, err) 267 } 268 269 // Now we delete some of the keyspace through a DeleteRange. We delete from 270 // the middle of the keyspace outwards. The keyspace is made of 2*runs 271 // sections, and we delete an additional two of these sections per run. 272 err := d.DeleteRange(mkkey(middleKey-runs*r), mkkey(middleKey+runs*r), nil) 273 require.NoError(t, err) 274 275 snapshots = append(snapshots, d.NewSnapshot()) 276 } 277 278 // Check that all the snapshots contain the expected number of keys. 279 // Iterating over so many keys is slow, so do it in parallel. 280 var wg sync.WaitGroup 281 sem := make(chan struct{}, runtime.GOMAXPROCS(0)) 282 for r := range snapshots { 283 wg.Add(1) 284 sem <- struct{}{} 285 go func(r int) { 286 defer func() { 287 <-sem 288 wg.Done() 289 }() 290 291 // Count the keys at this snapshot. 292 iter, _ := snapshots[r].NewIter(nil) 293 var keysFound int 294 for iter.First(); iter.Valid(); iter.Next() { 295 keysFound++ 296 } 297 err := firstError(iter.Error(), iter.Close()) 298 if err != nil { 299 t.Error(err) 300 return 301 } 302 303 // At the time that this snapshot was taken, (r+1)*2*runs unique keys 304 // were Set (one in each of the 2*runs sections per run). But this 305 // run also deleted the 2*r middlemost sections. When this snapshot 306 // was taken, a Set to each of those sections had been made (r+1) 307 // times, so 2*r*(r+1) previously-set keys are now deleted. 308 309 keysExpected := (r+1)*2*runs - 2*r*(r+1) 310 if keysFound != keysExpected { 311 t.Errorf("%d: found %d keys, want %d", r, keysFound, keysExpected) 312 } 313 if err := snapshots[r].Close(); err != nil { 314 t.Error(err) 315 } 316 }(r) 317 } 318 wg.Wait() 319 require.NoError(t, d.Close()) 320 } 321 322 // TestNewSnapshotRace tests atomicity of NewSnapshot. 323 // 324 // It tests for a regression of a previous race condition in which NewSnapshot 325 // would retrieve the visible sequence number for a new snapshot before 326 // locking the database mutex to add the snapshot. A write and flush that 327 // that occurred between the reading of the sequence number and appending the 328 // snapshot could drop keys required by the snapshot. 329 func TestNewSnapshotRace(t *testing.T) { 330 const runs = 10 331 d, err := Open("", &Options{FS: vfs.NewMem()}) 332 require.NoError(t, err) 333 334 v := []byte(`foo`) 335 ch := make(chan string) 336 var wg sync.WaitGroup 337 wg.Add(1) 338 339 go func() { 340 defer wg.Done() 341 for k := range ch { 342 if err := d.Set([]byte(k), v, nil); err != nil { 343 t.Error(err) 344 return 345 } 346 if err := d.Flush(); err != nil { 347 t.Error(err) 348 return 349 } 350 } 351 }() 352 for i := 0; i < runs; i++ { 353 // This main test goroutine sets `k` before creating a new snapshot. 354 // The key `k` should always be present within the snapshot. 355 k := fmt.Sprintf("key%06d", i) 356 require.NoError(t, d.Set([]byte(k), v, nil)) 357 358 // Lock d.mu in another goroutine so that our call to NewSnapshot 359 // will need to contend for d.mu. 360 wg.Add(1) 361 locked := make(chan struct{}) 362 go func() { 363 defer wg.Done() 364 d.mu.Lock() 365 close(locked) 366 time.Sleep(20 * time.Millisecond) 367 d.mu.Unlock() 368 }() 369 <-locked 370 371 // Tell the other goroutine to overwrite `k` with a later sequence 372 // number. It's indeterminate which key we'll read, but we should 373 // always read one of them. 374 ch <- k 375 s := d.NewSnapshot() 376 _, c, err := s.Get([]byte(k)) 377 require.NoError(t, err) 378 require.NoError(t, c.Close()) 379 require.NoError(t, s.Close()) 380 } 381 close(ch) 382 wg.Wait() 383 require.NoError(t, d.Close()) 384 }