-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
kv_descriptors.go
345 lines (317 loc) · 11.6 KB
/
kv_descriptors.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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// 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 descs
import (
"context"
"github.com/cockroachdb/cockroach/pkg/clusterversion"
"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/kv"
"github.com/cockroachdb/cockroach/pkg/sql/catalog"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/dbdesc"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/internal/catkv"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/internal/validate"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/nstree"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/systemschema"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/cockroachdb/cockroach/pkg/util/mon"
"github.com/cockroachdb/errors"
)
type kvDescriptors struct {
codec keys.SQLCodec
// systemNamespace is a cache of system table namespace entries. We assume
// these are immutable for the life of the process.
systemNamespace *systemDatabaseNamespaceCache
// allDescriptors is a slice of all available descriptors. The descriptors
// are cached to avoid repeated lookups by users like virtual tables. The
// cache is purged whenever events would cause a scan of all descriptors to
// return different values, such as when the txn timestamp changes or when
// new descriptors are written in the txn.
//
// TODO(ajwerner): This cache may be problematic in clusters with very large
// numbers of descriptors.
allDescriptors allDescriptors
// allDatabaseDescriptors is a slice of all available database descriptors.
// These are purged at the same time as allDescriptors.
allDatabaseDescriptors []catalog.DatabaseDescriptor
// allSchemasForDatabase maps databaseID -> schemaID -> schemaName.
// For each databaseID, all schemas visible under the database can be
// observed.
// These are purged at the same time as allDescriptors.
allSchemasForDatabase map[descpb.ID]map[descpb.ID]string
// memAcc is the actual account of an injected, upstream monitor
// to track memory usage of kvDescriptors.
memAcc mon.BoundAccount
}
// allDescriptors is an abstraction to capture the complete set of descriptors
// read from the store. It is used to accelerate repeated invocations of virtual
// tables which utilize descriptors. It tends to get used to build a
// sql.internalLookupCtx.
//
// TODO(ajwerner): Memory monitoring.
// TODO(ajwerner): Unify this struct with the uncommittedDescriptors set.
// TODO(ajwerner): Unify the sql.internalLookupCtx with the descs.Collection.
type allDescriptors struct {
c nstree.Catalog
}
func (d *allDescriptors) init(c nstree.Catalog) {
d.c = c
}
func (d *allDescriptors) clear() {
*d = allDescriptors{}
}
func (d *allDescriptors) isUnset() bool {
return !d.c.IsInitialized()
}
func (d *allDescriptors) contains(id descpb.ID) bool {
return d.c.IsInitialized() && d.c.LookupDescriptorEntry(id) != nil
}
func makeKVDescriptors(
codec keys.SQLCodec, systemNamespace *systemDatabaseNamespaceCache, monitor *mon.BytesMonitor,
) kvDescriptors {
return kvDescriptors{
codec: codec,
systemNamespace: systemNamespace,
memAcc: monitor.MakeBoundAccount(),
}
}
func (kd *kvDescriptors) reset(ctx context.Context) {
kd.releaseAllDescriptors()
kd.memAcc.Clear(ctx)
}
// releaseAllDescriptors releases the cached slice of all descriptors
// held by Collection.
//
// TODO(ajwerner): Make this unnecessary by ensuring that all writes properly
// interact with this layer.
func (kd *kvDescriptors) releaseAllDescriptors() {
kd.allDescriptors.clear()
kd.allDatabaseDescriptors = nil
kd.allSchemasForDatabase = nil
}
// lookupName is used when reading a descriptor from the storage layer by name.
// Descriptors are physically keyed by ID, so we need to resolve their ID by
// querying the system.namespace table first, which is what this method does.
// We can avoid having to do this in some special cases:
// - When the descriptor name and ID are hard-coded. This is the case for the
// system database and for the tables in it.
// - When we're looking up a schema for which we already have the descriptor
// of the parent database. The schema ID can be looked up in it.
//
func (kd *kvDescriptors) lookupName(
ctx context.Context,
txn *kv.Txn,
maybeDB catalog.DatabaseDescriptor,
parentID descpb.ID,
parentSchemaID descpb.ID,
name string,
) (id descpb.ID, err error) {
// Handle special cases which might avoid a namespace table query.
switch parentID {
case descpb.InvalidID:
if name == systemschema.SystemDatabaseName {
// Special case: looking up the system database.
// The system database's descriptor ID is hard-coded.
return keys.SystemDatabaseID, nil
}
case keys.SystemDatabaseID:
// Special case: looking up something in the system database.
// Those namespace table entries are cached.
id = kd.systemNamespace.lookup(parentSchemaID, name)
if id != descpb.InvalidID {
return id, err
}
// Make sure to cache the result if we had to look it up.
defer func() {
if err == nil && id != descpb.InvalidID {
kd.systemNamespace.add(descpb.NameInfo{
ParentID: keys.SystemDatabaseID,
ParentSchemaID: parentSchemaID,
Name: name,
}, id)
}
}()
default:
if parentSchemaID == descpb.InvalidID {
// At this point we know that parentID is not zero, so a zero
// parentSchemaID means we're looking up a schema.
if maybeDB != nil {
// Special case: looking up a schema, but in a database which we already
// have the descriptor for. We find the schema ID in there.
id := maybeDB.GetSchemaID(name)
return id, nil
}
}
}
// Fall back to querying the namespace table.
return catkv.LookupID(
ctx, txn, kd.codec, parentID, parentSchemaID, name,
)
}
// getByName reads a descriptor from the storage layer by name.
//
// This is a three-step process:
// 1. resolve the descriptor's ID using the name information,
// 2. actually read the descriptor from storage,
// 3. check that the name in the descriptor is the one we expect; meaning that
// there is no RENAME underway for instance.
//
func (kd *kvDescriptors) getByName(
ctx context.Context,
version clusterversion.ClusterVersion,
txn *kv.Txn,
vd validate.ValidationDereferencer,
maybeDB catalog.DatabaseDescriptor,
parentID descpb.ID,
parentSchemaID descpb.ID,
name string,
) (catalog.Descriptor, error) {
descID, err := kd.lookupName(ctx, txn, maybeDB, parentID, parentSchemaID, name)
if err != nil || descID == descpb.InvalidID {
return nil, err
}
descs, err := kd.getByIDs(ctx, version, txn, vd, []descpb.ID{descID})
if err != nil {
if errors.Is(err, catalog.ErrDescriptorNotFound) {
// Having done the namespace lookup, the descriptor must exist.
return nil, errors.WithAssertionFailure(err)
}
return nil, err
}
if descs[0].GetName() != name {
// Immediately after a RENAME an old name still points to the descriptor
// during the drain phase for the name. Do not return a descriptor during
// draining.
//
// TODO(postamar): remove this after 22.1 is release.
// At that point, draining names will no longer have to be supported.
// We can then consider making the descriptor collection aware of
// uncommitted namespace operations.
return nil, nil
}
return descs[0], nil
}
// getByIDs actually reads a batch of descriptors from the storage layer.
func (kd *kvDescriptors) getByIDs(
ctx context.Context,
version clusterversion.ClusterVersion,
txn *kv.Txn,
vd validate.ValidationDereferencer,
ids []descpb.ID,
) ([]catalog.Descriptor, error) {
ret := make([]catalog.Descriptor, len(ids))
kvIDs := make([]descpb.ID, 0, len(ids))
indexes := make([]int, 0, len(ids))
for i, id := range ids {
if id == keys.SystemDatabaseID {
// Special handling for the system database descriptor.
//
// This is done for performance reasons, to save ourselves an unnecessary
// round trip to storage which otherwise quickly compounds.
//
// The system database descriptor should never actually be mutated, which is
// why we return the same hard-coded descriptor every time. It's assumed
// that callers of this method will check the privileges on the descriptor
// (like any other database) and return an error.
ret[i] = dbdesc.NewBuilder(systemschema.SystemDB.DatabaseDesc()).BuildExistingMutable()
} else {
kvIDs = append(kvIDs, id)
indexes = append(indexes, i)
}
}
if len(kvIDs) == 0 {
return ret, nil
}
kvDescs, err := catkv.MustGetDescriptorsByID(ctx, version, kd.codec, txn, vd, kvIDs, catalog.Any)
if err != nil {
return nil, err
}
for j, desc := range kvDescs {
ret[indexes[j]] = desc
}
return ret, nil
}
func (kd *kvDescriptors) getAllDescriptors(
ctx context.Context, txn *kv.Txn, version clusterversion.ClusterVersion,
) (nstree.Catalog, error) {
if kd.allDescriptors.isUnset() {
c, err := catkv.GetCatalogUnvalidated(ctx, kd.codec, txn)
if err != nil {
return nstree.Catalog{}, err
}
// Monitor memory usage of the catalog recently read from storage.
if err := kd.memAcc.Grow(ctx, c.ByteSize()); err != nil {
err = errors.Wrap(err, "Memory usage exceeds limit for kvDescriptors.allDescriptors.")
return nstree.Catalog{}, err
}
descs := c.OrderedDescriptors()
ve := c.Validate(ctx, version, catalog.ValidationReadTelemetry, catalog.ValidationLevelCrossReferences, descs...)
if err := ve.CombinedError(); err != nil {
return nstree.Catalog{}, err
}
// There could be tables with user defined types that need hydrating.
if err := HydrateGivenDescriptors(ctx, descs); err != nil {
// If we ran into an error hydrating the types, that means that we
// have some sort of corrupted descriptor state. Rather than disable
// uses of getAllDescriptors, just log the error.
log.Errorf(ctx, "%s", err.Error())
}
kd.allDescriptors.init(c)
}
return kd.allDescriptors.c, nil
}
func (kd *kvDescriptors) getAllDatabaseDescriptors(
ctx context.Context,
version clusterversion.ClusterVersion,
txn *kv.Txn,
vd validate.ValidationDereferencer,
) ([]catalog.DatabaseDescriptor, error) {
if kd.allDatabaseDescriptors == nil {
c, err := catkv.GetAllDatabaseDescriptorIDs(ctx, txn, kd.codec)
if err != nil {
return nil, err
}
dbDescs, err := catkv.MustGetDescriptorsByID(ctx, version, kd.codec, txn, vd, c.OrderedDescriptorIDs(), catalog.Database)
if err != nil {
return nil, err
}
kd.allDatabaseDescriptors = make([]catalog.DatabaseDescriptor, len(dbDescs))
for i, dbDesc := range dbDescs {
kd.allDatabaseDescriptors[i] = dbDesc.(catalog.DatabaseDescriptor)
}
}
return kd.allDatabaseDescriptors, nil
}
func (kd *kvDescriptors) getSchemasForDatabase(
ctx context.Context, txn *kv.Txn, db catalog.DatabaseDescriptor,
) (map[descpb.ID]string, error) {
if kd.allSchemasForDatabase == nil {
kd.allSchemasForDatabase = make(map[descpb.ID]map[descpb.ID]string)
}
if _, ok := kd.allSchemasForDatabase[db.GetID()]; !ok {
var err error
allSchemas, err := resolver.GetForDatabase(ctx, txn, kd.codec, db)
if err != nil {
return nil, err
}
kd.allSchemasForDatabase[db.GetID()] = make(map[descpb.ID]string)
for id, entry := range allSchemas {
kd.allSchemasForDatabase[db.GetID()][id] = entry.Name
}
}
return kd.allSchemasForDatabase[db.GetID()], nil
}
func (kd *kvDescriptors) idDefinitelyDoesNotExist(id descpb.ID) bool {
if kd.allDescriptors.isUnset() {
return false
}
return !kd.allDescriptors.contains(id)
}