summaryrefslogtreecommitdiff
path: root/src/mongo/db/catalog/uncommitted_catalog_updates.h
blob: 9c2a7057229fa17e67b9c69da7044f0500079c09 (plain)
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
/**
 *    Copyright (C) 2018-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    <http://www.mongodb.com/licensing/server-side-public-license>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#pragma once

#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/collection_catalog.h"
#include "mongo/db/catalog/views_for_database.h"
#include "mongo/db/database_name.h"
#include "mongo/db/views/view.h"
#include "mongo/util/uuid.h"

namespace mongo {

/**
 * Decoration on Snapshot to store cloned Collections until they are committed or rolled back.
 */
class UncommittedCatalogUpdates {
public:
    struct Entry {
        enum class Action {
            // Created collection instance
            kCreatedCollection,
            // Writable clone
            kWritableCollection,
            // Marker to indicate that the namespace has been renamed
            kRenamedCollection,
            // Dropped collection instance
            kDroppedCollection,
            // Recreated collection after drop
            kRecreatedCollection,
            // Replaced views for a particular database
            kReplacedViewsForDatabase,
            // Add a view resource
            kAddViewResource,
            // Remove a view resource
            kRemoveViewResource,
            // Dropped index instance
            kDroppedIndex,
        };

        boost::optional<UUID> uuid() const {
            if (action == Action::kCreatedCollection || action == Action::kWritableCollection ||
                action == Action::kRenamedCollection)
                return collection->uuid();
            return externalUUID;
        }

        // Type of action this entry has stored. Members below may or may not be set depending on
        // this member.
        Action action;

        // Storage for the actual collection.
        // Set for actions kWritableCollection, kCreatedCollection, kRecreatedCollection and nullptr
        // otherwise.
        std::shared_ptr<Collection> collection;

        // Store namespace separately to handle rename and drop without making writable first.
        // Set for all actions.
        NamespaceString nss;

        // External uuid when not accessible via collection.
        // Set for actions kDroppedCollection, kRecreatedCollection. boost::none otherwise.
        boost::optional<UUID> externalUUID;

        // New namespace this collection has been renamed to.
        // Set for action kRenamedCollection. Default constructed otherwise.
        NamespaceString renameTo;

        // New set of view information for a database.
        // Set for action kReplacedViewsForDatabase, boost::none otherwise.
        boost::optional<ViewsForDatabase> viewsForDb;

        // Storage for the actual index entry.
        // Set for action kDroppedIndex and nullptr otherwise.
        std::shared_ptr<IndexCatalogEntry> indexEntry;

        // Whether the collection or index entry is drop pending.
        // Set for actions kDroppedCollection and kDroppedIndex, boost::none otherwise.
        boost::optional<bool> isDropPending;
    };

    struct CollectionLookupResult {
        // True if the collection is currently being managed in this transaction.
        bool found;

        // Storage for the actual collection.
        // Set for actions kWritableCollection, kCreatedCollection, and kRecreatedCollection.
        std::shared_ptr<Collection> collection;

        // True if the collection was created during this transaction for the first time.
        bool newColl;
    };

    UncommittedCatalogUpdates() {}
    ~UncommittedCatalogUpdates() = default;

    /**
     * Determine if an entry is associated with a collection action (as opposed to a view action).
     */
    static bool isCollectionEntry(const Entry& entry) {
        return (entry.action == Entry::Action::kCreatedCollection ||
                entry.action == Entry::Action::kWritableCollection ||
                entry.action == Entry::Action::kRenamedCollection ||
                entry.action == Entry::Action::kDroppedCollection ||
                entry.action == Entry::Action::kRecreatedCollection);
    }

    /**
     * Determine if an entry uses two-phase commit to write into the CollectionCatalog.
     * kCreatedCollection is also committed using two-phase commit but using a separate system and
     * is excluded from this list. kDroppedIndex is covered by kWritableCollection as a writable
     * collection must be used to drop an index.
     */
    static bool isTwoPhaseCommitEntry(const Entry& entry) {
        return (entry.action == Entry::Action::kWritableCollection ||
                entry.action == Entry::Action::kRenamedCollection ||
                entry.action == Entry::Action::kDroppedCollection ||
                entry.action == Entry::Action::kRecreatedCollection);
    }

    /**
     * Lookup of Collection by UUID describing whether this namespace is managed, a managed
     * Collection pointer (may be returned as nullptr, which indicates a drop), and if it was
     * created in this transaction.
     */
    static UncommittedCatalogUpdates::CollectionLookupResult lookupCollection(
        OperationContext* opCtx, UUID uuid);

    /**
     * Lookup of Collection by Namestring describing whether this namespace is managed, a managed
     * Collection pointer (may be returned as nullptr, which indicates a drop), and if it was
     * created in this transaction.
     */
    static CollectionLookupResult lookupCollection(OperationContext* opCtx,
                                                   const NamespaceString& nss);

    boost::optional<const ViewsForDatabase&> getViewsForDatabase(const DatabaseName& dbName) const;

    /**
     * Add collection to entries and register RecoveryUnit preCommitHook to throw a
     * `WriteConflictException` if there is a NamespaceString conflict in the catalog.
     */
    void createCollection(OperationContext* opCtx, std::shared_ptr<Collection> coll);

    /**
     * Wraps 'createCollection' and does not register a preCommitHook in order to defer committing a
     * collection after a collection drop.
     */
    void recreateCollection(OperationContext* opCtx, std::shared_ptr<Collection> coll);

    /**
     * Manage the lifetime of uncommitted writable collection.
     */
    void writableCollection(std::shared_ptr<Collection> collection);

    /**
     * Manage an uncommitted rename, pointer must have made writable first and should exist in entry
     * list.
     */
    void renameCollection(const Collection* collection, const NamespaceString& from);

    /**
     * Manages an uncommitted index entry drop.
     */
    void dropIndex(const NamespaceString& nss,
                   std::shared_ptr<IndexCatalogEntry> indexEntry,
                   bool isDropPending);

    /**
     * Manage an uncommitted collection drop.
     */
    void dropCollection(const Collection* collection, bool isDropPending);

    /**
     * Replace the ViewsForDatabase instance assocated with database `dbName` with `vfdb`. This is
     * the primary low-level write method to alter any information about the views associated with a
     * given database.
     */
    void replaceViewsForDatabase(const DatabaseName& dbName, ViewsForDatabase&& vfdb);

    /**
     * Adds a ResourceID associated with a view namespace, and registers a preCommitHook to do
     * conflict-checking on the view namespace.
     */
    void addView(OperationContext* opCtx, const NamespaceString& nss);

    /**
     * Removes the ResourceID associated with a view namespace.
     */
    void removeView(const NamespaceString& nss);

    /**
     * Returns all entries without releasing them.
     */
    const std::vector<Entry>& entries() const;

    /**
     * Releases all entries, needs to be done when WriteUnitOfWork commits or rolls back.
     */
    std::vector<Entry> releaseEntries();

    /**
     * The catalog needs to ignore external view changes for its own modifications. This method
     * should be used by DDL operations to prevent op observers from triggering additional catalog
     * operations.
     */
    void setIgnoreExternalViewChanges(const DatabaseName& dbName, bool value);

    /**
     * The catalog needs to ignore external view changes for its own modifications. This method can
     * be used by methods called by op observers (e.g. 'CollectionCatalog::reload()') to distinguish
     * between an external write to 'system.views' and one initiated through the proper view DDL
     * operations.
     */
    bool shouldIgnoreExternalViewChanges(const DatabaseName& dbName) const;

    /**
     * Checks if there is an entry with the nss `nss` and the
     * 'kCreatedCollection'/'kRecreatedCollection' action type.
     */
    static bool isCreatedCollection(OperationContext* opCtx, const NamespaceString& nss);

    bool isEmpty() {
        return _entries.empty();
    }

    static UncommittedCatalogUpdates& get(OperationContext* opCtx);

private:
    /**
     * Adds a created or recreated collection to the entries vector and registers rollback handlers
     * (in addition to a preCommitHook for newly created collections).
     */
    void _createCollection(OperationContext* opCtx,
                           std::shared_ptr<Collection> coll,
                           Entry::Action action);

    /**
     * Store entries in vector, we will do linear search to find what we're looking for but it will
     * be very few entries so it should be fine.
     */
    std::vector<Entry> _entries;

    stdx::unordered_set<DatabaseName> _ignoreExternalViewChanges;
};

/**
 * Decoration on Snapshot to store Collections instantiated from durable catalog data. Lifetime tied
 * to Snapshot lifetime.
 */
class OpenedCollections {
public:
    static OpenedCollections& get(OperationContext* opCtx);

    /**
     * Lookup collection instance by namespace.
     *
     * May return nullptr which indicates that the namespace does not exist in the snapshot.
     *
     * Returns boost::none if this namespace is unknown to OpenedCollections.
     */
    boost::optional<std::shared_ptr<const Collection>> lookupByNamespace(
        const NamespaceString& ns) const;

    /**
     * Lookup collection instance by UUID.
     *
     * May return nullptr which indicates that the UUID does not exist in the snapshot.
     *
     * Returns boost::none if this UUID is unknown to OpenedCollections.
     */
    boost::optional<std::shared_ptr<const Collection>> lookupByUUID(UUID uuid) const;

    /**
     * Stores a Collection instance. Lifetime of instance will be tied to lifetime of opened storage
     * snapshot.
     *
     * Collection instance may be nullptr to indicate that the namespace and/or UUID does not exist
     * in the snapshot.
     */
    void store(std::shared_ptr<const Collection> coll,
               boost::optional<NamespaceString> nss,
               boost::optional<UUID> uuid);

private:
    struct Entry {
        std::shared_ptr<const Collection> collection;
        boost::optional<NamespaceString> nss;
        boost::optional<UUID> uuid;
    };

    // Static storage for one entry. The expected common case is that only a single collection will
    // be needed so we optimize for that.
    boost::container::small_vector<Entry, 1> _collections;
};

}  // namespace mongo