github.com/scottcagno/storage@v1.8.0/pkg/_junk/_lsmtree/sstable/manager.go (about) 1 package sstable 2 3 import ( 4 "bytes" 5 "os" 6 "path/filepath" 7 "sort" 8 "strings" 9 ) 10 11 // https: //play.golang.org/p/jRpPRa4Q4Nh 12 // https://play.golang.org/p/hTuTKen_ovK 13 14 type SSManager struct { 15 base string 16 sparse []*SparseIndex 17 gidx int64 18 } 19 20 func OpenSSManager(base string) (*SSManager, error) { 21 // make sure we are working with absolute paths 22 //base, err := filepath.Abs(base) 23 //if err != nil { 24 // return nil, err 25 //} 26 // sanitize any path separators 27 base = filepath.ToSlash(base) 28 ssm := &SSManager{ 29 base: base, 30 sparse: make([]*SparseIndex, 0), 31 gidx: -1, 32 } 33 ssm.gidx = ssm.GetLatestIndex() 34 // load sparse index 35 idxs, err := ssm.AllIndexes() 36 if err != nil { 37 return nil, err 38 } 39 for i := range idxs { 40 spi, err := OpenSparseIndex(ssm.base, idxs[i]) 41 if err != nil { 42 return nil, err 43 } 44 ssm.sparse = append(ssm.sparse, spi) 45 } 46 return ssm, nil 47 } 48 49 func (ssm *SSManager) AllIndexes() ([]int64, error) { 50 ssts, err := ssm.ListSSTables() 51 if err != nil { 52 return nil, ErrSSTableNotFound 53 } 54 if len(ssts) == 0 { 55 return nil, ErrSSTableNotFound 56 } 57 sort.Strings(ssts) 58 var nn []int64 59 for i := range ssts { 60 index, err := IndexFromDataFileName(filepath.Base(ssts[i])) 61 if err != nil { 62 return nil, err 63 } 64 nn = append(nn, index) 65 } 66 return nn, nil 67 } 68 69 func (ssm *SSManager) GetLatestIndex() int64 { 70 ssts, err := ssm.ListSSTables() 71 if err != nil { 72 return 0 73 } 74 if len(ssts) == 0 { 75 return 0 76 } 77 sort.Strings(ssts) 78 index, err := IndexFromDataFileName(filepath.Base(ssts[len(ssts)-1])) 79 if err != nil { 80 return 0 81 } 82 return index 83 } 84 85 func (ssm *SSManager) SearchSparseIndex(key string) (string, int64) { 86 var path string 87 var offset int64 88 for i := range ssm.sparse { 89 if !ssm.sparse[i].HasKey(key) { 90 continue 91 } 92 path, offset = ssm.sparse[i].GetClose(key) 93 break 94 } 95 return path, offset 96 } 97 98 func (ssm *SSManager) Get(key string) ([]byte, error) { 99 ssts, err := ssm.ListSSTables() 100 if err != nil { 101 return nil, err 102 } 103 var indexes []int 104 for _, name := range ssts { 105 index, err := IndexFromDataFileName(filepath.Base(name)) 106 if err != nil { 107 return nil, err 108 } 109 indexes = append(indexes, int(index)) 110 } 111 sort.Ints(indexes) 112 var de *sstDataEntry 113 // TODO: find better way to do this 114 for i := len(indexes) - 1; i > 0; i-- { 115 sst, err := OpenSSTable(ssm.base, int64(indexes[i])) 116 if err != nil { 117 return nil, err 118 } 119 de, err = sst.ReadEntry(key) 120 if de != nil { 121 break 122 } 123 err = sst.Close() 124 if err != nil { 125 return nil, err 126 } 127 } 128 return de.value, nil 129 } 130 131 func (ssm *SSManager) CompactSSTables(index int64) error { 132 // load sstable 133 sst, err := OpenSSTable(ssm.base, index) 134 if err != nil { 135 return err 136 } 137 // make batch 138 batch := NewBatch() 139 // iterate 140 err = sst.Scan(func(de *sstDataEntry) bool { 141 // add any data entries that are not tombstones to batch 142 if de.value != nil && !bytes.Equal(de.value, TombstoneEntry) { 143 batch.WriteDataEntry(de) 144 } 145 return true 146 }) 147 if err != nil { 148 return err 149 } 150 // get path 151 tpath, ipath := sst.SSTablePath(), sst.SSIndexPath() 152 // close sstable 153 err = sst.Close() 154 if err != nil { 155 return err 156 } 157 // remove old table 158 err = os.Remove(tpath) 159 if err != nil { 160 return err 161 } 162 // remove old index 163 err = os.Remove(ipath) 164 if err != nil { 165 return err 166 } 167 // open new sstable to write to 168 sst, err = CreateSSTable(ssm.base, index) 169 if err != nil { 170 return err 171 } 172 // write batch to table 173 err = sst.WriteBatch(batch) 174 // flush and close sstable 175 err = sst.Close() 176 if err != nil { 177 return err 178 } 179 return nil 180 } 181 182 func (ssm *SSManager) MergeSSTables(iA, iB int64) error { 183 // load sstable A 184 sstA, err := OpenSSTable(ssm.base, iA) 185 if err != nil { 186 return err 187 } 188 // and sstable B 189 sstB, err := OpenSSTable(ssm.base, iB) 190 if err != nil { 191 return err 192 } 193 // make batch to write data to 194 batch := NewBatch() 195 // pass tables to the merge writer 196 err = mergeWriter(sstA, sstB, batch) 197 if err != nil { 198 return err 199 } 200 // close table A 201 err = sstA.Close() 202 if err != nil { 203 return err 204 } 205 // close table B 206 err = sstB.Close() 207 if err != nil { 208 return err 209 } 210 // open new sstable to write to 211 sstC, err := CreateSSTable(ssm.base, iB+1) 212 if err != nil { 213 return err 214 } 215 // write batch to table 216 err = sstC.WriteBatch(batch) 217 // flush and close sstable 218 err = sstC.Close() 219 if err != nil { 220 return err 221 } 222 return nil 223 } 224 225 func mergeWriter(sstA, sstB *SSTable, batch *Batch) error { 226 227 i, j := 0, 0 228 n1, n2 := sstA.index.Len(), sstB.index.Len() 229 230 var err error 231 var de *sstDataEntry 232 for i < n1 && j < n2 { 233 if sstA.index.data[i].key == sstB.index.data[j].key { 234 // read entry from sstB 235 de, err = sstB.ReadEntryAt(sstB.index.data[j].offset) 236 if err != nil { 237 return err 238 } 239 // write entry to batch 240 batch.WriteDataEntry(de) 241 i++ 242 j++ 243 continue 244 } 245 if sstA.index.data[i].key < sstB.index.data[j].key { 246 // read entry from sstA 247 de, err = sstA.ReadEntryAt(sstA.index.data[i].offset) 248 if err != nil { 249 return err 250 } 251 // write entry to batch 252 batch.WriteDataEntry(de) 253 i++ 254 continue 255 } 256 if sstB.index.data[j].key < sstA.index.data[i].key { 257 // read entry from sstB 258 de, err = sstB.ReadEntryAt(sstB.index.data[j].offset) 259 if err != nil { 260 return err 261 } 262 // write entry to batch 263 batch.WriteDataEntry(de) 264 j++ 265 continue 266 } 267 } 268 269 // print remaining 270 for i < n1 { 271 // read entry from sstA 272 de, err = sstA.ReadEntryAt(sstA.index.data[i].offset) 273 if err != nil { 274 return err 275 } 276 // write entry to batch 277 batch.WriteDataEntry(de) 278 i++ 279 } 280 281 // print remaining 282 for j < n2 { 283 // read entry from sstB 284 de, err = sstB.ReadEntryAt(sstB.index.data[j].offset) 285 if err != nil { 286 return err 287 } 288 // write entry to batch 289 batch.WriteDataEntry(de) 290 j++ 291 } 292 293 // return error free 294 return nil 295 } 296 297 func (ssm *SSManager) ListSSTables() ([]string, error) { 298 files, err := os.ReadDir(ssm.base) 299 if err != nil { 300 return nil, err 301 } 302 var ss []string 303 for _, file := range files { 304 if file.IsDir() { 305 continue 306 } 307 if strings.HasSuffix(file.Name(), dataFileSuffix) { 308 ss = append(ss, filepath.Join(ssm.base, file.Name())) 309 } 310 } 311 return ss, nil 312 } 313 314 func (ssm *SSManager) ListSSIndexes() ([]string, error) { 315 files, err := os.ReadDir(ssm.base) 316 if err != nil { 317 return nil, err 318 } 319 var ss []string 320 for _, file := range files { 321 if file.IsDir() { 322 continue 323 } 324 if strings.HasSuffix(file.Name(), indexFileSuffix) { 325 ss = append(ss, filepath.Join(ssm.base, file.Name())) 326 } 327 } 328 return ss, nil 329 } 330 331 func (ssm *SSManager) Close() error { 332 return nil 333 }