1 // kv_catalog.cpp
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
34 
35 #include "mongo/db/storage/kv/kv_catalog.h"
36 
37 #include <stdlib.h>
38 
39 #include "mongo/bson/util/bson_extract.h"
40 #include "mongo/bson/util/builder.h"
41 #include "mongo/db/concurrency/d_concurrency.h"
42 #include "mongo/db/namespace_string.h"
43 #include "mongo/db/operation_context.h"
44 #include "mongo/db/storage/kv/kv_catalog_feature_tracker.h"
45 #include "mongo/db/storage/record_store.h"
46 #include "mongo/db/storage/recovery_unit.h"
47 #include "mongo/platform/bits.h"
48 #include "mongo/platform/random.h"
49 #include "mongo/stdx/memory.h"
50 #include "mongo/util/log.h"
51 #include "mongo/util/mongoutils/str.h"
52 
53 namespace mongo {
54 namespace {
55 // This is a global resource, which protects accesses to the catalog metadata (instance-wide).
56 // It is never used with KVEngines that support doc-level locking so this should never conflict
57 // with anything else.
58 
59 const char kIsFeatureDocumentFieldName[] = "isFeatureDoc";
60 const char kNamespaceFieldName[] = "ns";
61 const char kNonRepairableFeaturesFieldName[] = "nonRepairable";
62 const char kRepairableFeaturesFieldName[] = "repairable";
63 
appendPositionsOfBitsSet(uint64_t value,StringBuilder * sb)64 void appendPositionsOfBitsSet(uint64_t value, StringBuilder* sb) {
65     invariant(sb);
66 
67     *sb << "[ ";
68     bool firstIteration = true;
69     while (value) {
70         const int lowestSetBitPosition = countTrailingZeros64(value);
71         if (!firstIteration) {
72             *sb << ", ";
73         }
74         *sb << lowestSetBitPosition;
75         value ^= (1ULL << lowestSetBitPosition);
76         firstIteration = false;
77     }
78     *sb << " ]";
79 }
80 }
81 
82 using std::unique_ptr;
83 using std::string;
84 
85 class KVCatalog::AddIdentChange : public RecoveryUnit::Change {
86 public:
AddIdentChange(KVCatalog * catalog,StringData ident)87     AddIdentChange(KVCatalog* catalog, StringData ident)
88         : _catalog(catalog), _ident(ident.toString()) {}
89 
commit()90     virtual void commit() {}
rollback()91     virtual void rollback() {
92         stdx::lock_guard<stdx::mutex> lk(_catalog->_identsLock);
93         _catalog->_idents.erase(_ident);
94     }
95 
96     KVCatalog* const _catalog;
97     const std::string _ident;
98 };
99 
100 class KVCatalog::RemoveIdentChange : public RecoveryUnit::Change {
101 public:
RemoveIdentChange(KVCatalog * catalog,StringData ident,const Entry & entry)102     RemoveIdentChange(KVCatalog* catalog, StringData ident, const Entry& entry)
103         : _catalog(catalog), _ident(ident.toString()), _entry(entry) {}
104 
commit()105     virtual void commit() {}
rollback()106     virtual void rollback() {
107         stdx::lock_guard<stdx::mutex> lk(_catalog->_identsLock);
108         _catalog->_idents[_ident] = _entry;
109     }
110 
111     KVCatalog* const _catalog;
112     const std::string _ident;
113     const Entry _entry;
114 };
115 
isFeatureDocument(BSONObj obj)116 bool KVCatalog::FeatureTracker::isFeatureDocument(BSONObj obj) {
117     BSONElement firstElem = obj.firstElement();
118     if (firstElem.fieldNameStringData() == kIsFeatureDocumentFieldName) {
119         return firstElem.booleanSafe();
120     }
121     return false;
122 }
123 
isCompatibleWithCurrentCode(OperationContext * opCtx) const124 Status KVCatalog::FeatureTracker::isCompatibleWithCurrentCode(OperationContext* opCtx) const {
125     FeatureBits versionInfo = getInfo(opCtx);
126 
127     uint64_t unrecognizedNonRepairableFeatures =
128         versionInfo.nonRepairableFeatures & ~_usedNonRepairableFeaturesMask;
129     if (unrecognizedNonRepairableFeatures) {
130         StringBuilder sb;
131         sb << "The data files use features not recognized by this version of mongod; the NR feature"
132               " bits in positions ";
133         appendPositionsOfBitsSet(unrecognizedNonRepairableFeatures, &sb);
134         sb << " aren't recognized by this version of mongod";
135         return {ErrorCodes::MustUpgrade, sb.str()};
136     }
137 
138     uint64_t unrecognizedRepairableFeatures =
139         versionInfo.repairableFeatures & ~_usedRepairableFeaturesMask;
140     if (unrecognizedRepairableFeatures) {
141         StringBuilder sb;
142         sb << "The data files use features not recognized by this version of mongod; the R feature"
143               " bits in positions ";
144         appendPositionsOfBitsSet(unrecognizedRepairableFeatures, &sb);
145         sb << " aren't recognized by this version of mongod";
146         return {ErrorCodes::CanRepairToDowngrade, sb.str()};
147     }
148 
149     return Status::OK();
150 }
151 
get(OperationContext * opCtx,KVCatalog * catalog,RecordId rid)152 std::unique_ptr<KVCatalog::FeatureTracker> KVCatalog::FeatureTracker::get(OperationContext* opCtx,
153                                                                           KVCatalog* catalog,
154                                                                           RecordId rid) {
155     auto record = catalog->_rs->dataFor(opCtx, rid);
156     BSONObj obj = record.toBson();
157     invariant(isFeatureDocument(obj));
158     return std::unique_ptr<KVCatalog::FeatureTracker>(new KVCatalog::FeatureTracker(catalog, rid));
159 }
160 
create(OperationContext * opCtx,KVCatalog * catalog)161 std::unique_ptr<KVCatalog::FeatureTracker> KVCatalog::FeatureTracker::create(
162     OperationContext* opCtx, KVCatalog* catalog) {
163     return std::unique_ptr<KVCatalog::FeatureTracker>(
164         new KVCatalog::FeatureTracker(catalog, RecordId()));
165 }
166 
isNonRepairableFeatureInUse(OperationContext * opCtx,NonRepairableFeature feature) const167 bool KVCatalog::FeatureTracker::isNonRepairableFeatureInUse(OperationContext* opCtx,
168                                                             NonRepairableFeature feature) const {
169     FeatureBits versionInfo = getInfo(opCtx);
170     return versionInfo.nonRepairableFeatures & static_cast<NonRepairableFeatureMask>(feature);
171 }
172 
markNonRepairableFeatureAsInUse(OperationContext * opCtx,NonRepairableFeature feature)173 void KVCatalog::FeatureTracker::markNonRepairableFeatureAsInUse(OperationContext* opCtx,
174                                                                 NonRepairableFeature feature) {
175     FeatureBits versionInfo = getInfo(opCtx);
176     versionInfo.nonRepairableFeatures |= static_cast<NonRepairableFeatureMask>(feature);
177     putInfo(opCtx, versionInfo);
178 }
179 
markNonRepairableFeatureAsNotInUse(OperationContext * opCtx,NonRepairableFeature feature)180 void KVCatalog::FeatureTracker::markNonRepairableFeatureAsNotInUse(OperationContext* opCtx,
181                                                                    NonRepairableFeature feature) {
182     FeatureBits versionInfo = getInfo(opCtx);
183     versionInfo.nonRepairableFeatures &= ~static_cast<NonRepairableFeatureMask>(feature);
184     putInfo(opCtx, versionInfo);
185 }
186 
isRepairableFeatureInUse(OperationContext * opCtx,RepairableFeature feature) const187 bool KVCatalog::FeatureTracker::isRepairableFeatureInUse(OperationContext* opCtx,
188                                                          RepairableFeature feature) const {
189     FeatureBits versionInfo = getInfo(opCtx);
190     return versionInfo.repairableFeatures & static_cast<RepairableFeatureMask>(feature);
191 }
192 
markRepairableFeatureAsInUse(OperationContext * opCtx,RepairableFeature feature)193 void KVCatalog::FeatureTracker::markRepairableFeatureAsInUse(OperationContext* opCtx,
194                                                              RepairableFeature feature) {
195     FeatureBits versionInfo = getInfo(opCtx);
196     versionInfo.repairableFeatures |= static_cast<RepairableFeatureMask>(feature);
197     putInfo(opCtx, versionInfo);
198 }
199 
markRepairableFeatureAsNotInUse(OperationContext * opCtx,RepairableFeature feature)200 void KVCatalog::FeatureTracker::markRepairableFeatureAsNotInUse(OperationContext* opCtx,
201                                                                 RepairableFeature feature) {
202     FeatureBits versionInfo = getInfo(opCtx);
203     versionInfo.repairableFeatures &= ~static_cast<RepairableFeatureMask>(feature);
204     putInfo(opCtx, versionInfo);
205 }
206 
getInfo(OperationContext * opCtx) const207 KVCatalog::FeatureTracker::FeatureBits KVCatalog::FeatureTracker::getInfo(
208     OperationContext* opCtx) const {
209     if (_rid.isNull()) {
210         return {};
211     }
212 
213     auto record = _catalog->_rs->dataFor(opCtx, _rid);
214     BSONObj obj = record.toBson();
215     invariant(isFeatureDocument(obj));
216 
217     BSONElement nonRepairableFeaturesElem;
218     auto nonRepairableFeaturesStatus = bsonExtractTypedField(
219         obj, kNonRepairableFeaturesFieldName, BSONType::NumberLong, &nonRepairableFeaturesElem);
220     fassert(40111, nonRepairableFeaturesStatus);
221 
222     BSONElement repairableFeaturesElem;
223     auto repairableFeaturesStatus = bsonExtractTypedField(
224         obj, kRepairableFeaturesFieldName, BSONType::NumberLong, &repairableFeaturesElem);
225     fassert(40112, repairableFeaturesStatus);
226 
227     FeatureBits versionInfo;
228     versionInfo.nonRepairableFeatures =
229         static_cast<NonRepairableFeatureMask>(nonRepairableFeaturesElem.numberLong());
230     versionInfo.repairableFeatures =
231         static_cast<RepairableFeatureMask>(repairableFeaturesElem.numberLong());
232     return versionInfo;
233 }
234 
putInfo(OperationContext * opCtx,const FeatureBits & versionInfo)235 void KVCatalog::FeatureTracker::putInfo(OperationContext* opCtx, const FeatureBits& versionInfo) {
236     BSONObjBuilder bob;
237     bob.appendBool(kIsFeatureDocumentFieldName, true);
238     // We intentionally include the "ns" field with a null value in the feature document to prevent
239     // older versions that do 'obj["ns"].String()' from starting up. This way only versions that are
240     // aware of the feature document's existence can successfully start up.
241     bob.appendNull(kNamespaceFieldName);
242     bob.append(kNonRepairableFeaturesFieldName,
243                static_cast<long long>(versionInfo.nonRepairableFeatures));
244     bob.append(kRepairableFeaturesFieldName,
245                static_cast<long long>(versionInfo.repairableFeatures));
246     BSONObj obj = bob.done();
247 
248     if (_rid.isNull()) {
249         // This is the first time a feature is being marked as in-use or not in-use, so we must
250         // insert the feature document rather than update it.
251         const bool enforceQuota = false;
252         // TODO SERVER-30638: using timestamp 0 for these inserts
253         auto rid = _catalog->_rs->insertRecord(
254             opCtx, obj.objdata(), obj.objsize(), Timestamp(), enforceQuota);
255         fassert(40113, rid.getStatus());
256         _rid = rid.getValue();
257     } else {
258         const bool enforceQuota = false;
259         UpdateNotifier* notifier = nullptr;
260         auto status = _catalog->_rs->updateRecord(
261             opCtx, _rid, obj.objdata(), obj.objsize(), enforceQuota, notifier);
262         fassert(40114, status);
263     }
264 }
265 
KVCatalog(RecordStore * rs,bool directoryPerDb,bool directoryForIndexes)266 KVCatalog::KVCatalog(RecordStore* rs, bool directoryPerDb, bool directoryForIndexes)
267     : _rs(rs),
268       _directoryPerDb(directoryPerDb),
269       _directoryForIndexes(directoryForIndexes),
270       _rand(_newRand()) {}
271 
~KVCatalog()272 KVCatalog::~KVCatalog() {
273     _rs = NULL;
274 }
275 
_newRand()276 std::string KVCatalog::_newRand() {
277     return str::stream() << std::unique_ptr<SecureRandom>(SecureRandom::create())->nextInt64();
278 }
279 
_hasEntryCollidingWithRand() const280 bool KVCatalog::_hasEntryCollidingWithRand() const {
281     // Only called from init() so don't need to lock.
282     for (NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it) {
283         if (StringData(it->first).endsWith(_rand))
284             return true;
285     }
286     return false;
287 }
288 
getFilesystemPathForDb(const std::string & dbName) const289 std::string KVCatalog::getFilesystemPathForDb(const std::string& dbName) const {
290     if (_directoryPerDb) {
291         return storageGlobalParams.dbpath + '/' + NamespaceString::escapeDbName(dbName);
292     } else {
293         return storageGlobalParams.dbpath;
294     }
295 }
296 
_newUniqueIdent(StringData ns,const char * kind)297 std::string KVCatalog::_newUniqueIdent(StringData ns, const char* kind) {
298     // If this changes to not put _rand at the end, _hasEntryCollidingWithRand will need fixing.
299     StringBuilder buf;
300     if (_directoryPerDb) {
301         buf << NamespaceString::escapeDbName(nsToDatabaseSubstring(ns)) << '/';
302     }
303     buf << kind;
304     buf << (_directoryForIndexes ? '/' : '-');
305     buf << _next.fetchAndAdd(1) << '-' << _rand;
306     return buf.str();
307 }
308 
init(OperationContext * opCtx)309 void KVCatalog::init(OperationContext* opCtx) {
310     // No locking needed since called single threaded.
311     auto cursor = _rs->getCursor(opCtx);
312     while (auto record = cursor->next()) {
313         BSONObj obj = record->data.releaseToBson();
314 
315         if (FeatureTracker::isFeatureDocument(obj)) {
316             // There should be at most one version document in the catalog.
317             invariant(!_featureTracker);
318 
319             // Initialize the feature tracker and skip over the version document because it doesn't
320             // correspond to a namespace entry.
321             _featureTracker = FeatureTracker::get(opCtx, this, record->id);
322             continue;
323         }
324 
325         // No rollback since this is just loading already committed data.
326         string ns = obj["ns"].String();
327         string ident = obj["ident"].String();
328         _idents[ns] = Entry(ident, record->id);
329     }
330 
331     if (!_featureTracker) {
332         // If there wasn't a feature document, then just an initialize a feature tracker that
333         // doesn't manage a feature document yet.
334         _featureTracker = KVCatalog::FeatureTracker::create(opCtx, this);
335     }
336 
337     // In the unlikely event that we have used this _rand before generate a new one.
338     while (_hasEntryCollidingWithRand()) {
339         _rand = _newRand();
340     }
341 }
342 
getAllCollections(std::vector<std::string> * out) const343 void KVCatalog::getAllCollections(std::vector<std::string>* out) const {
344     stdx::lock_guard<stdx::mutex> lk(_identsLock);
345     for (NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it) {
346         out->push_back(it->first);
347     }
348 }
349 
newCollection(OperationContext * opCtx,StringData ns,const CollectionOptions & options,KVPrefix prefix)350 Status KVCatalog::newCollection(OperationContext* opCtx,
351                                 StringData ns,
352                                 const CollectionOptions& options,
353                                 KVPrefix prefix) {
354     invariant(opCtx->lockState()->isDbLockedForMode(nsToDatabaseSubstring(ns), MODE_X));
355 
356     const string ident = _newUniqueIdent(ns, "collection");
357 
358     stdx::lock_guard<stdx::mutex> lk(_identsLock);
359     Entry& old = _idents[ns.toString()];
360     if (!old.ident.empty()) {
361         return Status(ErrorCodes::NamespaceExists, "collection already exists");
362     }
363 
364     opCtx->recoveryUnit()->registerChange(new AddIdentChange(this, ns));
365 
366     BSONObj obj;
367     {
368         BSONObjBuilder b;
369         b.append("ns", ns);
370         b.append("ident", ident);
371         BSONCollectionCatalogEntry::MetaData md;
372         md.ns = ns.toString();
373         md.options = options;
374         md.prefix = prefix;
375         b.append("md", md.toBSON());
376         obj = b.obj();
377     }
378     const bool enforceQuota = false;
379     // TODO SERVER-30638: using timestamp 0 for these inserts.
380     StatusWith<RecordId> res =
381         _rs->insertRecord(opCtx, obj.objdata(), obj.objsize(), Timestamp(), enforceQuota);
382     if (!res.isOK())
383         return res.getStatus();
384 
385     old = Entry(ident, res.getValue());
386     LOG(1) << "stored meta data for " << ns << " @ " << res.getValue();
387     return Status::OK();
388 }
389 
getCollectionIdent(StringData ns) const390 std::string KVCatalog::getCollectionIdent(StringData ns) const {
391     stdx::lock_guard<stdx::mutex> lk(_identsLock);
392     NSToIdentMap::const_iterator it = _idents.find(ns.toString());
393     invariant(it != _idents.end());
394     return it->second.ident;
395 }
396 
getIndexIdent(OperationContext * opCtx,StringData ns,StringData idxName) const397 std::string KVCatalog::getIndexIdent(OperationContext* opCtx,
398                                      StringData ns,
399                                      StringData idxName) const {
400     BSONObj obj = _findEntry(opCtx, ns);
401     BSONObj idxIdent = obj["idxIdent"].Obj();
402     return idxIdent[idxName].String();
403 }
404 
_findEntry(OperationContext * opCtx,StringData ns,RecordId * out) const405 BSONObj KVCatalog::_findEntry(OperationContext* opCtx, StringData ns, RecordId* out) const {
406     RecordId dl;
407     {
408         stdx::lock_guard<stdx::mutex> lk(_identsLock);
409         NSToIdentMap::const_iterator it = _idents.find(ns.toString());
410         invariant(it != _idents.end());
411         dl = it->second.storedLoc;
412     }
413 
414     LOG(3) << "looking up metadata for: " << ns << " @ " << dl;
415     RecordData data;
416     if (!_rs->findRecord(opCtx, dl, &data)) {
417         // since the in memory meta data isn't managed with mvcc
418         // its possible for different transactions to see slightly
419         // different things, which is ok via the locking above.
420         return BSONObj();
421     }
422 
423     if (out)
424         *out = dl;
425 
426     return data.releaseToBson().getOwned();
427 }
428 
getMetaData(OperationContext * opCtx,StringData ns)429 const BSONCollectionCatalogEntry::MetaData KVCatalog::getMetaData(OperationContext* opCtx,
430                                                                   StringData ns) {
431     BSONObj obj = _findEntry(opCtx, ns);
432     LOG(3) << " fetched CCE metadata: " << obj;
433     BSONCollectionCatalogEntry::MetaData md;
434     const BSONElement mdElement = obj["md"];
435     if (mdElement.isABSONObj()) {
436         LOG(3) << "returning metadata: " << mdElement;
437         md.parse(mdElement.Obj());
438     }
439     return md;
440 }
441 
putMetaData(OperationContext * opCtx,StringData ns,BSONCollectionCatalogEntry::MetaData & md)442 void KVCatalog::putMetaData(OperationContext* opCtx,
443                             StringData ns,
444                             BSONCollectionCatalogEntry::MetaData& md) {
445     RecordId loc;
446     BSONObj obj = _findEntry(opCtx, ns, &loc);
447 
448     {
449         // rebuilt doc
450         BSONObjBuilder b;
451         b.append("md", md.toBSON());
452 
453         BSONObjBuilder newIdentMap;
454         BSONObj oldIdentMap;
455         if (obj["idxIdent"].isABSONObj())
456             oldIdentMap = obj["idxIdent"].Obj();
457 
458         // fix ident map
459         for (size_t i = 0; i < md.indexes.size(); i++) {
460             string name = md.indexes[i].name();
461             BSONElement e = oldIdentMap[name];
462             if (e.type() == String) {
463                 newIdentMap.append(e);
464                 continue;
465             }
466             // missing, create new
467             newIdentMap.append(name, _newUniqueIdent(ns, "index"));
468         }
469         b.append("idxIdent", newIdentMap.obj());
470 
471         // add whatever is left
472         b.appendElementsUnique(obj);
473         obj = b.obj();
474     }
475 
476     LOG(3) << "recording new metadata: " << obj;
477     Status status = _rs->updateRecord(opCtx, loc, obj.objdata(), obj.objsize(), false, NULL);
478     fassert(28521, status.isOK());
479 }
480 
renameCollection(OperationContext * opCtx,StringData fromNS,StringData toNS,bool stayTemp)481 Status KVCatalog::renameCollection(OperationContext* opCtx,
482                                    StringData fromNS,
483                                    StringData toNS,
484                                    bool stayTemp) {
485     RecordId loc;
486     BSONObj old = _findEntry(opCtx, fromNS, &loc).getOwned();
487     {
488         BSONObjBuilder b;
489 
490         b.append("ns", toNS);
491 
492         BSONCollectionCatalogEntry::MetaData md;
493         md.parse(old["md"].Obj());
494         md.rename(toNS);
495         if (!stayTemp)
496             md.options.temp = false;
497         b.append("md", md.toBSON());
498 
499         b.appendElementsUnique(old);
500 
501         BSONObj obj = b.obj();
502         Status status = _rs->updateRecord(opCtx, loc, obj.objdata(), obj.objsize(), false, NULL);
503         fassert(28522, status.isOK());
504     }
505 
506     stdx::lock_guard<stdx::mutex> lk(_identsLock);
507     const NSToIdentMap::iterator fromIt = _idents.find(fromNS.toString());
508     invariant(fromIt != _idents.end());
509 
510     opCtx->recoveryUnit()->registerChange(new RemoveIdentChange(this, fromNS, fromIt->second));
511     opCtx->recoveryUnit()->registerChange(new AddIdentChange(this, toNS));
512 
513     _idents.erase(fromIt);
514     _idents[toNS.toString()] = Entry(old["ident"].String(), loc);
515 
516     return Status::OK();
517 }
518 
dropCollection(OperationContext * opCtx,StringData ns)519 Status KVCatalog::dropCollection(OperationContext* opCtx, StringData ns) {
520     invariant(opCtx->lockState()->isDbLockedForMode(nsToDatabaseSubstring(ns), MODE_X));
521     stdx::lock_guard<stdx::mutex> lk(_identsLock);
522     const NSToIdentMap::iterator it = _idents.find(ns.toString());
523     if (it == _idents.end()) {
524         return Status(ErrorCodes::NamespaceNotFound, "collection not found");
525     }
526 
527     opCtx->recoveryUnit()->registerChange(new RemoveIdentChange(this, ns, it->second));
528 
529     LOG(1) << "deleting metadata for " << ns << " @ " << it->second.storedLoc;
530     _rs->deleteRecord(opCtx, it->second.storedLoc);
531     _idents.erase(it);
532 
533     return Status::OK();
534 }
535 
getAllIdentsForDB(StringData db) const536 std::vector<std::string> KVCatalog::getAllIdentsForDB(StringData db) const {
537     std::vector<std::string> v;
538 
539     {
540         stdx::lock_guard<stdx::mutex> lk(_identsLock);
541         for (NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it) {
542             NamespaceString ns(it->first);
543             if (ns.db() != db)
544                 continue;
545             v.push_back(it->second.ident);
546         }
547     }
548 
549     return v;
550 }
551 
getAllIdents(OperationContext * opCtx) const552 std::vector<std::string> KVCatalog::getAllIdents(OperationContext* opCtx) const {
553     std::vector<std::string> v;
554 
555     auto cursor = _rs->getCursor(opCtx);
556     while (auto record = cursor->next()) {
557         BSONObj obj = record->data.releaseToBson();
558         if (FeatureTracker::isFeatureDocument(obj)) {
559             // Skip over the version document because it doesn't correspond to a namespace entry and
560             // therefore doesn't refer to any idents.
561             continue;
562         }
563         v.push_back(obj["ident"].String());
564 
565         BSONElement e = obj["idxIdent"];
566         if (!e.isABSONObj())
567             continue;
568         BSONObj idxIdent = e.Obj();
569 
570         BSONObjIterator sub(idxIdent);
571         while (sub.more()) {
572             BSONElement e = sub.next();
573             v.push_back(e.String());
574         }
575     }
576 
577     return v;
578 }
579 
isUserDataIdent(StringData ident) const580 bool KVCatalog::isUserDataIdent(StringData ident) const {
581     return ident.find("index-") != std::string::npos || ident.find("index/") != std::string::npos ||
582         ident.find("collection-") != std::string::npos ||
583         ident.find("collection/") != std::string::npos;
584 }
585 }
586