github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/open_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 "io/ioutil" 9 "os" 10 "path/filepath" 11 "reflect" 12 "sort" 13 "strconv" 14 "strings" 15 "testing" 16 17 "github.com/kr/pretty" 18 "github.com/petermattis/pebble/internal/base" 19 "github.com/petermattis/pebble/vfs" 20 "github.com/stretchr/testify/require" 21 ) 22 23 func TestErrorIfDBExists(t *testing.T) { 24 for _, b := range [...]bool{false, true} { 25 mem := vfs.NewMem() 26 d0, err := Open("", &Options{ 27 FS: mem, 28 }) 29 if err != nil { 30 t.Errorf("b=%v: d0 Open: %v", b, err) 31 continue 32 } 33 if err := d0.Close(); err != nil { 34 t.Errorf("b=%v: d0 Close: %v", b, err) 35 continue 36 } 37 38 d1, err := Open("", &Options{ 39 FS: mem, 40 ErrorIfDBExists: b, 41 }) 42 if d1 != nil { 43 defer d1.Close() 44 } 45 if got := err != nil; got != b { 46 t.Errorf("b=%v: d1 Open: err is %v, got (err != nil) is %v, want %v", b, err, got, b) 47 continue 48 } 49 } 50 } 51 52 func TestNewDBFilenames(t *testing.T) { 53 fooBar := filepath.Join("foo", "bar") 54 mem := vfs.NewMem() 55 d, err := Open(fooBar, &Options{ 56 FS: mem, 57 }) 58 if err != nil { 59 t.Fatalf("Open: %v", err) 60 } 61 if err := d.Close(); err != nil { 62 t.Fatalf("Close: %v", err) 63 } 64 got, err := mem.List(fooBar) 65 if err != nil { 66 t.Fatalf("List: %v", err) 67 } 68 sort.Strings(got) 69 want := []string{ 70 "000002.log", 71 "CURRENT", 72 "MANIFEST-000003", 73 "OPTIONS-000004", 74 } 75 if !reflect.DeepEqual(got, want) { 76 t.Errorf("\ngot %v\nwant %v", got, want) 77 } 78 } 79 80 func testOpenCloseOpenClose(t *testing.T, fs vfs.FS, root string) { 81 opts := &Options{ 82 FS: fs, 83 } 84 85 for _, startFromEmpty := range []bool{false, true} { 86 for _, walDirname := range []string{"", "wal"} { 87 for _, length := range []int{-1, 0, 1, 1000, 10000, 100000} { 88 dirname := "sharedDatabase" + walDirname 89 if startFromEmpty { 90 dirname = "startFromEmpty" + walDirname + strconv.Itoa(length) 91 } 92 dirname = filepath.Join(root, dirname) 93 if walDirname == "" { 94 opts.WALDir = "" 95 } else { 96 opts.WALDir = filepath.Join(dirname, walDirname) 97 } 98 99 got, xxx := []byte(nil), "" 100 if length >= 0 { 101 xxx = strings.Repeat("x", length) 102 } 103 104 d0, err := Open(dirname, opts) 105 if err != nil { 106 t.Fatalf("sfe=%t, length=%d: Open #0: %v", 107 startFromEmpty, length, err) 108 continue 109 } 110 if length >= 0 { 111 err = d0.Set([]byte("key"), []byte(xxx), nil) 112 if err != nil { 113 t.Errorf("sfe=%t, length=%d: Set: %v", 114 startFromEmpty, length, err) 115 continue 116 } 117 } 118 err = d0.Close() 119 if err != nil { 120 t.Errorf("sfe=%t, length=%d: Close #0: %v", 121 startFromEmpty, length, err) 122 continue 123 } 124 125 d1, err := Open(dirname, opts) 126 if err != nil { 127 t.Errorf("sfe=%t, length=%d: Open #1: %v", 128 startFromEmpty, length, err) 129 continue 130 } 131 if length >= 0 { 132 got, err = d1.Get([]byte("key")) 133 if err != nil { 134 t.Errorf("sfe=%t, length=%d: Get: %v", 135 startFromEmpty, length, err) 136 continue 137 } 138 } 139 err = d1.Close() 140 if err != nil { 141 t.Errorf("sfe=%t, length=%d: Close #1: %v", 142 startFromEmpty, length, err) 143 continue 144 } 145 146 if length >= 0 && string(got) != xxx { 147 t.Errorf("sfe=%t, length=%d: got value differs from set value", 148 startFromEmpty, length) 149 continue 150 } 151 152 { 153 got, err := opts.FS.List(dirname) 154 if err != nil { 155 t.Fatalf("List: %v", err) 156 } 157 var optionsCount int 158 for _, s := range got { 159 if t, _, ok := base.ParseFilename(s); ok && t == fileTypeOptions { 160 optionsCount++ 161 } 162 } 163 if optionsCount != 1 { 164 t.Fatalf("expected 1 OPTIONS file, but found %d", optionsCount) 165 } 166 } 167 } 168 } 169 } 170 } 171 172 func TestOpenCloseOpenClose(t *testing.T) { 173 for _, fstype := range []string{"disk", "mem"} { 174 t.Run(fstype, func(t *testing.T) { 175 var fs vfs.FS 176 var dir string 177 switch fstype { 178 case "disk": 179 var err error 180 dir, err = ioutil.TempDir("", "open-close") 181 if err != nil { 182 t.Fatal(err) 183 } 184 defer func() { 185 _ = os.RemoveAll(dir) 186 }() 187 fs = vfs.Default 188 case "mem": 189 dir = "" 190 fs = vfs.NewMem() 191 } 192 testOpenCloseOpenClose(t, fs, dir) 193 }) 194 } 195 } 196 197 func TestOpenOptionsCheck(t *testing.T) { 198 mem := vfs.NewMem() 199 opts := &Options{FS: mem} 200 201 d, err := Open("", opts) 202 if err != nil { 203 t.Fatal(err) 204 } 205 if err := d.Close(); err != nil { 206 t.Fatal(err) 207 } 208 209 opts = &Options{ 210 Comparer: &Comparer{Name: "foo"}, 211 FS: mem, 212 } 213 _, err = Open("", opts) 214 require.Regexp(t, `comparer name from file.*!=.*`, err) 215 216 opts = &Options{ 217 Merger: &Merger{Name: "bar"}, 218 FS: mem, 219 } 220 _, err = Open("", opts) 221 require.Regexp(t, `merger name from file.*!=.*`, err) 222 } 223 224 func TestOpenReadOnly(t *testing.T) { 225 mem := vfs.NewMem() 226 227 { 228 // Opening a non-existent DB in read-only mode should result in no mutable 229 // filesystem operations. 230 var buf syncedBuffer 231 _, err := Open("non-existent", &Options{ 232 FS: loggingFS{mem, &buf}, 233 ReadOnly: true, 234 WALDir: "non-existent-waldir", 235 }) 236 if err == nil { 237 t.Fatalf("expected error, but found success") 238 } 239 const expected = `open-dir: non-existent` 240 if trimmed := strings.TrimSpace(buf.String()); expected != trimmed { 241 t.Fatalf("expected %s, but found %s", expected, trimmed) 242 } 243 } 244 245 var contents []string 246 { 247 // Create a new DB and populate it with a small amount of data. 248 d, err := Open("", &Options{ 249 FS: mem, 250 }) 251 require.NoError(t, err) 252 require.NoError(t, d.Set([]byte("test"), nil, nil)) 253 require.NoError(t, d.Close()) 254 contents, err = mem.List("") 255 require.NoError(t, err) 256 sort.Strings(contents) 257 } 258 259 { 260 // Re-open the DB read-only. The directory contents should be unchanged. 261 d, err := Open("", &Options{ 262 FS: mem, 263 ReadOnly: true, 264 }) 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 // Verify various write operations fail in read-only mode. 270 require.EqualValues(t, ErrReadOnly, d.Compact(nil, nil)) 271 require.EqualValues(t, ErrReadOnly, d.Flush()) 272 require.EqualValues(t, ErrReadOnly, func() error { _, err := d.AsyncFlush(); return err }()) 273 274 require.EqualValues(t, ErrReadOnly, d.Delete(nil, nil)) 275 require.EqualValues(t, ErrReadOnly, d.DeleteRange(nil, nil, nil)) 276 require.EqualValues(t, ErrReadOnly, d.LogData(nil, nil)) 277 require.EqualValues(t, ErrReadOnly, d.Merge(nil, nil, nil)) 278 require.EqualValues(t, ErrReadOnly, d.Set(nil, nil, nil)) 279 280 // Verify we can still read in read-only mode. 281 require.NoError(t, func() error { _, err := d.Get([]byte("test")); return err }()) 282 283 checkIter := func(iter *Iterator) { 284 t.Helper() 285 286 var keys []string 287 for valid := iter.First(); valid; valid = iter.Next() { 288 keys = append(keys, string(iter.Key())) 289 } 290 require.NoError(t, iter.Close()) 291 expectedKeys := []string{"test"} 292 if diff := pretty.Diff(keys, expectedKeys); diff != nil { 293 t.Fatalf("%s\n%s", strings.Join(diff, "\n"), keys) 294 } 295 } 296 297 checkIter(d.NewIter(nil)) 298 299 b := d.NewIndexedBatch() 300 checkIter(b.NewIter(nil)) 301 require.EqualValues(t, ErrReadOnly, b.Commit(nil)) 302 require.EqualValues(t, ErrReadOnly, d.Apply(b, nil)) 303 304 s := d.NewSnapshot() 305 checkIter(s.NewIter(nil)) 306 require.NoError(t, s.Close()) 307 308 require.NoError(t, d.Close()) 309 310 newContents, err := mem.List("") 311 require.NoError(t, err) 312 313 sort.Strings(newContents) 314 if diff := pretty.Diff(contents, newContents); diff != nil { 315 t.Fatalf("%s", strings.Join(diff, "\n")) 316 } 317 } 318 }