github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/replay/replay.go (about)

     1  // Copyright 2020 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 replay implements facilities for replaying writes to a database.
     6  package replay
     7  
     8  import (
     9  	"sort"
    10  
    11  	"github.com/zuoyebang/bitalostable"
    12  	"github.com/zuoyebang/bitalostable/internal/manifest"
    13  	"github.com/zuoyebang/bitalostable/internal/private"
    14  )
    15  
    16  // Table describes a sstable from the reference database whose writes are
    17  // being replayed.
    18  type Table struct {
    19  	Path         string
    20  	FileMetadata *manifest.FileMetadata
    21  }
    22  
    23  type bySeqNum []Table
    24  
    25  func (s bySeqNum) Len() int      { return len(s) }
    26  func (s bySeqNum) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    27  func (s bySeqNum) Less(i, j int) bool {
    28  	return s[i].FileMetadata.SmallestSeqNum < s[j].FileMetadata.SmallestSeqNum
    29  }
    30  
    31  // Open opens a database for replaying flushed and ingested tables. It's
    32  // intended for use by the `bitalostable bench compact` command.
    33  func Open(dirname string, opts *bitalostable.Options) (*DB, error) {
    34  	d, err := bitalostable.Open(dirname, opts)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return &DB{d: d}, nil
    39  }
    40  
    41  // A DB is a wrapper around a Pebble database for replaying table-level
    42  // writes to a database. It is not safe for concurrent access.
    43  type DB struct {
    44  	done bool
    45  	d    *bitalostable.DB
    46  }
    47  
    48  // Ingest ingests a set of sstables into the DB.
    49  func (d *DB) Ingest(tables []Table) error {
    50  	if d.done {
    51  		panic("replay already finished")
    52  	}
    53  	if len(tables) == 0 {
    54  		return nil
    55  	}
    56  
    57  	// Sort the tables by their assigned sequence numbers so that we pass them
    58  	// to Ingest in the same order and ultimately assign sequence numbers in
    59  	// the same order.
    60  	sort.Sort(bySeqNum(tables))
    61  
    62  	// The replay database's current allocated sequence number might be less
    63  	// than it was at the time of this ingest in the reference database
    64  	// because only overlapping memtables are flushed before an ingest.
    65  	// We need the ingested files to adopt the same sequence numbers as they
    66  	// originally did to ensure the sequence number invariants still hold.
    67  	// To accommodate this, we bump the sequence number up to just below the
    68  	// sequence number these files' were originally ingested at.
    69  	smallest := tables[0].FileMetadata.SmallestSeqNum
    70  	private.RatchetSeqNum(d.d, smallest)
    71  
    72  	paths := make([]string, len(tables))
    73  	for i, tbl := range tables {
    74  		paths[i] = tbl.Path
    75  	}
    76  	return d.d.Ingest(paths)
    77  }
    78  
    79  // FlushExternal simulates a flush of the table, linking it directly
    80  // into level zero.
    81  func (d *DB) FlushExternal(tbl Table) error {
    82  	if d.done {
    83  		panic("replay already finished")
    84  	}
    85  	return private.FlushExternalTable(d.d, tbl.Path, tbl.FileMetadata)
    86  }
    87  
    88  // Metrics returns the underlying DB's Metrics.
    89  func (d *DB) Metrics() *bitalostable.Metrics {
    90  	if d.done {
    91  		panic("replay already finished")
    92  	}
    93  	return d.d.Metrics()
    94  }
    95  
    96  // Done finishes a replay, returning the underlying database.  All of the
    97  // *replay.DB's methods except Close will error if called after Done.
    98  func (d *DB) Done() *bitalostable.DB {
    99  	if d.done {
   100  		panic("replay already finished")
   101  	}
   102  	d.done = true
   103  	return d.d
   104  }
   105  
   106  // Close closes a replay and the underlying database.
   107  // If Close is called after Done, Close does nothing.
   108  func (d *DB) Close() error {
   109  	if d.done {
   110  		// Allow clients to defer Close()
   111  		return nil
   112  	}
   113  	d.done = true
   114  	return d.d.Close()
   115  }