github.com/scottcagno/storage@v1.8.0/pkg/_junk/_memtable/memtable.go (about)

     1  package memtable
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/scottcagno/storage/pkg/lsmt/binary"
     7  	"github.com/scottcagno/storage/pkg/lsmt/trees/rbtree"
     8  	"github.com/scottcagno/storage/pkg/lsmt/wal"
     9  	"os"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  )
    14  
    15  var Tombstone = []byte(nil)
    16  
    17  type MemtableEntry = memtableEntry
    18  
    19  type memtableEntry struct {
    20  	Key   string
    21  	Entry *binary.Entry
    22  }
    23  
    24  func (me memtableEntry) Compare(that rbtree.RBEntry) int {
    25  	return strings.Compare(me.Key, that.(memtableEntry).Key)
    26  }
    27  
    28  func (me memtableEntry) Size() int {
    29  	return len(me.Key) + len(me.Entry.Key) + len(me.Entry.Value)
    30  }
    31  
    32  func (me memtableEntry) String() string {
    33  	return fmt.Sprintf("entry.key=%q", me.Key)
    34  }
    35  
    36  const (
    37  	defaultBasePath       = "log"
    38  	defaultFlushThreshold = 1 << 20 // 1 MB
    39  	defaultSyncOnWrite    = false
    40  )
    41  
    42  var defaultMemtableConfig = &MemtableConfig{
    43  	BasePath:       defaultBasePath,
    44  	FlushThreshold: defaultFlushThreshold,
    45  	SyncOnWrite:    defaultSyncOnWrite,
    46  }
    47  
    48  type MemtableConfig struct {
    49  	BasePath       string // base storage path
    50  	FlushThreshold int64  // memtable flush threshold in KB
    51  	SyncOnWrite    bool   // perform sync every time an entry is write
    52  }
    53  
    54  func checkMemtableConfig(conf *MemtableConfig) *MemtableConfig {
    55  	if conf == nil {
    56  		return defaultMemtableConfig
    57  	}
    58  	if conf.BasePath == *new(string) {
    59  		conf.BasePath = defaultBasePath
    60  	}
    61  	if conf.FlushThreshold < 1 {
    62  		conf.FlushThreshold = defaultFlushThreshold
    63  	}
    64  	return conf
    65  }
    66  
    67  type Memtable struct {
    68  	lock sync.RWMutex
    69  	conf *MemtableConfig
    70  	data *rbtree.RBTree
    71  	wacl *wal.WAL
    72  }
    73  
    74  func OpenMemtable(c *MemtableConfig) (*Memtable, error) {
    75  	// check memtable config
    76  	conf := checkMemtableConfig(c)
    77  	// open write-ahead commit log
    78  	wacl, err := wal.OpenWAL(&wal.WALConfig{
    79  		BasePath:    conf.BasePath,
    80  		MaxFileSize: -1, // use wal defaultMaxFileSize
    81  		SyncOnWrite: conf.SyncOnWrite,
    82  	})
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	// create new memtable
    87  	memt := &Memtable{
    88  		conf: conf,
    89  		data: rbtree.NewRBTree(),
    90  		wacl: wacl,
    91  	}
    92  	// load mem-table entries from commit log
    93  	err = memt.loadDataFromCommitLog()
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return memt, nil
    98  }
    99  
   100  // loadEntries loads any entries from the supplied segmented file back into the memtable
   101  func (mt *Memtable) loadDataFromCommitLog() error {
   102  	// lock
   103  	mt.lock.Lock()
   104  	defer mt.lock.Unlock()
   105  	return mt.wacl.Scan(func(e *binary.Entry) bool {
   106  		mt.data.Put(memtableEntry{Key: string(e.Key), Entry: e})
   107  		return true
   108  	})
   109  }
   110  
   111  func (mt *Memtable) Reset() error {
   112  	// lock
   113  	mt.lock.Lock()
   114  	defer mt.lock.Unlock()
   115  	// grab current configuration
   116  	//walConf := mt.wacl.GetConfig()
   117  	// reset and close
   118  	//err := mt.wacl.ResetAndClose()
   119  	//if err != nil {
   120  	//	return err
   121  	//}
   122  	// open fresh write-ahead commit log
   123  	//mt.wacl, err = wal.OpenWAL(walConf)
   124  	//if err != nil {
   125  	//	return err
   126  	//}
   127  	//// truncate all the log files
   128  	//err := mt.wacl.TruncateFront(mt.wacl.LastIndex())
   129  	//if err != nil {
   130  	//	return err
   131  	//}
   132  	// reset tree data in the mem-table
   133  	mt.data.Reset()
   134  	return nil
   135  }
   136  
   137  func (mt *Memtable) ResetWorksWithTimer() error {
   138  	// grab current configuration
   139  	walConf := mt.wacl.GetConfig()
   140  	// close write-ahead commit log
   141  	err := mt.wacl.Close()
   142  	if err != nil {
   143  		return err
   144  	}
   145  	// putting this in here (for now, fixes this bug) so weird
   146  	time.Sleep(1 * time.Millisecond)
   147  	// wipe write-ahead commit log segments
   148  	err = os.RemoveAll(mt.conf.BasePath)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	// open fresh write-ahead commit log
   153  	mt.wacl, err = wal.OpenWAL(walConf)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	// reset tree data
   158  	mt.data.Reset()
   159  	return nil
   160  }
   161  
   162  func (mt *Memtable) insert(e *binary.Entry) error {
   163  	mt.data.Put(memtableEntry{Key: string(e.Key), Entry: e})
   164  	if mt.data.Size() > mt.conf.FlushThreshold {
   165  		return ErrFlushThreshold
   166  	}
   167  	return nil
   168  }
   169  
   170  func (mt *Memtable) Size() int64 {
   171  	// lock
   172  	mt.lock.Lock()
   173  	defer mt.lock.Unlock()
   174  	return mt.data.Size()
   175  }
   176  
   177  func (mt *Memtable) ShouldFlush() bool {
   178  	// lock
   179  	mt.lock.Lock()
   180  	defer mt.lock.Unlock()
   181  	return mt.data.Size() > mt.conf.FlushThreshold
   182  }
   183  
   184  func (mt *Memtable) Put(e *binary.Entry) error {
   185  	// lock
   186  	mt.lock.Lock()
   187  	defer mt.lock.Unlock()
   188  	// write entry to the write-ahead commit log
   189  	_, err := mt.wacl.Write(e)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	// write entry to the mem-table
   194  	err = mt.insert(e)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	return nil
   199  }
   200  
   201  func (mt *Memtable) PutBatch(batch *binary.Batch) error {
   202  	// lock
   203  	mt.lock.Lock()
   204  	defer mt.lock.Unlock()
   205  	// write batch to the write-ahead commit log
   206  	err := mt.wacl.WriteBatch(batch)
   207  	if err != nil {
   208  		return err
   209  	}
   210  	// write batch entries to the mem-table
   211  	for i := range batch.Entries {
   212  		e := batch.Entries[i]
   213  		mt.data.Put(memtableEntry{Key: string(e.Key), Entry: e})
   214  	}
   215  	// after batch writing is finished, check
   216  	// and return to flush or not to flush
   217  	if mt.data.Size() > mt.conf.FlushThreshold {
   218  		return ErrFlushThreshold
   219  	}
   220  	return nil
   221  }
   222  
   223  func (mt *Memtable) Has(k string) bool {
   224  	// read lock
   225  	mt.lock.RLock()
   226  	defer mt.lock.RUnlock()
   227  	return mt.data.Has(memtableEntry{Key: k})
   228  }
   229  
   230  func (mt *Memtable) Get(k string) (*binary.Entry, error) {
   231  	// read lock
   232  	mt.lock.RLock()
   233  	defer mt.lock.RUnlock()
   234  	v, ok := mt.data.Get(memtableEntry{Key: k})
   235  	if !ok {
   236  		return nil, ErrKeyNotFound
   237  	}
   238  	if v.(memtableEntry).Entry == nil || bytes.Equal(v.(memtableEntry).Entry.Value, Tombstone) {
   239  		return nil, ErrFoundTombstone
   240  	}
   241  	return v.(memtableEntry).Entry, nil
   242  }
   243  
   244  func (mt *Memtable) Del(k string) error {
   245  	// lock
   246  	mt.lock.Lock()
   247  	defer mt.lock.Unlock()
   248  	// create delete entry
   249  	e := &binary.Entry{Key: []byte(k), Value: Tombstone}
   250  	// write entry to the write-ahead commit log
   251  	_, err := mt.wacl.Write(e)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	// write entry to the mem-table
   256  	err = mt.insert(e)
   257  	if err != nil {
   258  		return err
   259  	}
   260  	return nil
   261  }
   262  
   263  func (mt *Memtable) Scan(iter func(me rbtree.RBEntry) bool) {
   264  	// lock
   265  	mt.lock.Lock()
   266  	defer mt.lock.Unlock()
   267  	if mt.data.Len() < 1 {
   268  		return
   269  	}
   270  	mt.data.Scan(iter)
   271  }
   272  
   273  func (mt *Memtable) Len() int {
   274  	// lock
   275  	mt.lock.Lock()
   276  	defer mt.lock.Unlock()
   277  	return mt.data.Len()
   278  }
   279  
   280  func (mt *Memtable) GetConfig() *MemtableConfig {
   281  	// lock
   282  	mt.lock.Lock()
   283  	defer mt.lock.Unlock()
   284  	return mt.conf
   285  }
   286  
   287  func (mt *Memtable) Sync() error {
   288  	// lock
   289  	mt.lock.Lock()
   290  	defer mt.lock.Unlock()
   291  	return mt.wacl.Sync()
   292  }
   293  
   294  func (mt *Memtable) Close() error {
   295  	// lock
   296  	mt.lock.Lock()
   297  	defer mt.lock.Unlock()
   298  	mt.data.Close()
   299  	err := mt.wacl.Close()
   300  	if err != nil {
   301  		return err
   302  	}
   303  	return nil
   304  }
   305  
   306  func (mt *Memtable) CloseAndRemove() error {
   307  	// lock
   308  	mt.lock.Lock()
   309  	defer mt.lock.Unlock()
   310  	mt.data.Close()
   311  	err := mt.wacl.Close()
   312  	if err != nil {
   313  		return err
   314  	}
   315  	return nil
   316  }