Line data Source code
1 : // Copyright 2019 The LevelDB-Go and Pebble 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 metamorphic
6 :
7 : import "github.com/cockroachdb/pebble/internal/randvar"
8 :
9 : type opType int
10 :
11 : const (
12 : batchAbort opType = iota
13 : batchCommit
14 : dbCheckpoint
15 : dbClose
16 : dbCompact
17 : dbFlush
18 : dbRatchetFormatMajorVersion
19 : dbRestart
20 : iterClose
21 : iterFirst
22 : iterLast
23 : iterNext
24 : iterNextWithLimit
25 : iterNextPrefix
26 : iterPrev
27 : iterPrevWithLimit
28 : iterSeekGE
29 : iterSeekGEWithLimit
30 : iterSeekLT
31 : iterSeekLTWithLimit
32 : iterSeekPrefixGE
33 : iterSetBounds
34 : iterSetOptions
35 : newBatch
36 : newIndexedBatch
37 : newIter
38 : newIterUsingClone
39 : newSnapshot
40 : readerGet
41 : replicate
42 : snapshotClose
43 : writerApply
44 : writerDelete
45 : writerDeleteRange
46 : writerIngest
47 : writerMerge
48 : writerRangeKeyDelete
49 : writerRangeKeySet
50 : writerRangeKeyUnset
51 : writerSet
52 : writerSingleDelete
53 : )
54 :
55 : type config struct {
56 : // Weights for the operation mix to generate. ops[i] corresponds to the
57 : // weight for opType(i).
58 : ops []int
59 :
60 : // newPrefix configures the probability that when generating a new user key,
61 : // the generated key uses a new key prefix rather than an existing prefix
62 : // with a suffix.
63 : newPrefix float64
64 : // writeSuffixDist defines the distribution of key suffixes during writing.
65 : // It's a dynamic randvar to roughly emulate workloads with MVCC timestamps,
66 : // skewing towards most recent timestamps.
67 : writeSuffixDist randvar.Dynamic
68 :
69 : // numInstances defines the number of pebble instances created for this
70 : // metamorphic test run.
71 : numInstances int
72 :
73 : // TODO(peter): unimplemented
74 : // keyDist randvar.Dynamic
75 : // keySizeDist randvar.Static
76 : // valueSizeDist randvar.Static
77 : // updateFrac float64
78 : // lowerBoundFrac float64
79 : // upperBoundFrac float64
80 : }
81 :
82 1 : func (c config) withNewPrefixProbability(p float64) config {
83 1 : c.newPrefix = p
84 1 : return c
85 1 : }
86 :
87 1 : func (c config) withOpWeight(op opType, weight int) config {
88 1 : c.ops[op] = weight
89 1 : return c
90 1 : }
91 :
92 : var presetConfigs = []config{
93 : defaultConfig(),
94 : // Generate a configuration that helps exercise code paths dependent on many
95 : // versions of keys with the same prefixes. The default configuration does
96 : // not tend to generate many versions of the same key. Additionally, its
97 : // relatively high weight for deletion write operations makes it less likely
98 : // that we'll accumulate enough versions to exercise some code paths (eg,
99 : // see #2921 which requires >16 SETs for versions of the same prefix to
100 : // reside in a single block to exercise the code path).
101 : //
102 : // To encourage generation of many versions of the same keys, generate a new
103 : // prefix only 4% of the time when generating a new key. The remaining 96%
104 : // of new key generations will use an existing prefix. To keep the size of
105 : // the database growing, we also reduce the probability of delete write
106 : // operations significantly.
107 : defaultConfig().
108 : withNewPrefixProbability(0.04).
109 : withOpWeight(writerDeleteRange, 1).
110 : withOpWeight(writerDelete, 5).
111 : withOpWeight(writerSingleDelete, 5).
112 : withOpWeight(writerMerge, 0),
113 : }
114 :
115 : var multiInstancePresetConfig = multiInstanceConfig()
116 :
117 1 : func defaultConfig() config {
118 1 : return config{
119 1 : // dbClose is not in this list since it is deterministically generated once, at the end of the test.
120 1 : ops: []int{
121 1 : batchAbort: 5,
122 1 : batchCommit: 5,
123 1 : dbCheckpoint: 1,
124 1 : dbCompact: 1,
125 1 : dbFlush: 2,
126 1 : dbRatchetFormatMajorVersion: 1,
127 1 : dbRestart: 2,
128 1 : iterClose: 5,
129 1 : iterFirst: 100,
130 1 : iterLast: 100,
131 1 : iterNext: 100,
132 1 : iterNextWithLimit: 20,
133 1 : iterNextPrefix: 20,
134 1 : iterPrev: 100,
135 1 : iterPrevWithLimit: 20,
136 1 : iterSeekGE: 100,
137 1 : iterSeekGEWithLimit: 20,
138 1 : iterSeekLT: 100,
139 1 : iterSeekLTWithLimit: 20,
140 1 : iterSeekPrefixGE: 100,
141 1 : iterSetBounds: 100,
142 1 : iterSetOptions: 10,
143 1 : newBatch: 5,
144 1 : newIndexedBatch: 5,
145 1 : newIter: 10,
146 1 : newIterUsingClone: 5,
147 1 : newSnapshot: 10,
148 1 : readerGet: 100,
149 1 : replicate: 0,
150 1 : snapshotClose: 10,
151 1 : writerApply: 10,
152 1 : writerDelete: 100,
153 1 : writerDeleteRange: 50,
154 1 : writerIngest: 100,
155 1 : writerMerge: 100,
156 1 : writerRangeKeySet: 10,
157 1 : writerRangeKeyUnset: 10,
158 1 : writerRangeKeyDelete: 5,
159 1 : writerSet: 100,
160 1 : writerSingleDelete: 50,
161 1 : },
162 1 : // Use a new prefix 75% of the time (and 25% of the time use an existing
163 1 : // prefix with an alternative suffix).
164 1 : newPrefix: 0.75,
165 1 : // Use a skewed distribution of suffixes to mimic MVCC timestamps. The
166 1 : // range will be widened whenever a suffix is found to already be in use
167 1 : // for a particular prefix.
168 1 : writeSuffixDist: mustDynamic(randvar.NewSkewedLatest(0, 1, 0.99)),
169 1 : }
170 1 : }
171 :
172 1 : func multiInstanceConfig() config {
173 1 : cfg := defaultConfig()
174 1 : cfg.ops[replicate] = 5
175 1 : // Single deletes and merges are disabled in multi-instance mode, as
176 1 : // replicateOp doesn't support them.
177 1 : cfg.ops[writerSingleDelete] = 0
178 1 : cfg.ops[writerMerge] = 0
179 1 : return cfg
180 1 : }
181 :
182 1 : func mustDynamic(dyn randvar.Dynamic, err error) randvar.Dynamic {
183 1 : if err != nil {
184 0 : panic(err)
185 : }
186 1 : return dyn
187 : }
|