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 }