github.com/scottcagno/storage@v1.8.0/pkg/lsmtree/sstable-util.go (about)

     1  package lsmtree
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  const (
    13  	filePrefix      = "sst-"
    14  	dataFileSuffix  = ".dat"
    15  	indexFileSuffix = ".idx"
    16  )
    17  
    18  func levelToDir(level int) string {
    19  	return fmt.Sprintf("level-%d", level)
    20  }
    21  
    22  func dirToLevel(dir string) (int, error) {
    23  	return strconv.Atoi(strings.Split(dir, "-")[1])
    24  }
    25  
    26  func toDataFileName(index int64) string {
    27  	hexa := strconv.FormatInt(index, 16)
    28  	return fmt.Sprintf("%s%010s%s", filePrefix, hexa, dataFileSuffix)
    29  }
    30  
    31  func toIndexFileName(index int64) string {
    32  	hexa := strconv.FormatInt(index, 16)
    33  	return fmt.Sprintf("%s%010s%s", filePrefix, hexa, indexFileSuffix)
    34  }
    35  
    36  func fromDataFileName(name string) (int64, error) {
    37  	name = filepath.Base(name)
    38  	hexa := name[len(filePrefix) : len(name)-len(dataFileSuffix)]
    39  	return strconv.ParseInt(hexa, 16, 32)
    40  }
    41  
    42  func fromIndexFileName(name string) (int64, error) {
    43  	name = filepath.Base(name)
    44  	hexa := name[len(filePrefix) : len(name)-len(indexFileSuffix)]
    45  	return strconv.ParseInt(hexa, 16, 32)
    46  }
    47  
    48  // openDataFile opens the ss-table data file (read only)
    49  func openDataFile(path string, seq int64, flag int) (*os.File, error) {
    50  	// get data file name
    51  	dataFileName := filepath.Join(path, toDataFileName(seq))
    52  	// open data file
    53  	dataFile, err := os.OpenFile(dataFileName, os.O_RDONLY, 0666)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	// remember to close
    59  	//defer func(dataFile *os.File) {
    60  	//	err := dataFile.Close()
    61  	//	if err != nil {
    62  	//		panic("closing dataFile: " + err.Error())
    63  	//	}
    64  	//}(dataFile)
    65  
    66  	// return data file
    67  	return dataFile, nil
    68  }
    69  
    70  // openIndexFile opens the ss-table index file (read only)
    71  func openIndexFile(path string, seq int64, flag int) (*os.File, error) {
    72  	// get index file name
    73  	indexFileName := filepath.Join(path, toIndexFileName(seq))
    74  	// open index file
    75  	indexFile, err := os.OpenFile(indexFileName, os.O_RDONLY, 0666)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	// remember to close
    81  	//defer func(indexFile *os.File) {
    82  	//	err := indexFile.Close()
    83  	//	if err != nil {
    84  	//		panic("closing indexFile: " + err.Error())
    85  	//	}
    86  	//}(indexFile)
    87  
    88  	// return index file
    89  	return indexFile, nil
    90  }
    91  
    92  func compactSSTable(path string) error {
    93  	// TODO: implement me...
    94  	// lock and defer unlock
    95  	// open and load ss-table
    96  	// make new ss-table
    97  	// iterate old ss-table and write entries to new ss-table (not adding tombstone entries to batch)
    98  	// flush new ss-table
    99  	// close both ss-tables
   100  	// remove old ss-table (and index)
   101  	return nil
   102  }
   103  
   104  func mergeSSTables(sstA, sstB *ssTable, batch *Batch) error {
   105  
   106  	i, j := 0, 0
   107  	n1, n2 := sstA.index.Len(), sstB.index.Len()
   108  
   109  	var err error
   110  	var de *Entry
   111  	for i < n1 && j < n2 {
   112  		if bytes.Compare(sstA.index.data[i].Key, sstB.index.data[j].Key) == 0 {
   113  			// read entry from sstB
   114  			de, err = sstB.ReadAt(sstB.index.data[j].Offset)
   115  			if err != nil {
   116  				return err
   117  			}
   118  			// write entry to batch
   119  			err = batch.writeEntry(de)
   120  			if err != nil {
   121  				return err
   122  			}
   123  			i++
   124  			j++
   125  			continue
   126  		}
   127  		if bytes.Compare(sstA.index.data[i].Key, sstB.index.data[j].Key) == -1 {
   128  			// read entry from sstA
   129  			de, err = sstA.ReadAt(sstA.index.data[i].Offset)
   130  			if err != nil {
   131  				return err
   132  			}
   133  			// write entry to batch
   134  			err = batch.writeEntry(de)
   135  			if err != nil {
   136  				return err
   137  			}
   138  			i++
   139  			continue
   140  		}
   141  		if bytes.Compare(sstB.index.data[j].Key, sstA.index.data[i].Key) == -1 {
   142  			// read entry from sstB
   143  			de, err = sstB.ReadAt(sstB.index.data[j].Offset)
   144  			if err != nil {
   145  				return err
   146  			}
   147  			// write entry to batch
   148  			err = batch.writeEntry(de)
   149  			if err != nil {
   150  				return err
   151  			}
   152  			j++
   153  			continue
   154  		}
   155  	}
   156  
   157  	// print remaining
   158  	for i < n1 {
   159  		// read entry from sstA
   160  		de, err = sstA.ReadAt(sstA.index.data[i].Offset)
   161  		if err != nil {
   162  			return err
   163  		}
   164  		// write entry to batch
   165  		err = batch.writeEntry(de)
   166  		if err != nil {
   167  			return err
   168  		}
   169  		i++
   170  	}
   171  
   172  	// print remaining
   173  	for j < n2 {
   174  		// read entry from sstB
   175  		de, err = sstB.ReadAt(sstB.index.data[j].Offset)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		// write entry to batch
   180  		err = batch.writeEntry(de)
   181  		if err != nil {
   182  			return err
   183  		}
   184  		j++
   185  	}
   186  
   187  	// return error free
   188  	return nil
   189  }