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  }