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