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  }