github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/table_stats_test.go (about)

     1  // Copyright 2020 The LevelDB-Go and Pebble and Bitalostored Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package bitalostable
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/require"
    14  	"github.com/zuoyebang/bitalostable/internal/base"
    15  	"github.com/zuoyebang/bitalostable/internal/datadriven"
    16  	"github.com/zuoyebang/bitalostable/internal/keyspan"
    17  	"github.com/zuoyebang/bitalostable/internal/rangekey"
    18  	"github.com/zuoyebang/bitalostable/internal/testkeys"
    19  	"github.com/zuoyebang/bitalostable/sstable"
    20  	"github.com/zuoyebang/bitalostable/vfs"
    21  )
    22  
    23  func TestTableStats(t *testing.T) {
    24  	fs := vfs.NewMem()
    25  	// loadedInfo is protected by d.mu.
    26  	var loadedInfo *TableStatsInfo
    27  	opts := &Options{
    28  		FS: fs,
    29  		EventListener: EventListener{
    30  			TableStatsLoaded: func(info TableStatsInfo) {
    31  				loadedInfo = &info
    32  			},
    33  		},
    34  	}
    35  	opts.DisableAutomaticCompactions = true
    36  	opts.Comparer = testkeys.Comparer
    37  	opts.FormatMajorVersion = FormatRangeKeys
    38  
    39  	d, err := Open("", opts)
    40  	require.NoError(t, err)
    41  	defer func() {
    42  		if d != nil {
    43  			require.NoError(t, d.Close())
    44  		}
    45  	}()
    46  
    47  	datadriven.RunTest(t, "testdata/table_stats", func(td *datadriven.TestData) string {
    48  		switch td.Cmd {
    49  		case "disable":
    50  			d.mu.Lock()
    51  			d.opts.private.disableTableStats = true
    52  			d.mu.Unlock()
    53  			return ""
    54  
    55  		case "enable":
    56  			d.mu.Lock()
    57  			d.opts.private.disableTableStats = false
    58  			d.maybeCollectTableStatsLocked()
    59  			d.mu.Unlock()
    60  			return ""
    61  
    62  		case "define":
    63  			require.NoError(t, d.Close())
    64  			loadedInfo = nil
    65  
    66  			d, err = runDBDefineCmd(td, opts)
    67  			if err != nil {
    68  				return err.Error()
    69  			}
    70  			d.mu.Lock()
    71  			s := d.mu.versions.currentVersion().String()
    72  			d.mu.Unlock()
    73  			return s
    74  
    75  		case "reopen":
    76  			require.NoError(t, d.Close())
    77  			loadedInfo = nil
    78  
    79  			// Open using existing file system.
    80  			d, err = Open("", opts)
    81  			require.NoError(t, err)
    82  			return ""
    83  
    84  		case "batch":
    85  			b := d.NewBatch()
    86  			if err := runBatchDefineCmd(td, b); err != nil {
    87  				return err.Error()
    88  			}
    89  			b.Commit(nil)
    90  			return ""
    91  
    92  		case "flush":
    93  			if err := d.Flush(); err != nil {
    94  				return err.Error()
    95  			}
    96  
    97  			d.mu.Lock()
    98  			s := d.mu.versions.currentVersion().String()
    99  			d.mu.Unlock()
   100  			return s
   101  
   102  		case "ingest":
   103  			if err = runBuildCmd(td, d, d.opts.FS); err != nil {
   104  				return err.Error()
   105  			}
   106  			if err = runIngestCmd(td, d, d.opts.FS); err != nil {
   107  				return err.Error()
   108  			}
   109  			d.mu.Lock()
   110  			s := d.mu.versions.currentVersion().String()
   111  			d.mu.Unlock()
   112  			return s
   113  
   114  		case "wait-pending-table-stats":
   115  			return runTableStatsCmd(td, d)
   116  
   117  		case "wait-loaded-initial":
   118  			d.mu.Lock()
   119  			for d.mu.tableStats.loading || !d.mu.tableStats.loadedInitial {
   120  				d.mu.tableStats.cond.Wait()
   121  			}
   122  			s := loadedInfo.String()
   123  			d.mu.Unlock()
   124  			return s
   125  
   126  		case "compact":
   127  			if err := runCompactCmd(td, d); err != nil {
   128  				return err.Error()
   129  			}
   130  			d.mu.Lock()
   131  			// Disable the "dynamic base level" code for this test.
   132  			d.mu.versions.picker.forceBaseLevel1()
   133  			s := d.mu.versions.currentVersion().String()
   134  			d.mu.Unlock()
   135  			return s
   136  
   137  		default:
   138  			return fmt.Sprintf("unknown command: %s", td.Cmd)
   139  		}
   140  	})
   141  }
   142  
   143  func TestTableRangeDeletionIter(t *testing.T) {
   144  	var m *fileMetadata
   145  	cmp := base.DefaultComparer.Compare
   146  	fs := vfs.NewMem()
   147  	datadriven.RunTest(t, "testdata/table_stats_deletion_iter", func(td *datadriven.TestData) string {
   148  		switch cmd := td.Cmd; cmd {
   149  		case "build":
   150  			f, err := fs.Create("tmp.sst")
   151  			if err != nil {
   152  				return err.Error()
   153  			}
   154  			w := sstable.NewWriter(f, sstable.WriterOptions{
   155  				TableFormat: sstable.TableFormatMax,
   156  			})
   157  			m = &fileMetadata{}
   158  			for _, line := range strings.Split(td.Input, "\n") {
   159  				s := keyspan.ParseSpan(line)
   160  				// Range dels can be written sequentially. Range keys must be collected.
   161  				rKeySpan := &keyspan.Span{Start: s.Start, End: s.End}
   162  				for _, k := range s.Keys {
   163  					if rangekey.IsRangeKey(k.Kind()) {
   164  						rKeySpan.Keys = append(rKeySpan.Keys, k)
   165  					} else {
   166  						k := base.InternalKey{UserKey: s.Start, Trailer: k.Trailer}
   167  						if err = w.Add(k, s.End); err != nil {
   168  							return err.Error()
   169  						}
   170  					}
   171  				}
   172  				err = rangekey.Encode(rKeySpan, func(k base.InternalKey, v []byte) error {
   173  					return w.AddRangeKey(k, v)
   174  				})
   175  				if err != nil {
   176  					return err.Error()
   177  				}
   178  			}
   179  			if err = w.Close(); err != nil {
   180  				return err.Error()
   181  			}
   182  			meta, err := w.Metadata()
   183  			if err != nil {
   184  				return err.Error()
   185  			}
   186  			if meta.HasPointKeys {
   187  				m.ExtendPointKeyBounds(cmp, meta.SmallestPoint, meta.LargestPoint)
   188  			}
   189  			if meta.HasRangeDelKeys {
   190  				m.ExtendPointKeyBounds(cmp, meta.SmallestRangeDel, meta.LargestRangeDel)
   191  			}
   192  			if meta.HasRangeKeys {
   193  				m.ExtendRangeKeyBounds(cmp, meta.SmallestRangeKey, meta.LargestRangeKey)
   194  			}
   195  			return m.DebugString(base.DefaultFormatter, false /* verbose */)
   196  		case "spans":
   197  			f, err := fs.Open("tmp.sst")
   198  			if err != nil {
   199  				return err.Error()
   200  			}
   201  			var r *sstable.Reader
   202  			r, err = sstable.NewReader(f, sstable.ReaderOptions{})
   203  			if err != nil {
   204  				return err.Error()
   205  			}
   206  			defer r.Close()
   207  			iter, err := newCombinedDeletionKeyspanIter(base.DefaultComparer, r, m)
   208  			if err != nil {
   209  				return err.Error()
   210  			}
   211  			defer iter.Close()
   212  			var buf bytes.Buffer
   213  			for s := iter.First(); s != nil; s = iter.Next() {
   214  				buf.WriteString(s.String() + "\n")
   215  			}
   216  			if buf.Len() == 0 {
   217  				return "(none)"
   218  			}
   219  			return buf.String()
   220  		default:
   221  			return fmt.Sprintf("unknown command: %s", cmd)
   222  		}
   223  	})
   224  }