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 }