github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/data_test.go (about) 1 // Copyright 2018 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 "strconv" 11 "strings" 12 13 "github.com/petermattis/pebble/internal/base" 14 "github.com/petermattis/pebble/internal/datadriven" 15 "github.com/petermattis/pebble/vfs" 16 ) 17 18 type iterCmdOpt int 19 20 const ( 21 iterCmdVerboseKey iterCmdOpt = iota 22 ) 23 24 func checkValidPrefix(prefix, key []byte) bool { 25 return prefix == nil || bytes.HasPrefix(key, prefix) 26 } 27 28 func runIterCmd(d *datadriven.TestData, iter *Iterator) string { 29 var b bytes.Buffer 30 for _, line := range strings.Split(d.Input, "\n") { 31 parts := strings.Fields(line) 32 if len(parts) == 0 { 33 continue 34 } 35 var valid bool 36 switch parts[0] { 37 case "seek-ge": 38 if len(parts) != 2 { 39 return fmt.Sprintf("seek-ge <key>\n") 40 } 41 valid = iter.SeekGE([]byte(strings.TrimSpace(parts[1]))) 42 case "seek-prefix-ge": 43 if len(parts) != 2 { 44 return fmt.Sprintf("seek-prefix-ge <key>\n") 45 } 46 valid = iter.SeekPrefixGE([]byte(strings.TrimSpace(parts[1]))) 47 case "seek-lt": 48 if len(parts) != 2 { 49 return fmt.Sprintf("seek-lt <key>\n") 50 } 51 valid = iter.SeekLT([]byte(strings.TrimSpace(parts[1]))) 52 case "first": 53 valid = iter.First() 54 case "last": 55 valid = iter.Last() 56 case "next": 57 valid = iter.Next() 58 case "prev": 59 valid = iter.Prev() 60 case "set-bounds": 61 if len(parts) <= 1 || len(parts) > 3 { 62 return fmt.Sprintf("set-bounds lower=<lower> upper=<upper>\n") 63 } 64 var lower []byte 65 var upper []byte 66 for _, part := range parts[1:] { 67 arg := strings.Split(strings.TrimSpace(part), "=") 68 switch arg[0] { 69 case "lower": 70 lower = []byte(arg[1]) 71 case "upper": 72 upper = []byte(arg[1]) 73 default: 74 return fmt.Sprintf("set-bounds: unknown arg: %s", arg) 75 } 76 } 77 iter.SetBounds(lower, upper) 78 valid = iter.Valid() 79 default: 80 return fmt.Sprintf("unknown op: %s", parts[0]) 81 } 82 if err := iter.Error(); err != nil { 83 fmt.Fprintf(&b, "err=%v\n", err) 84 } else if valid != iter.Valid() { 85 fmt.Fprintf(&b, "mismatched valid states: %t vs %t\n", valid, iter.Valid()) 86 } else if valid { 87 fmt.Fprintf(&b, "%s:%s\n", iter.Key(), iter.Value()) 88 } else { 89 fmt.Fprintf(&b, ".\n") 90 } 91 } 92 return b.String() 93 } 94 95 func runInternalIterCmd(d *datadriven.TestData, iter internalIterator, opts ...iterCmdOpt) string { 96 var verboseKey bool 97 for _, opt := range opts { 98 if opt == iterCmdVerboseKey { 99 verboseKey = true 100 } 101 } 102 103 var b bytes.Buffer 104 var prefix []byte 105 for _, line := range strings.Split(d.Input, "\n") { 106 parts := strings.Fields(line) 107 if len(parts) == 0 { 108 continue 109 } 110 switch parts[0] { 111 case "seek-ge": 112 if len(parts) != 2 { 113 return fmt.Sprintf("seek-ge <key>\n") 114 } 115 prefix = nil 116 iter.SeekGE([]byte(strings.TrimSpace(parts[1]))) 117 case "seek-prefix-ge": 118 if len(parts) != 2 { 119 return fmt.Sprintf("seek-prefix-ge <key>\n") 120 } 121 prefix = []byte(strings.TrimSpace(parts[1])) 122 iter.SeekPrefixGE(prefix, prefix /* key */) 123 case "seek-lt": 124 if len(parts) != 2 { 125 return fmt.Sprintf("seek-lt <key>\n") 126 } 127 prefix = nil 128 iter.SeekLT([]byte(strings.TrimSpace(parts[1]))) 129 case "first": 130 prefix = nil 131 iter.First() 132 case "last": 133 prefix = nil 134 iter.Last() 135 case "next": 136 iter.Next() 137 case "prev": 138 iter.Prev() 139 case "set-bounds": 140 if len(parts) <= 1 || len(parts) > 3 { 141 return fmt.Sprintf("set-bounds lower=<lower> upper=<upper>\n") 142 } 143 var lower []byte 144 var upper []byte 145 for _, part := range parts[1:] { 146 arg := strings.Split(strings.TrimSpace(part), "=") 147 switch arg[0] { 148 case "lower": 149 lower = []byte(arg[1]) 150 case "upper": 151 upper = []byte(arg[1]) 152 default: 153 return fmt.Sprintf("set-bounds: unknown arg: %s", arg) 154 } 155 } 156 iter.SetBounds(lower, upper) 157 continue 158 default: 159 return fmt.Sprintf("unknown op: %s", parts[0]) 160 } 161 if iter.Valid() && checkValidPrefix(prefix, iter.Key().UserKey) { 162 if verboseKey { 163 fmt.Fprintf(&b, "%s:%s\n", iter.Key(), iter.Value()) 164 } else { 165 fmt.Fprintf(&b, "%s:%s\n", iter.Key().UserKey, iter.Value()) 166 } 167 } else if err := iter.Error(); err != nil { 168 fmt.Fprintf(&b, "err=%v\n", err) 169 } else { 170 fmt.Fprintf(&b, ".\n") 171 } 172 } 173 return b.String() 174 } 175 176 func runBatchDefineCmd(d *datadriven.TestData, b *Batch) error { 177 for _, line := range strings.Split(d.Input, "\n") { 178 parts := strings.Fields(line) 179 if len(parts) == 0 { 180 continue 181 } 182 if parts[1] == `<nil>` { 183 parts[1] = "" 184 } 185 var err error 186 switch parts[0] { 187 case "set": 188 if len(parts) != 3 { 189 return fmt.Errorf("%s expects 2 arguments", parts[0]) 190 } 191 err = b.Set([]byte(parts[1]), []byte(parts[2]), nil) 192 case "del": 193 if len(parts) != 2 { 194 return fmt.Errorf("%s expects 1 argument", parts[0]) 195 } 196 err = b.Delete([]byte(parts[1]), nil) 197 case "del-range": 198 if len(parts) != 3 { 199 return fmt.Errorf("%s expects 2 arguments", parts[0]) 200 } 201 err = b.DeleteRange([]byte(parts[1]), []byte(parts[2]), nil) 202 case "merge": 203 if len(parts) != 3 { 204 return fmt.Errorf("%s expects 2 arguments", parts[0]) 205 } 206 err = b.Merge([]byte(parts[1]), []byte(parts[2]), nil) 207 default: 208 return fmt.Errorf("unknown op: %s", parts[0]) 209 } 210 if err != nil { 211 return err 212 } 213 } 214 return nil 215 } 216 217 func runCompactCommand(td *datadriven.TestData, d *DB) error { 218 if len(td.CmdArgs) > 2 { 219 return fmt.Errorf("%s expects at most two arguments", td.Cmd) 220 } 221 parts := strings.Split(td.CmdArgs[0].Key, "-") 222 if len(parts) != 2 { 223 return fmt.Errorf("expected <begin>-<end>: %s", td.Input) 224 } 225 if len(td.CmdArgs) == 2 { 226 levelString := td.CmdArgs[1].String() 227 iStart := base.MakeInternalKey([]byte(parts[0]), InternalKeySeqNumMax, InternalKeyKindMax) 228 iEnd := base.MakeInternalKey([]byte(parts[1]), 0, 0) 229 if levelString[0] != 'L' { 230 return fmt.Errorf("expected L<n>: %s", levelString) 231 } 232 level, err := strconv.Atoi(levelString[1:]) 233 if err != nil { 234 return err 235 } 236 return d.manualCompact(&manualCompaction{ 237 done: make(chan error, 1), 238 level: level, 239 start: iStart, 240 end: iEnd, 241 }) 242 } 243 return d.Compact([]byte(parts[0]), []byte(parts[1])) 244 } 245 246 func runDBDefineCmd(td *datadriven.TestData, opts *Options) (*DB, error) { 247 if td.Input == "" { 248 return nil, fmt.Errorf("empty test input") 249 } 250 251 opts = opts.EnsureDefaults() 252 opts.FS = vfs.NewMem() 253 254 var snapshots []uint64 255 for _, arg := range td.CmdArgs { 256 switch arg.Key { 257 case "target-file-sizes": 258 opts.Levels = make([]LevelOptions, len(arg.Vals)) 259 for i := range arg.Vals { 260 size, err := strconv.ParseInt(arg.Vals[i], 10, 64) 261 if err != nil { 262 return nil, err 263 } 264 opts.Levels[i].TargetFileSize = size 265 } 266 case "snapshots": 267 snapshots = make([]uint64, len(arg.Vals)) 268 for i := range arg.Vals { 269 seqNum, err := strconv.ParseUint(arg.Vals[i], 10, 64) 270 if err != nil { 271 return nil, err 272 } 273 snapshots[i] = seqNum 274 if i > 0 && snapshots[i] < snapshots[i-1] { 275 return nil, fmt.Errorf("Snapshots must be in ascending order") 276 } 277 } 278 default: 279 return nil, fmt.Errorf("%s: unknown arg: %s", td.Cmd, arg.Key) 280 } 281 } 282 d, err := Open("", opts) 283 if err != nil { 284 return nil, err 285 } 286 d.mu.Lock() 287 d.mu.versions.dynamicBaseLevel = false 288 for i := range snapshots { 289 s := &Snapshot{db: d} 290 s.seqNum = snapshots[i] 291 d.mu.snapshots.pushBack(s) 292 } 293 defer d.mu.Unlock() 294 295 var mem *memTable 296 ve := &versionEdit{} 297 level := -1 298 299 maybeFlush := func() error { 300 if level < 0 { 301 return nil 302 } 303 304 c := newFlush(d.opts, d.mu.versions.currentVersion(), 305 d.mu.versions.picker.baseLevel, []flushable{mem}, &d.bytesFlushed) 306 c.disableRangeTombstoneElision = true 307 newVE, _, err := d.runCompaction(0, c, nilPacer) 308 if err != nil { 309 return nil 310 } 311 for _, f := range newVE.NewFiles { 312 ve.NewFiles = append(ve.NewFiles, newFileEntry{ 313 Level: level, 314 Meta: f.Meta, 315 }) 316 } 317 level = -1 318 return nil 319 } 320 321 for _, line := range strings.Split(td.Input, "\n") { 322 fields := strings.Fields(line) 323 if len(fields) > 0 { 324 switch fields[0] { 325 case "mem": 326 if err := maybeFlush(); err != nil { 327 return nil, err 328 } 329 // Add a memtable layer. 330 if !d.mu.mem.mutable.empty() { 331 d.mu.mem.mutable = newMemTable(d.opts) 332 d.mu.mem.queue = append(d.mu.mem.queue, d.mu.mem.mutable) 333 d.updateReadStateLocked() 334 } 335 mem = d.mu.mem.mutable 336 fields = fields[1:] 337 case "L0", "L1", "L2", "L3", "L4", "L5", "L6": 338 if err := maybeFlush(); err != nil { 339 return nil, err 340 } 341 var err error 342 if level, err = strconv.Atoi(fields[0][1:]); err != nil { 343 return nil, err 344 } 345 fields = fields[1:] 346 mem = newMemTable(d.opts) 347 } 348 } 349 350 for _, data := range fields { 351 i := strings.Index(data, ":") 352 key := base.ParseInternalKey(data[:i]) 353 value := []byte(data[i+1:]) 354 if err := mem.set(key, value); err != nil { 355 return nil, err 356 } 357 } 358 } 359 360 if err := maybeFlush(); err != nil { 361 return nil, err 362 } 363 364 if len(ve.NewFiles) > 0 { 365 jobID := d.mu.nextJobID 366 d.mu.nextJobID++ 367 if err := d.mu.versions.logAndApply(jobID, ve, nil, d.dataDir); err != nil { 368 return nil, err 369 } 370 d.updateReadStateLocked() 371 for i := range ve.NewFiles { 372 meta := &ve.NewFiles[i].Meta 373 delete(d.mu.compact.pendingOutputs, meta.FileNum) 374 } 375 } 376 377 return d, nil 378 }