-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
Copy pathopen.go
311 lines (278 loc) · 9.51 KB
/
open.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
// Copyright 2021 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package storage
import (
"context"
"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/cloud"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
"github.com/cockroachdb/errors"
"github.com/cockroachdb/pebble"
"github.com/cockroachdb/pebble/vfs"
)
// A ConfigOption may be passed to Open to configure the storage engine.
type ConfigOption func(cfg *engineConfig) error
// CombineOptions combines many options into one.
func CombineOptions(opts ...ConfigOption) ConfigOption {
return func(cfg *engineConfig) error {
for _, opt := range opts {
if err := opt(cfg); err != nil {
return err
}
}
return nil
}
}
// ReadOnly configures an engine to be opened in read-only mode.
var ReadOnly ConfigOption = func(cfg *engineConfig) error {
cfg.Opts.ReadOnly = true
return nil
}
// MustExist configures an engine to error on Open if the target directory
// does not contain an initialized store.
var MustExist ConfigOption = func(cfg *engineConfig) error {
cfg.MustExist = true
return nil
}
// DisableAutomaticCompactions configures an engine to be opened with disabled
// automatic compactions. Used primarily for debugCompactCmd.
var DisableAutomaticCompactions ConfigOption = func(cfg *engineConfig) error {
cfg.Opts.DisableAutomaticCompactions = true
return nil
}
// ForceWriterParallelism configures an engine to be opened with disabled
// automatic compactions. Used primarily for debugCompactCmd.
var ForceWriterParallelism ConfigOption = func(cfg *engineConfig) error {
cfg.Opts.Experimental.ForceWriterParallelism = true
return nil
}
// ForTesting configures the engine for use in testing. It may randomize some
// config options to improve test coverage.
var ForTesting ConfigOption = func(cfg *engineConfig) error {
cfg.onClose = append(cfg.onClose, func(p *Pebble) {
m := p.db.Metrics()
if m.Keys.MissizedTombstonesCount > 0 {
// A missized tombstone is a Pebble DELSIZED tombstone that encodes
// the wrong size of the value it deletes. This kind of tombstone is
// written when ClearOptions.ValueSizeKnown=true. If this assertion
// failed, something might be awry in the code clearing the key. Are
// we feeding the wrong value length to ValueSize?
panic(errors.AssertionFailedf("expected to find 0 missized tombstones; found %d", m.Keys.MissizedTombstonesCount))
}
})
return nil
}
// ForStickyEngineTesting is similar to ForTesting but leaves separated
// intents as enabled since we cannot ensure consistency in the test setup
// between what the KV layer thinks and what the engine does in terms of
// writing separated intents. Since our optimizations are for the case where
// we know there are only separated intents, this sidesteps any test issues
// due to inconsistencies.
var ForStickyEngineTesting ConfigOption = func(cfg *engineConfig) error {
return nil
}
// Attributes configures the engine's attributes.
func Attributes(attrs roachpb.Attributes) ConfigOption {
return func(cfg *engineConfig) error {
cfg.Attrs = attrs
return nil
}
}
// MaxSize sets the intended maximum store size. MaxSize is used for
// calculating free space and making rebalancing decisions.
func MaxSize(size int64) ConfigOption {
return func(cfg *engineConfig) error {
cfg.MaxSize = size
return nil
}
}
// BlockSize sets the engine block size, primarily for testing purposes.
func BlockSize(size int) ConfigOption {
return func(cfg *engineConfig) error {
for i := range cfg.Opts.Levels {
cfg.Opts.Levels[i].BlockSize = size
cfg.Opts.Levels[i].IndexBlockSize = size
}
return nil
}
}
// TargetFileSize sets the target file size across all levels of the LSM,
// primarily for testing purposes.
func TargetFileSize(size int64) ConfigOption {
return func(cfg *engineConfig) error {
for i := range cfg.Opts.Levels {
cfg.Opts.Levels[i].TargetFileSize = size
}
return nil
}
}
// MaxWriterConcurrency sets the concurrency of the sstable Writers. A concurrency
// of 0 implies no parallelism in the Writer, and a concurrency of 1 or more implies
// parallelism in the Writer. Currently, there's no difference between a concurrency
// of 1 or more.
func MaxWriterConcurrency(concurrency int) ConfigOption {
return func(cfg *engineConfig) error {
cfg.Opts.Experimental.MaxWriterConcurrency = concurrency
return nil
}
}
// MaxOpenFiles sets the maximum number of files an engine should open.
func MaxOpenFiles(count int) ConfigOption {
return func(cfg *engineConfig) error {
cfg.Opts.MaxOpenFiles = count
return nil
}
}
// CacheSize configures the size of the block cache.
func CacheSize(size int64) ConfigOption {
return func(cfg *engineConfig) error {
cfg.cacheSize = &size
return nil
}
}
// Caches sets the block and table caches. Useful when multiple stores share
// the same caches.
func Caches(cache *pebble.Cache, tableCache *pebble.TableCache) ConfigOption {
return func(cfg *engineConfig) error {
cfg.Opts.Cache = cache
cfg.Opts.TableCache = tableCache
return nil
}
}
// BallastSize sets the amount reserved by a ballast file for manual
// out-of-disk recovery.
func BallastSize(size int64) ConfigOption {
return func(cfg *engineConfig) error {
cfg.BallastSize = size
return nil
}
}
// SharedStorage enables use of shared storage (experimental).
func SharedStorage(sharedStorage cloud.ExternalStorage) ConfigOption {
return func(cfg *engineConfig) error {
cfg.SharedStorage = sharedStorage
// TODO(bilal): Do the format major version ratchet while accounting for
// version upgrade finalization. However, seeing as shared storage is
// an experimental feature and upgrading from existing stores is not
// supported, this is fine.
cfg.Opts.FormatMajorVersion = pebble.ExperimentalFormatVirtualSSTables
return nil
}
}
// RemoteStorageFactory enables use of remote storage (experimental).
func RemoteStorageFactory(accessor *cloud.ExternalStorageAccessor) ConfigOption {
return func(cfg *engineConfig) error {
cfg.RemoteStorageFactory = accessor
return nil
}
}
// MaxConcurrentCompactions configures the maximum number of concurrent
// compactions an Engine will execute.
func MaxConcurrentCompactions(n int) ConfigOption {
return func(cfg *engineConfig) error {
cfg.Opts.MaxConcurrentCompactions = func() int { return n }
return nil
}
}
// PebbleOptions contains Pebble-specific options in the same format as a
// Pebble OPTIONS file. For example:
// [Options]
// delete_range_flush_delay=2s
// flush_split_bytes=4096
func PebbleOptions(pebbleOptions string, parseHooks *pebble.ParseHooks) ConfigOption {
return func(cfg *engineConfig) error {
return cfg.Opts.Parse(pebbleOptions, parseHooks)
}
}
// EncryptionAtRest configures an engine to use encryption-at-rest. It is used
// for configuring in-memory engines, which are used in tests. It is not safe
// to modify the given slice afterwards as it is captured by reference.
func EncryptionAtRest(encryptionOptions []byte) ConfigOption {
return func(cfg *engineConfig) error {
if len(encryptionOptions) > 0 {
cfg.UseFileRegistry = true
cfg.EncryptionOptions = encryptionOptions
}
return nil
}
}
// Hook configures a hook to initialize additional storage options. It's used
// to initialize encryption-at-rest details in CCL builds.
func Hook(hookFunc func(*base.StorageConfig) error) ConfigOption {
return func(cfg *engineConfig) error {
if hookFunc == nil {
return nil
}
return hookFunc(&cfg.PebbleConfig.StorageConfig)
}
}
// If enables the given option if enable is true.
func If(enable bool, opt ConfigOption) ConfigOption {
if enable {
return opt
}
return func(cfg *engineConfig) error { return nil }
}
// A Location describes where the storage engine's data will be written. A
// Location may be in-memory or on the filesystem.
type Location struct {
dir string
fs vfs.FS
}
// Filesystem constructs a Location that instructs the storage engine to read
// and store data on the filesystem in the provided directory.
func Filesystem(dir string) Location {
return Location{
dir: dir,
fs: vfs.Default,
}
}
// InMemory constructs a Location that instructs the storage engine to store
// data in-memory.
func InMemory() Location {
return Location{
dir: "",
fs: vfs.NewMem(),
}
}
type engineConfig struct {
PebbleConfig
// cacheSize is stored separately so that we can avoid constructing the
// PebbleConfig.Opts.Cache until the call to Open. A Cache is created with
// a ref count of 1, so creating the Cache during execution of
// ConfigOption makes it too easy to leak a cache.
cacheSize *int64
}
// Open opens a new Pebble storage engine, reading and writing data to the
// provided Location, configured with the provided options.
func Open(
ctx context.Context, loc Location, settings *cluster.Settings, opts ...ConfigOption,
) (*Pebble, error) {
var cfg engineConfig
cfg.Dir = loc.dir
cfg.Settings = settings
cfg.Opts = DefaultPebbleOptions()
cfg.Opts.FS = loc.fs
for _, opt := range opts {
if err := opt(&cfg); err != nil {
return nil, err
}
}
if cfg.cacheSize != nil && cfg.Opts.Cache == nil {
cfg.Opts.Cache = pebble.NewCache(*cfg.cacheSize)
defer cfg.Opts.Cache.Unref()
}
p, err := NewPebble(ctx, cfg.PebbleConfig)
if err != nil {
return nil, err
}
return p, nil
}