github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/metrics_test.go (about) 1 // Copyright 2019 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 "fmt" 9 "testing" 10 11 "github.com/cockroachdb/redact" 12 "github.com/stretchr/testify/require" 13 "github.com/zuoyebang/bitalostable/internal/datadriven" 14 "github.com/zuoyebang/bitalostable/internal/humanize" 15 "github.com/zuoyebang/bitalostable/vfs" 16 ) 17 18 func TestMetricsFormat(t *testing.T) { 19 var m Metrics 20 m.BlockCache.Size = 1 21 m.BlockCache.Count = 2 22 m.BlockCache.Hits = 3 23 m.BlockCache.Misses = 4 24 m.Compact.Count = 5 25 m.Compact.DefaultCount = 27 26 m.Compact.DeleteOnlyCount = 28 27 m.Compact.ElisionOnlyCount = 29 28 m.Compact.MoveCount = 30 29 m.Compact.ReadCount = 31 30 m.Compact.RewriteCount = 32 31 m.Compact.MultiLevelCount = 33 32 m.Compact.EstimatedDebt = 6 33 m.Compact.InProgressBytes = 7 34 m.Compact.NumInProgress = 2 35 m.Flush.Count = 8 36 m.Filter.Hits = 9 37 m.Filter.Misses = 10 38 m.MemTable.Size = 11 39 m.MemTable.Count = 12 40 m.MemTable.ZombieSize = 13 41 m.MemTable.ZombieCount = 14 42 m.Snapshots.Count = 4 43 m.Snapshots.EarliestSeqNum = 1024 44 m.Table.ZombieSize = 15 45 m.Table.ZombieCount = 16 46 m.TableCache.Size = 17 47 m.TableCache.Count = 18 48 m.TableCache.Hits = 19 49 m.TableCache.Misses = 20 50 m.TableIters = 21 51 m.WAL.Files = 22 52 m.WAL.ObsoleteFiles = 23 53 m.WAL.Size = 24 54 m.WAL.BytesIn = 25 55 m.WAL.BytesWritten = 26 56 57 for i := range m.Levels { 58 l := &m.Levels[i] 59 base := uint64((i + 1) * 100) 60 l.Sublevels = int32(i + 1) 61 l.NumFiles = int64(base) + 1 62 l.Size = int64(base) + 2 63 l.Score = float64(base) + 3 64 l.BytesIn = base + 4 65 l.BytesIngested = base + 4 66 l.BytesMoved = base + 6 67 l.BytesRead = base + 7 68 l.BytesCompacted = base + 8 69 l.BytesFlushed = base + 9 70 l.TablesCompacted = base + 10 71 l.TablesFlushed = base + 11 72 l.TablesIngested = base + 12 73 l.TablesMoved = base + 13 74 } 75 76 const expected = ` 77 __level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp 78 WAL 22 24 B - 25 B - - - - 26 B - - - 1.0 79 0 101 102 B 103.00 104 B 104 B 112 106 B 113 217 B 221 107 B 1 2.1 80 1 201 202 B 203.00 204 B 204 B 212 206 B 213 417 B 421 207 B 2 2.0 81 2 301 302 B 303.00 304 B 304 B 312 306 B 313 617 B 621 307 B 3 2.0 82 3 401 402 B 403.00 404 B 404 B 412 406 B 413 817 B 821 407 B 4 2.0 83 4 501 502 B 503.00 504 B 504 B 512 506 B 513 1017 B 1.0 K 507 B 5 2.0 84 5 601 602 B 603.00 604 B 604 B 612 606 B 613 1.2 K 1.2 K 607 B 6 2.0 85 6 701 702 B - 704 B 704 B 712 706 B 713 1.4 K 1.4 K 707 B 7 2.0 86 total 2807 2.7 K - 2.8 K 2.8 K 2.9 K 2.8 K 2.9 K 8.4 K 5.7 K 2.8 K 28 3.0 87 flush 8 88 compact 5 6 B 7 B 2 (size == estimated-debt, score = in-progress-bytes, in = num-in-progress) 89 ctype 27 28 29 30 31 32 33 (default, delete, elision, move, read, rewrite, multi-level) 90 memtbl 12 11 B 91 zmemtbl 14 13 B 92 ztbl 16 15 B 93 bcache 2 1 B 42.9% (score == hit-rate) 94 tcache 18 17 B 48.7% (score == hit-rate) 95 snaps 4 - 1024 (score == earliest seq num) 96 titers 21 97 filter - - 47.4% (score == utility) 98 ` 99 if s := "\n" + m.String(); expected != s { 100 t.Fatalf("expected%s\nbut found%s", expected, s) 101 } 102 } 103 104 func TestMetrics(t *testing.T) { 105 opts := &Options{ 106 FS: vfs.NewMem(), 107 L0CompactionThreshold: 8, 108 } 109 110 // Prevent foreground flushes and compactions from triggering asynchronous 111 // follow-up compactions. This avoids asynchronously-scheduled work from 112 // interfering with the expected metrics output and reduces test flakiness. 113 opts.DisableAutomaticCompactions = true 114 115 d, err := Open("", opts) 116 require.NoError(t, err) 117 defer func() { 118 require.NoError(t, d.Close()) 119 }() 120 121 iters := make(map[string]*Iterator) 122 defer func() { 123 for _, i := range iters { 124 require.NoError(t, i.Close()) 125 } 126 }() 127 128 datadriven.RunTest(t, "testdata/metrics", func(td *datadriven.TestData) string { 129 switch td.Cmd { 130 case "batch": 131 b := d.NewBatch() 132 if err := runBatchDefineCmd(td, b); err != nil { 133 return err.Error() 134 } 135 b.Commit(nil) 136 return "" 137 138 case "compact": 139 if err := runCompactCmd(td, d); err != nil { 140 return err.Error() 141 } 142 143 d.mu.Lock() 144 s := d.mu.versions.currentVersion().String() 145 d.mu.Unlock() 146 return s 147 148 case "flush": 149 if err := d.Flush(); err != nil { 150 return err.Error() 151 } 152 153 d.mu.Lock() 154 s := d.mu.versions.currentVersion().String() 155 d.mu.Unlock() 156 return s 157 158 case "iter-close": 159 if len(td.CmdArgs) != 1 { 160 return "iter-close <name>" 161 } 162 name := td.CmdArgs[0].String() 163 if iter := iters[name]; iter != nil { 164 if err := iter.Close(); err != nil { 165 return err.Error() 166 } 167 delete(iters, name) 168 } else { 169 return fmt.Sprintf("%s: not found", name) 170 } 171 172 // The deletion of obsolete files happens asynchronously when an iterator 173 // is closed. Wait for the obsolete tables to be deleted. Note that 174 // waiting on cleaner.cond isn't precisely correct. 175 d.mu.Lock() 176 for d.mu.cleaner.cleaning || len(d.mu.versions.obsoleteTables) > 0 { 177 d.mu.cleaner.cond.Wait() 178 } 179 d.mu.Unlock() 180 return "" 181 182 case "iter-new": 183 if len(td.CmdArgs) != 1 { 184 return "iter-new <name>" 185 } 186 name := td.CmdArgs[0].String() 187 if iter := iters[name]; iter != nil { 188 if err := iter.Close(); err != nil { 189 return err.Error() 190 } 191 } 192 iter := d.NewIter(nil) 193 // Some iterators (eg. levelIter) do not instantiate the underlying 194 // iterator until the first positioning call. Position the iterator 195 // so that levelIters will have loaded an sstable. 196 iter.First() 197 iters[name] = iter 198 return "" 199 200 case "metrics": 201 // The asynchronous loading of table stats can change metrics, so 202 // wait for all the tables' stats to be loaded. 203 d.mu.Lock() 204 d.waitTableStats() 205 d.mu.Unlock() 206 207 return d.Metrics().String() 208 209 case "disk-usage": 210 return humanize.IEC.Uint64(d.Metrics().DiskSpaceUsage()).String() 211 212 default: 213 return fmt.Sprintf("unknown command: %s", td.Cmd) 214 } 215 }) 216 } 217 218 func TestMetricsRedact(t *testing.T) { 219 const expected = ` 220 __level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp 221 WAL 0 0 B - 0 B - - - - 0 B - - - 0.0 222 0 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 223 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 224 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 225 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 226 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 227 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 228 6 0 0 B - 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 229 total 0 0 B - 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 230 flush 0 231 compact 0 0 B 0 B 0 (size == estimated-debt, score = in-progress-bytes, in = num-in-progress) 232 ctype 0 0 0 0 0 0 0 (default, delete, elision, move, read, rewrite, multi-level) 233 memtbl 0 0 B 234 zmemtbl 0 0 B 235 ztbl 0 0 B 236 bcache 0 0 B 0.0% (score == hit-rate) 237 tcache 0 0 B 0.0% (score == hit-rate) 238 snaps 0 - 0 (score == earliest seq num) 239 titers 0 240 filter - - 0.0% (score == utility) 241 ` 242 243 got := redact.Sprintf("%s", &Metrics{}).Redact() 244 if s := "\n" + got; expected != s { 245 t.Fatalf("expected%s\nbut found%s", expected, s) 246 } 247 }