github.com/blong14/gache@v0.0.0-20240124023949-89416fd8bbfa/internal/db/table.go (about)

     1  package db
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"path"
     9  	"sync"
    10  
    11  	gmtable "github.com/blong14/gache/internal/db/memtable"
    12  	gstable "github.com/blong14/gache/internal/db/sstable"
    13  	gwal "github.com/blong14/gache/internal/db/wal"
    14  	gfile "github.com/blong14/gache/internal/io/file"
    15  )
    16  
    17  type Table interface {
    18  	Get(k []byte) ([]byte, bool)
    19  	Set(k, v []byte) error
    20  	Scan(s, e []byte) ([][][]byte, bool)
    21  	ScanWithLimit(s, e []byte, l int) ([][][]byte, bool)
    22  	Range(func(k, v []byte) bool)
    23  	Print()
    24  	Connect() error
    25  	Count() uint64
    26  	Close()
    27  }
    28  
    29  type TableOpts struct {
    30  	TableName []byte
    31  	DataDir   []byte
    32  	InMemory  bool
    33  	WalMode   bool
    34  }
    35  
    36  type fileDatabase struct {
    37  	dir      string
    38  	name     string
    39  	memtable *gmtable.MemTable
    40  	sstable  *gstable.SSTable
    41  	handle   *os.File
    42  	wal      *gwal.WAL
    43  	useWal   bool
    44  	onSet    chan struct{}
    45  }
    46  
    47  func New(opts *TableOpts) Table {
    48  	if opts.InMemory {
    49  		return &inMemoryDatabase{
    50  			name:     string(opts.TableName),
    51  			memtable: gmtable.New(),
    52  		}
    53  	}
    54  	db := &fileDatabase{
    55  		dir:      string(opts.DataDir),
    56  		name:     string(opts.TableName),
    57  		memtable: gmtable.New(),
    58  		useWal:   opts.WalMode,
    59  		onSet:    make(chan struct{}),
    60  	}
    61  	if err := db.Connect(); err != nil {
    62  		panic(err)
    63  	}
    64  	return db
    65  }
    66  
    67  var once sync.Once
    68  
    69  func (db *fileDatabase) Connect() error {
    70  	once.Do(func() {
    71  		f, err := gfile.NewDatFile(db.dir, db.name)
    72  		if err != nil {
    73  			panic(err)
    74  		}
    75  		db.handle = f
    76  		db.sstable = gstable.New(db.handle)
    77  		file := fmt.Sprintf("%s-wal.dat", db.name)
    78  		p := path.Join(db.dir, file)
    79  		f, err = os.OpenFile(p, os.O_CREATE|os.O_RDWR, 0644)
    80  		if err != nil {
    81  			panic(err)
    82  		}
    83  		db.wal = gwal.New(f)
    84  	})
    85  	return nil
    86  }
    87  
    88  func (db *fileDatabase) Get(k []byte) ([]byte, bool) {
    89  	value, ok := db.memtable.Get(k)
    90  	if ok {
    91  		return value, true
    92  	}
    93  	return db.sstable.Get(k)
    94  }
    95  
    96  func (db *fileDatabase) Count() uint64 {
    97  	return db.memtable.Count()
    98  }
    99  
   100  func (db *fileDatabase) Set(k, v []byte) error {
   101  	if db.useWal {
   102  		if err := db.wal.Set(k, v); err != nil {
   103  			return err
   104  		}
   105  	}
   106  	if err := db.memtable.Set(k, v); err != nil {
   107  		if errors.Is(err, gmtable.ErrAllowedBytesExceeded) {
   108  			go func() {
   109  				if db.sstable != nil {
   110  					_ = db.memtable.Flush(db.sstable)
   111  				}
   112  			}()
   113  		} else {
   114  			return err
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  func (db *fileDatabase) Close() {
   121  	if db.sstable != nil {
   122  		err := db.memtable.Flush(db.sstable)
   123  		if err != nil {
   124  			log.Println(err)
   125  		}
   126  		db.sstable.Free()
   127  	}
   128  }
   129  
   130  func (db *fileDatabase) Print() {}
   131  
   132  func (db *fileDatabase) Range(fnc func(k, v []byte) bool) {
   133  	db.memtable.Range(fnc)
   134  }
   135  
   136  func (db *fileDatabase) Scan(s, e []byte) ([][][]byte, bool) {
   137  	out := make([][][]byte, 0)
   138  	db.memtable.Scan(s, e, func(k, v []byte) bool {
   139  		out = append(out, [][]byte{k, v})
   140  		return true
   141  	})
   142  	return out, true
   143  }
   144  
   145  func (db *fileDatabase) ScanWithLimit(s, e []byte, limit int) ([][][]byte, bool) {
   146  	out := make([][][]byte, 0)
   147  	db.memtable.Scan(s, e, func(k, v []byte) bool {
   148  		out = append(out, [][]byte{k, v})
   149  		if limit > 0 && len(out) >= limit {
   150  			return false
   151  		}
   152  		return true
   153  	})
   154  	return out, true
   155  }
   156  
   157  type inMemoryDatabase struct {
   158  	name     string
   159  	memtable *gmtable.MemTable
   160  }
   161  
   162  func (db *inMemoryDatabase) Get(k []byte) ([]byte, bool) {
   163  	value, ok := db.memtable.Get(k)
   164  	return value, ok
   165  }
   166  
   167  func (db *inMemoryDatabase) Set(k, v []byte) error {
   168  	return db.memtable.Set(k, v)
   169  }
   170  
   171  func (db *inMemoryDatabase) Scan(s, e []byte) ([][][]byte, bool) {
   172  	out := make([][][]byte, 0)
   173  	db.memtable.Scan(s, e, func(k, v []byte) bool {
   174  		out = append(out, [][]byte{k, v})
   175  		return true
   176  	})
   177  	return out, true
   178  }
   179  
   180  func (db *inMemoryDatabase) ScanWithLimit(s, e []byte, limit int) ([][][]byte, bool) {
   181  	out := make([][][]byte, 0)
   182  	db.memtable.Scan(s, e, func(k, v []byte) bool {
   183  		out = append(out, [][]byte{k, v})
   184  		if limit > 0 && len(out) >= limit {
   185  			return false
   186  		}
   187  		return true
   188  	})
   189  	return out, true
   190  }
   191  
   192  func (db *inMemoryDatabase) Range(fnc func(k, v []byte) bool) {
   193  	db.memtable.Range(fnc)
   194  }
   195  
   196  func (db *inMemoryDatabase) Count() uint64  { return db.memtable.Count() }
   197  func (db *inMemoryDatabase) Close()         {}
   198  func (db *inMemoryDatabase) Print()         {}
   199  func (db *inMemoryDatabase) Connect() error { return nil }