// NewIndexFile returns a new handle to the named index file. func NewIndexFile(filename string) (*IndexFile, error) { v(1, "opening index %q", filename) f, err := os.Open(filename) if err != nil { return nil, fmt.Errorf("error opening file %q: %v", filename, err) } ss := table.NewReader(f, nil) if versions, err := ss.Get([]byte{0}, nil); err != nil { return nil, fmt.Errorf("invalid index file %q missing versions record: %v", filename, err) } else if len(versions) != 8 { return nil, fmt.Errorf("invalid index file %q invalid versions record: %v", filename, versions) } else if major, minor := binary.BigEndian.Uint32(versions[:4]), binary.BigEndian.Uint32(versions[4:]); major != majorVersionNumber { return nil, fmt.Errorf("invalid index file %q: version mismatch, want %d got %d", filename, majorVersionNumber, major) } else { v(3, "index file %q has file format version %d:%d", filename, major, minor) } if *base.VerboseLogging >= 10 { iter := ss.Find([]byte{}, nil) v(4, "=== %q ===", filename) for iter.Next() { v(4, " %v", iter.Key()) } v(4, " ERR: %v", iter.Close()) } index := &IndexFile{ss: ss, name: filename} return index, nil }
func (n *tableCacheNode) load(c *tableCache) { // Try opening the fileTypeTable first. If that file doesn't exist, // fall back onto the fileTypeOldFashionedTable. f, err := c.fs.Open(dbFilename(c.dirname, fileTypeTable, n.fileNum)) if os.IsNotExist(err) { f, err = c.fs.Open(dbFilename(c.dirname, fileTypeOldFashionedTable, n.fileNum)) } if err != nil { n.result <- tableReaderOrError{err: err} return } n.result <- tableReaderOrError{reader: table.NewReader(f, c.opts)} }
func dump(filename string) error { f, err := os.Open(filename) if err != nil { return err } // No need to "defer f.Close()", as closing r will close f. r := table.NewReader(f, &db.Options{ VerifyChecksums: *verifyChecksums, }) defer r.Close() t := r.Find(nil, nil) for t.Next() { k, v := t.Key(), t.Value() if *truncate { k = trunc(&kBuf, k) v = trunc(&vBuf, v) } fmt.Printf("%q: %q,\n", k, v) } return t.Close() }
func TestCompaction(t *testing.T) { const writeBufferSize = 1000 fs := memfs.New() d, err := Open("", &db.Options{ FileSystem: fs, WriteBufferSize: writeBufferSize, }) if err != nil { t.Fatalf("Open: %v", err) } get1 := func(x db.DB) (ret string) { b := &bytes.Buffer{} iter := x.Find(nil, nil) for iter.Next() { b.Write(internalKey(iter.Key()).ukey()) } if err := iter.Close(); err != nil { t.Fatalf("iterator Close: %v", err) } return b.String() } getAll := func() (gotMem, gotDisk string, err error) { d.mu.Lock() defer d.mu.Unlock() if d.mem != nil { gotMem = get1(d.mem) } ss := []string(nil) v := d.versions.currentVersion() for _, files := range v.files { for _, meta := range files { f, err := fs.Open(dbFilename("", fileTypeTable, meta.fileNum)) if err != nil { return "", "", fmt.Errorf("Open: %v", err) } defer f.Close() r := table.NewReader(f, &db.Options{ Comparer: internalKeyComparer{db.DefaultComparer}, }) defer r.Close() ss = append(ss, get1(r)+".") } } sort.Strings(ss) return gotMem, strings.Join(ss, ""), nil } value := bytes.Repeat([]byte("x"), writeBufferSize*6/10) testCases := []struct { key, wantMem, wantDisk string }{ {"+A", "A", ""}, {"+a", "Aa", ""}, {"+B", "B", "Aa."}, {"+b", "Bb", "Aa."}, // The next level-0 table overwrites the B key. {"+C", "C", "Aa.Bb."}, {"+B", "BC", "Aa.Bb."}, // The next level-0 table deletes the a key. {"+D", "D", "Aa.BC.Bb."}, {"-a", "Da", "Aa.BC.Bb."}, {"+d", "Dad", "Aa.BC.Bb."}, // The next addition creates the fourth level-0 table, and l0CompactionTrigger == 4, // so this triggers a non-trivial compaction into one level-1 table. Note that the // keys in this one larger table are interleaved from the four smaller ones. {"+E", "E", "ABCDbd."}, {"+e", "Ee", "ABCDbd."}, {"+F", "F", "ABCDbd.Ee."}, } for _, tc := range testCases { if key := tc.key[1:]; tc.key[0] == '+' { if err := d.Set([]byte(key), value, nil); err != nil { t.Errorf("%q: Set: %v", key, err) break } } else { if err := d.Delete([]byte(key), nil); err != nil { t.Errorf("%q: Delete: %v", key, err) break } } // try backs off to allow any writes to the memfs to complete. err := try(100*time.Microsecond, 20*time.Second, func() error { gotMem, gotDisk, err := getAll() if err != nil { return err } if gotMem != tc.wantMem { return fmt.Errorf("mem: got %q, want %q", gotMem, tc.wantMem) } if gotDisk != tc.wantDisk { return fmt.Errorf("ldb: got %q, want %q", gotDisk, tc.wantDisk) } return nil }) if err != nil { t.Errorf("%q: %v", tc.key, err) } } if err := d.Close(); err != nil { t.Fatalf("db Close: %v", err) } }