1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/platform/basic.h"
32
33 #include "mongo/db/op_observer_impl.h"
34
35 #include "mongo/bson/bsonobjbuilder.h"
36 #include "mongo/db/auth/authorization_manager.h"
37 #include "mongo/db/catalog/collection_catalog_entry.h"
38 #include "mongo/db/catalog/collection_options.h"
39 #include "mongo/db/catalog/database.h"
40 #include "mongo/db/catalog/database_holder.h"
41 #include "mongo/db/catalog/namespace_uuid_cache.h"
42 #include "mongo/db/catalog/uuid_catalog.h"
43 #include "mongo/db/commands/feature_compatibility_version.h"
44 #include "mongo/db/concurrency/d_concurrency.h"
45 #include "mongo/db/index/index_descriptor.h"
46 #include "mongo/db/namespace_string.h"
47 #include "mongo/db/operation_context.h"
48 #include "mongo/db/repl/oplog.h"
49 #include "mongo/db/repl/replication_coordinator.h"
50 #include "mongo/db/s/collection_sharding_state.h"
51 #include "mongo/db/server_options.h"
52 #include "mongo/db/session_catalog.h"
53 #include "mongo/db/views/durable_view_catalog.h"
54 #include "mongo/scripting/engine.h"
55 #include "mongo/util/assert_util.h"
56 #include "mongo/util/fail_point_service.h"
57
58 namespace mongo {
59 namespace {
60
61 MONGO_FP_DECLARE(failCollectionUpdates);
62
63 /**
64 * Returns whether we're a master using master-slave replication.
65 */
isMasterSlave(OperationContext * opCtx)66 bool isMasterSlave(OperationContext* opCtx) {
67 return repl::ReplicationCoordinator::get(opCtx)->getReplicationMode() ==
68 repl::ReplicationCoordinator::modeMasterSlave;
69 }
70
71 /**
72 * Updates the session state with the last write timestamp and transaction for that session.
73 *
74 * In the case of writes with transaction/statement id, this method will be recursively entered a
75 * second time for the actual write to the transactions table. Since this write does not generate an
76 * oplog entry, the recursion will stop at this point.
77 */
onWriteOpCompleted(OperationContext * opCtx,const NamespaceString & nss,Session * session,std::vector<StmtId> stmtIdsWritten,const repl::OpTime & lastStmtIdWriteOpTime,Date_t lastStmtIdWriteDate)78 void onWriteOpCompleted(OperationContext* opCtx,
79 const NamespaceString& nss,
80 Session* session,
81 std::vector<StmtId> stmtIdsWritten,
82 const repl::OpTime& lastStmtIdWriteOpTime,
83 Date_t lastStmtIdWriteDate) {
84 if (lastStmtIdWriteOpTime.isNull())
85 return;
86
87 if (session) {
88 session->onWriteOpCompletedOnPrimary(opCtx,
89 *opCtx->getTxnNumber(),
90 std::move(stmtIdsWritten),
91 lastStmtIdWriteOpTime,
92 lastStmtIdWriteDate);
93 }
94 }
95
96 /**
97 * Given a raw collMod command object and associated collection metadata, create and return the
98 * object for the 'o' field of a collMod oplog entry. For TTL index updates, we make sure the oplog
99 * entry always stores the index name, instead of a key pattern.
100 */
makeCollModCmdObj(const BSONObj & collModCmd,const CollectionOptions & oldCollOptions,boost::optional<TTLCollModInfo> ttlInfo)101 BSONObj makeCollModCmdObj(const BSONObj& collModCmd,
102 const CollectionOptions& oldCollOptions,
103 boost::optional<TTLCollModInfo> ttlInfo) {
104 BSONObjBuilder cmdObjBuilder;
105 std::string ttlIndexFieldName = "index";
106
107 // Add all fields from the original collMod command.
108 for (auto elem : collModCmd) {
109 // We normalize all TTL collMod oplog entry objects to use the index name, even if the
110 // command used an index key pattern.
111 if (elem.fieldNameStringData() == ttlIndexFieldName && ttlInfo) {
112 BSONObjBuilder ttlIndexObjBuilder;
113 ttlIndexObjBuilder.append("name", ttlInfo->indexName);
114 ttlIndexObjBuilder.append("expireAfterSeconds",
115 durationCount<Seconds>(ttlInfo->expireAfterSeconds));
116
117 cmdObjBuilder.append(ttlIndexFieldName, ttlIndexObjBuilder.obj());
118 } else {
119 cmdObjBuilder.append(elem);
120 }
121 }
122
123 return cmdObjBuilder.obj();
124 }
125
getWallClockTimeForOpLog(OperationContext * opCtx)126 Date_t getWallClockTimeForOpLog(OperationContext* opCtx) {
127 auto const clockSource = opCtx->getServiceContext()->getFastClockSource();
128 return clockSource->now();
129 }
130
131 struct OpTimeBundle {
132 repl::OpTime writeOpTime;
133 repl::OpTime prePostImageOpTime;
134 Date_t wallClockTime;
135 };
136
137 /**
138 * Write oplog entry(ies) for the update operation.
139 */
replLogUpdate(OperationContext * opCtx,Session * session,const OplogUpdateEntryArgs & args)140 OpTimeBundle replLogUpdate(OperationContext* opCtx,
141 Session* session,
142 const OplogUpdateEntryArgs& args) {
143 BSONObj storeObj;
144 if (args.storeDocOption == OplogUpdateEntryArgs::StoreDocOption::PreImage) {
145 invariant(args.preImageDoc);
146 storeObj = *args.preImageDoc;
147 } else if (args.storeDocOption == OplogUpdateEntryArgs::StoreDocOption::PostImage) {
148 storeObj = args.updatedDoc;
149 }
150
151 OperationSessionInfo sessionInfo;
152 repl::OplogLink oplogLink;
153
154 if (session) {
155 sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
156 sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
157 oplogLink.prevOpTime = session->getLastWriteOpTime(*opCtx->getTxnNumber());
158 }
159
160 OpTimeBundle opTimes;
161 opTimes.wallClockTime = getWallClockTimeForOpLog(opCtx);
162
163 if (!storeObj.isEmpty() && opCtx->getTxnNumber()) {
164 auto noteUpdateOpTime = repl::logOp(opCtx,
165 "n",
166 args.nss,
167 args.uuid,
168 storeObj,
169 nullptr,
170 false,
171 opTimes.wallClockTime,
172 sessionInfo,
173 args.stmtId,
174 {},
175 OplogSlot());
176
177 opTimes.prePostImageOpTime = noteUpdateOpTime;
178
179 if (args.storeDocOption == OplogUpdateEntryArgs::StoreDocOption::PreImage) {
180 oplogLink.preImageOpTime = noteUpdateOpTime;
181 } else if (args.storeDocOption == OplogUpdateEntryArgs::StoreDocOption::PostImage) {
182 oplogLink.postImageOpTime = noteUpdateOpTime;
183 }
184 }
185
186 opTimes.writeOpTime = repl::logOp(opCtx,
187 "u",
188 args.nss,
189 args.uuid,
190 args.update,
191 &args.criteria,
192 args.fromMigrate,
193 opTimes.wallClockTime,
194 sessionInfo,
195 args.stmtId,
196 oplogLink,
197 OplogSlot());
198
199 return opTimes;
200 }
201
202 /**
203 * Write oplog entry(ies) for the delete operation.
204 */
replLogDelete(OperationContext * opCtx,const NamespaceString & nss,OptionalCollectionUUID uuid,Session * session,StmtId stmtId,const CollectionShardingState::DeleteState & deleteState,bool fromMigrate,const boost::optional<BSONObj> & deletedDoc)205 OpTimeBundle replLogDelete(OperationContext* opCtx,
206 const NamespaceString& nss,
207 OptionalCollectionUUID uuid,
208 Session* session,
209 StmtId stmtId,
210 const CollectionShardingState::DeleteState& deleteState,
211 bool fromMigrate,
212 const boost::optional<BSONObj>& deletedDoc) {
213 OperationSessionInfo sessionInfo;
214 repl::OplogLink oplogLink;
215
216 if (session) {
217 sessionInfo.setSessionId(*opCtx->getLogicalSessionId());
218 sessionInfo.setTxnNumber(*opCtx->getTxnNumber());
219 oplogLink.prevOpTime = session->getLastWriteOpTime(*opCtx->getTxnNumber());
220 }
221
222 OpTimeBundle opTimes;
223 opTimes.wallClockTime = getWallClockTimeForOpLog(opCtx);
224
225 if (deletedDoc && opCtx->getTxnNumber()) {
226 auto noteOplog = repl::logOp(opCtx,
227 "n",
228 nss,
229 uuid,
230 deletedDoc.get(),
231 nullptr,
232 false,
233 opTimes.wallClockTime,
234 sessionInfo,
235 stmtId,
236 {},
237 OplogSlot());
238 opTimes.prePostImageOpTime = noteOplog;
239 oplogLink.preImageOpTime = noteOplog;
240 }
241
242 opTimes.writeOpTime = repl::logOp(opCtx,
243 "d",
244 nss,
245 uuid,
246 deleteState.documentKey,
247 nullptr,
248 fromMigrate,
249 opTimes.wallClockTime,
250 sessionInfo,
251 stmtId,
252 oplogLink,
253 OplogSlot());
254 return opTimes;
255 }
256
257 } // namespace
258
onCreateIndex(OperationContext * opCtx,const NamespaceString & nss,OptionalCollectionUUID uuid,BSONObj indexDoc,bool fromMigrate)259 void OpObserverImpl::onCreateIndex(OperationContext* opCtx,
260 const NamespaceString& nss,
261 OptionalCollectionUUID uuid,
262 BSONObj indexDoc,
263 bool fromMigrate) {
264 const NamespaceString systemIndexes{nss.getSystemIndexesCollection()};
265
266 if (uuid && !isMasterSlave(opCtx)) {
267 BSONObjBuilder builder;
268 builder.append("createIndexes", nss.coll());
269
270 for (const auto& e : indexDoc) {
271 if (e.fieldNameStringData() != "ns"_sd)
272 builder.append(e);
273 }
274
275 repl::logOp(opCtx,
276 "c",
277 nss.getCommandNS(),
278 uuid,
279 builder.done(),
280 nullptr,
281 fromMigrate,
282 getWallClockTimeForOpLog(opCtx),
283 {},
284 kUninitializedStmtId,
285 {},
286 OplogSlot());
287 } else {
288 repl::logOp(opCtx,
289 "i",
290 systemIndexes,
291 {},
292 indexDoc,
293 nullptr,
294 fromMigrate,
295 getWallClockTimeForOpLog(opCtx),
296 {},
297 kUninitializedStmtId,
298 {},
299 OplogSlot());
300 }
301
302 AuthorizationManager::get(opCtx->getServiceContext())
303 ->logOp(opCtx, "i", systemIndexes, indexDoc, nullptr);
304
305 auto css = CollectionShardingState::get(opCtx, systemIndexes);
306 if (!fromMigrate) {
307 css->onInsertOp(opCtx, indexDoc, {});
308 }
309 }
310
onInserts(OperationContext * opCtx,const NamespaceString & nss,OptionalCollectionUUID uuid,std::vector<InsertStatement>::const_iterator begin,std::vector<InsertStatement>::const_iterator end,bool fromMigrate)311 void OpObserverImpl::onInserts(OperationContext* opCtx,
312 const NamespaceString& nss,
313 OptionalCollectionUUID uuid,
314 std::vector<InsertStatement>::const_iterator begin,
315 std::vector<InsertStatement>::const_iterator end,
316 bool fromMigrate) {
317 Session* const session = opCtx->getTxnNumber() ? OperationContextSession::get(opCtx) : nullptr;
318
319 const auto lastWriteDate = getWallClockTimeForOpLog(opCtx);
320
321 const auto opTimeList =
322 repl::logInsertOps(opCtx, nss, uuid, session, begin, end, fromMigrate, lastWriteDate);
323
324 auto css = (nss == NamespaceString::kSessionTransactionsTableNamespace || fromMigrate)
325 ? nullptr
326 : CollectionShardingState::get(opCtx, nss.ns());
327
328 size_t index = 0;
329 for (auto it = begin; it != end; it++, index++) {
330 AuthorizationManager::get(opCtx->getServiceContext())
331 ->logOp(opCtx, "i", nss, it->doc, nullptr);
332 if (css) {
333 auto opTime = opTimeList.empty() ? repl::OpTime() : opTimeList[index];
334 css->onInsertOp(opCtx, it->doc, opTime);
335 }
336 }
337
338 const auto lastOpTime = opTimeList.empty() ? repl::OpTime() : opTimeList.back();
339 if (nss.coll() == "system.js") {
340 Scope::storedFuncMod(opCtx);
341 } else if (nss.coll() == DurableViewCatalog::viewsCollectionName()) {
342 DurableViewCatalog::onExternalChange(opCtx, nss);
343 } else if (nss.ns() == FeatureCompatibilityVersion::kCollection) {
344 for (auto it = begin; it != end; it++) {
345 FeatureCompatibilityVersion::onInsertOrUpdate(opCtx, it->doc);
346 }
347 } else if (nss == NamespaceString::kSessionTransactionsTableNamespace && !lastOpTime.isNull()) {
348 for (auto it = begin; it != end; it++) {
349 SessionCatalog::get(opCtx)->invalidateSessions(opCtx, it->doc);
350 }
351 }
352
353 std::vector<StmtId> stmtIdsWritten;
354 std::transform(begin, end, std::back_inserter(stmtIdsWritten), [](const InsertStatement& stmt) {
355 return stmt.stmtId;
356 });
357
358 onWriteOpCompleted(opCtx, nss, session, stmtIdsWritten, lastOpTime, lastWriteDate);
359 }
360
onUpdate(OperationContext * opCtx,const OplogUpdateEntryArgs & args)361 void OpObserverImpl::onUpdate(OperationContext* opCtx, const OplogUpdateEntryArgs& args) {
362 MONGO_FAIL_POINT_BLOCK(failCollectionUpdates, extraData) {
363 auto collElem = extraData.getData()["collectionNS"];
364 // If the failpoint specifies no collection or matches the existing one, fail.
365 if (!collElem || args.nss.ns() == collElem.String()) {
366 uasserted(40654,
367 str::stream() << "failCollectionUpdates failpoint enabled, namespace: "
368 << args.nss.ns()
369 << ", update: "
370 << args.update
371 << " on document with "
372 << args.criteria);
373 }
374 }
375
376 // Do not log a no-op operation; see SERVER-21738
377 if (args.update.isEmpty()) {
378 return;
379 }
380
381 Session* const session = opCtx->getTxnNumber() ? OperationContextSession::get(opCtx) : nullptr;
382 const auto opTime = replLogUpdate(opCtx, session, args);
383
384 AuthorizationManager::get(opCtx->getServiceContext())
385 ->logOp(opCtx, "u", args.nss, args.update, &args.criteria);
386
387 if (args.nss != NamespaceString::kSessionTransactionsTableNamespace) {
388 if (!args.fromMigrate) {
389 auto css = CollectionShardingState::get(opCtx, args.nss);
390 css->onUpdateOp(opCtx,
391 args.criteria,
392 args.update,
393 args.updatedDoc,
394 opTime.writeOpTime,
395 opTime.prePostImageOpTime);
396 }
397 }
398
399 if (args.nss.coll() == "system.js") {
400 Scope::storedFuncMod(opCtx);
401 } else if (args.nss.coll() == DurableViewCatalog::viewsCollectionName()) {
402 DurableViewCatalog::onExternalChange(opCtx, args.nss);
403 } else if (args.nss.ns() == FeatureCompatibilityVersion::kCollection) {
404 FeatureCompatibilityVersion::onInsertOrUpdate(opCtx, args.updatedDoc);
405 } else if (args.nss == NamespaceString::kSessionTransactionsTableNamespace &&
406 !opTime.writeOpTime.isNull()) {
407 SessionCatalog::get(opCtx)->invalidateSessions(opCtx, args.updatedDoc);
408 }
409
410 onWriteOpCompleted(opCtx,
411 args.nss,
412 session,
413 std::vector<StmtId>{args.stmtId},
414 opTime.writeOpTime,
415 opTime.wallClockTime);
416 }
417
aboutToDelete(OperationContext * opCtx,NamespaceString const & nss,BSONObj const & doc)418 auto OpObserverImpl::aboutToDelete(OperationContext* opCtx,
419 NamespaceString const& nss,
420 BSONObj const& doc) -> CollectionShardingState::DeleteState {
421 auto* css = CollectionShardingState::get(opCtx, nss.ns());
422 return css->makeDeleteState(doc);
423 }
424
onDelete(OperationContext * opCtx,const NamespaceString & nss,OptionalCollectionUUID uuid,StmtId stmtId,CollectionShardingState::DeleteState deleteState,bool fromMigrate,const boost::optional<BSONObj> & deletedDoc)425 void OpObserverImpl::onDelete(OperationContext* opCtx,
426 const NamespaceString& nss,
427 OptionalCollectionUUID uuid,
428 StmtId stmtId,
429 CollectionShardingState::DeleteState deleteState,
430 bool fromMigrate,
431 const boost::optional<BSONObj>& deletedDoc) {
432 if (deleteState.documentKey.isEmpty()) {
433 return;
434 }
435
436 Session* const session = opCtx->getTxnNumber() ? OperationContextSession::get(opCtx) : nullptr;
437 const auto opTime =
438 replLogDelete(opCtx, nss, uuid, session, stmtId, deleteState, fromMigrate, deletedDoc);
439
440 AuthorizationManager::get(opCtx->getServiceContext())
441 ->logOp(opCtx, "d", nss, deleteState.documentKey, nullptr);
442
443 if (nss != NamespaceString::kSessionTransactionsTableNamespace) {
444 if (!fromMigrate) {
445 auto css = CollectionShardingState::get(opCtx, nss.ns());
446 css->onDeleteOp(opCtx, deleteState, opTime.writeOpTime, opTime.prePostImageOpTime);
447 }
448 }
449
450 if (nss.coll() == "system.js") {
451 Scope::storedFuncMod(opCtx);
452 } else if (nss.coll() == DurableViewCatalog::viewsCollectionName()) {
453 DurableViewCatalog::onExternalChange(opCtx, nss);
454 } else if (nss.isAdminDotSystemDotVersion()) {
455 auto _id = deleteState.documentKey["_id"];
456 if (_id.type() == BSONType::String &&
457 _id.String() == FeatureCompatibilityVersion::kParameterName)
458 uasserted(40670, "removing FeatureCompatibilityVersion document is not allowed");
459 } else if (nss == NamespaceString::kSessionTransactionsTableNamespace &&
460 !opTime.writeOpTime.isNull()) {
461 SessionCatalog::get(opCtx)->invalidateSessions(opCtx, deleteState.documentKey);
462 }
463
464 onWriteOpCompleted(
465 opCtx, nss, session, std::vector<StmtId>{stmtId}, opTime.writeOpTime, opTime.wallClockTime);
466 }
467
onInternalOpMessage(OperationContext * opCtx,const NamespaceString & nss,const boost::optional<UUID> uuid,const BSONObj & msgObj,const boost::optional<BSONObj> o2MsgObj)468 void OpObserverImpl::onInternalOpMessage(OperationContext* opCtx,
469 const NamespaceString& nss,
470 const boost::optional<UUID> uuid,
471 const BSONObj& msgObj,
472 const boost::optional<BSONObj> o2MsgObj) {
473 const BSONObj* o2MsgPtr = o2MsgObj ? o2MsgObj.get_ptr() : nullptr;
474 repl::logOp(opCtx,
475 "n",
476 nss,
477 uuid,
478 msgObj,
479 o2MsgPtr,
480 false,
481 getWallClockTimeForOpLog(opCtx),
482 {},
483 kUninitializedStmtId,
484 {},
485 OplogSlot());
486 }
487
onCreateCollection(OperationContext * opCtx,Collection * coll,const NamespaceString & collectionName,const CollectionOptions & options,const BSONObj & idIndex,const OplogSlot & createOpTime)488 void OpObserverImpl::onCreateCollection(OperationContext* opCtx,
489 Collection* coll,
490 const NamespaceString& collectionName,
491 const CollectionOptions& options,
492 const BSONObj& idIndex,
493 const OplogSlot& createOpTime) {
494 const auto cmdNss = collectionName.getCommandNS();
495
496 BSONObjBuilder b;
497 b.append("create", collectionName.coll().toString());
498 {
499 // Don't store the UUID as part of the options, but instead only at the top level
500 CollectionOptions optionsToStore = options;
501 optionsToStore.uuid.reset();
502 b.appendElements(optionsToStore.toBSON());
503 }
504
505 // Include the full _id index spec in the oplog for index versions >= 2.
506 if (!idIndex.isEmpty()) {
507 auto versionElem = idIndex[IndexDescriptor::kIndexVersionFieldName];
508 invariant(versionElem.isNumber());
509 if (IndexDescriptor::IndexVersion::kV2 <=
510 static_cast<IndexDescriptor::IndexVersion>(versionElem.numberInt())) {
511 b.append("idIndex", idIndex);
512 }
513 }
514
515 const auto cmdObj = b.done();
516
517 if (!collectionName.isSystemDotProfile()) {
518 // do not replicate system.profile modifications
519 repl::logOp(opCtx,
520 "c",
521 cmdNss,
522 options.uuid,
523 cmdObj,
524 nullptr,
525 false,
526 getWallClockTimeForOpLog(opCtx),
527 {},
528 kUninitializedStmtId,
529 {},
530 OplogSlot());
531 }
532
533 AuthorizationManager::get(opCtx->getServiceContext())
534 ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
535
536 if (options.uuid) {
537 UUIDCatalog& catalog = UUIDCatalog::get(opCtx);
538 catalog.onCreateCollection(opCtx, coll, options.uuid.get());
539 opCtx->recoveryUnit()->onRollback([opCtx, collectionName]() {
540 NamespaceUUIDCache::get(opCtx).evictNamespace(collectionName);
541 });
542 }
543 }
544
onCollMod(OperationContext * opCtx,const NamespaceString & nss,OptionalCollectionUUID uuid,const BSONObj & collModCmd,const CollectionOptions & oldCollOptions,boost::optional<TTLCollModInfo> ttlInfo)545 void OpObserverImpl::onCollMod(OperationContext* opCtx,
546 const NamespaceString& nss,
547 OptionalCollectionUUID uuid,
548 const BSONObj& collModCmd,
549 const CollectionOptions& oldCollOptions,
550 boost::optional<TTLCollModInfo> ttlInfo) {
551 const auto cmdNss = nss.getCommandNS();
552
553 // Create the 'o' field object.
554 const auto cmdObj = makeCollModCmdObj(collModCmd, oldCollOptions, ttlInfo);
555
556 // Create the 'o2' field object. We save the old collection metadata and TTL expiration.
557 BSONObjBuilder o2Builder;
558 o2Builder.append("collectionOptions_old", oldCollOptions.toBSON());
559 if (ttlInfo) {
560 auto oldExpireAfterSeconds = durationCount<Seconds>(ttlInfo->oldExpireAfterSeconds);
561 o2Builder.append("expireAfterSeconds_old", oldExpireAfterSeconds);
562 }
563
564 const auto o2Obj = o2Builder.done();
565
566 if (!nss.isSystemDotProfile()) {
567 // do not replicate system.profile modifications
568 repl::logOp(opCtx,
569 "c",
570 cmdNss,
571 uuid,
572 cmdObj,
573 &o2Obj,
574 false,
575 getWallClockTimeForOpLog(opCtx),
576 {},
577 kUninitializedStmtId,
578 {},
579 OplogSlot());
580 }
581
582 AuthorizationManager::get(opCtx->getServiceContext())
583 ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
584
585 // Make sure the UUID values in the Collection metadata, the Collection object, and the UUID
586 // catalog are all present and equal if uuid exists and do not exist if uuid does not exist.
587 invariant(opCtx->lockState()->isDbLockedForMode(nss.db(), MODE_X));
588 Database* db = dbHolder().get(opCtx, nss.db());
589 // Some unit tests call the op observer on an unregistered Database.
590 if (!db) {
591 return;
592 }
593 Collection* coll = db->getCollection(opCtx, nss.ns());
594 invariant(coll->uuid() == uuid,
595 str::stream() << (uuid ? uuid->toString() : "<no uuid>") << ","
596 << (coll->uuid() ? coll->uuid()->toString() : "<no uuid>"));
597 CollectionCatalogEntry* entry = coll->getCatalogEntry();
598 invariant(entry->isEqualToMetadataUUID(opCtx, uuid));
599
600 if (uuid) {
601 UUIDCatalog& catalog = UUIDCatalog::get(opCtx->getServiceContext());
602 Collection* catalogColl = catalog.lookupCollectionByUUID(uuid.get());
603 invariant(catalogColl && catalogColl->uuid() == uuid);
604 }
605 }
606
onDropDatabase(OperationContext * opCtx,const std::string & dbName)607 void OpObserverImpl::onDropDatabase(OperationContext* opCtx, const std::string& dbName) {
608 const NamespaceString cmdNss{dbName, "$cmd"};
609 const auto cmdObj = BSON("dropDatabase" << 1);
610
611 repl::logOp(opCtx,
612 "c",
613 cmdNss,
614 {},
615 cmdObj,
616 nullptr,
617 false,
618 getWallClockTimeForOpLog(opCtx),
619 {},
620 kUninitializedStmtId,
621 {},
622 OplogSlot());
623
624 if (dbName == FeatureCompatibilityVersion::kDatabase) {
625 FeatureCompatibilityVersion::onDropCollection(opCtx);
626 } else if (dbName == NamespaceString::kSessionTransactionsTableNamespace.db()) {
627 SessionCatalog::get(opCtx)->invalidateSessions(opCtx, boost::none);
628 }
629
630 NamespaceUUIDCache::get(opCtx).evictNamespacesInDatabase(dbName);
631
632 AuthorizationManager::get(opCtx->getServiceContext())
633 ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
634 }
635
onDropCollection(OperationContext * opCtx,const NamespaceString & collectionName,OptionalCollectionUUID uuid)636 repl::OpTime OpObserverImpl::onDropCollection(OperationContext* opCtx,
637 const NamespaceString& collectionName,
638 OptionalCollectionUUID uuid) {
639 const auto cmdNss = collectionName.getCommandNS();
640 const auto cmdObj = BSON("drop" << collectionName.coll());
641
642 repl::OpTime dropOpTime;
643 if (!collectionName.isSystemDotProfile()) {
644 // Do not replicate system.profile modifications
645 dropOpTime = repl::logOp(opCtx,
646 "c",
647 cmdNss,
648 uuid,
649 cmdObj,
650 nullptr,
651 false,
652 getWallClockTimeForOpLog(opCtx),
653 {},
654 kUninitializedStmtId,
655 {},
656 OplogSlot());
657 }
658
659 if (collectionName.coll() == DurableViewCatalog::viewsCollectionName()) {
660 DurableViewCatalog::onExternalChange(opCtx, collectionName);
661 } else if (collectionName.ns() == FeatureCompatibilityVersion::kCollection) {
662 FeatureCompatibilityVersion::onDropCollection(opCtx);
663 } else if (collectionName == NamespaceString::kSessionTransactionsTableNamespace) {
664 SessionCatalog::get(opCtx)->invalidateSessions(opCtx, boost::none);
665 }
666
667 AuthorizationManager::get(opCtx->getServiceContext())
668 ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
669
670 auto css = CollectionShardingState::get(opCtx, collectionName);
671 css->onDropCollection(opCtx, collectionName);
672
673 // Evict namespace entry from the namespace/uuid cache if it exists.
674 NamespaceUUIDCache::get(opCtx).evictNamespace(collectionName);
675
676 // Remove collection from the uuid catalog.
677 if (uuid) {
678 UUIDCatalog& catalog = UUIDCatalog::get(opCtx);
679 catalog.onDropCollection(opCtx, uuid.get());
680 }
681
682 return dropOpTime;
683 }
684
onDropIndex(OperationContext * opCtx,const NamespaceString & nss,OptionalCollectionUUID uuid,const std::string & indexName,const BSONObj & indexInfo)685 void OpObserverImpl::onDropIndex(OperationContext* opCtx,
686 const NamespaceString& nss,
687 OptionalCollectionUUID uuid,
688 const std::string& indexName,
689 const BSONObj& indexInfo) {
690 const auto cmdNss = nss.getCommandNS();
691 const auto cmdObj = BSON("dropIndexes" << nss.coll() << "index" << indexName);
692
693 repl::logOp(opCtx,
694 "c",
695 cmdNss,
696 uuid,
697 cmdObj,
698 &indexInfo,
699 false,
700 getWallClockTimeForOpLog(opCtx),
701 {},
702 kUninitializedStmtId,
703 {},
704 OplogSlot());
705
706 AuthorizationManager::get(opCtx->getServiceContext())
707 ->logOp(opCtx, "c", cmdNss, cmdObj, &indexInfo);
708 }
709
onRenameCollection(OperationContext * opCtx,const NamespaceString & fromCollection,const NamespaceString & toCollection,OptionalCollectionUUID uuid,bool dropTarget,OptionalCollectionUUID dropTargetUUID,bool stayTemp)710 repl::OpTime OpObserverImpl::onRenameCollection(OperationContext* opCtx,
711 const NamespaceString& fromCollection,
712 const NamespaceString& toCollection,
713 OptionalCollectionUUID uuid,
714 bool dropTarget,
715 OptionalCollectionUUID dropTargetUUID,
716 bool stayTemp) {
717 const auto cmdNss = fromCollection.getCommandNS();
718
719 BSONObjBuilder builder;
720 builder.append("renameCollection", fromCollection.ns());
721 builder.append("to", toCollection.ns());
722 builder.append("stayTemp", stayTemp);
723 if (dropTargetUUID && enableCollectionUUIDs && !isMasterSlave(opCtx)) {
724 dropTargetUUID->appendToBuilder(&builder, "dropTarget");
725 } else {
726 builder.append("dropTarget", dropTarget);
727 }
728
729 const auto cmdObj = builder.done();
730
731 const auto renameOpTime = repl::logOp(opCtx,
732 "c",
733 cmdNss,
734 uuid,
735 cmdObj,
736 nullptr,
737 false,
738 getWallClockTimeForOpLog(opCtx),
739 {},
740 kUninitializedStmtId,
741 {},
742 OplogSlot());
743
744 if (fromCollection.isSystemDotViews())
745 DurableViewCatalog::onExternalChange(opCtx, fromCollection);
746 if (toCollection.isSystemDotViews())
747 DurableViewCatalog::onExternalChange(opCtx, toCollection);
748
749 AuthorizationManager::get(opCtx->getServiceContext())
750 ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
751
752 // Evict namespace entry from the namespace/uuid cache if it exists.
753 NamespaceUUIDCache& cache = NamespaceUUIDCache::get(opCtx);
754 cache.evictNamespace(fromCollection);
755 cache.evictNamespace(toCollection);
756 opCtx->recoveryUnit()->onRollback(
757 [&cache, toCollection]() { cache.evictNamespace(toCollection); });
758
759 // Finally update the UUID Catalog.
760 if (uuid) {
761 auto getNewCollection = [opCtx, toCollection] {
762 auto db = dbHolder().get(opCtx, toCollection.db());
763 auto newColl = db->getCollection(opCtx, toCollection);
764 invariant(newColl);
765 return newColl;
766 };
767 UUIDCatalog& catalog = UUIDCatalog::get(opCtx);
768 catalog.onRenameCollection(opCtx, getNewCollection, uuid.get());
769 }
770
771 return renameOpTime;
772 }
773
onApplyOps(OperationContext * opCtx,const std::string & dbName,const BSONObj & applyOpCmd)774 void OpObserverImpl::onApplyOps(OperationContext* opCtx,
775 const std::string& dbName,
776 const BSONObj& applyOpCmd) {
777 const NamespaceString cmdNss{dbName, "$cmd"};
778 repl::logOp(opCtx,
779 "c",
780 cmdNss,
781 {},
782 applyOpCmd,
783 nullptr,
784 false,
785 getWallClockTimeForOpLog(opCtx),
786 {},
787 kUninitializedStmtId,
788 {},
789 OplogSlot());
790
791 AuthorizationManager::get(opCtx->getServiceContext())
792 ->logOp(opCtx, "c", cmdNss, applyOpCmd, nullptr);
793 }
794
onEmptyCapped(OperationContext * opCtx,const NamespaceString & collectionName,OptionalCollectionUUID uuid)795 void OpObserverImpl::onEmptyCapped(OperationContext* opCtx,
796 const NamespaceString& collectionName,
797 OptionalCollectionUUID uuid) {
798 const auto cmdNss = collectionName.getCommandNS();
799 const auto cmdObj = BSON("emptycapped" << collectionName.coll());
800
801 if (!collectionName.isSystemDotProfile()) {
802 // Do not replicate system.profile modifications
803 repl::logOp(opCtx,
804 "c",
805 cmdNss,
806 uuid,
807 cmdObj,
808 nullptr,
809 false,
810 getWallClockTimeForOpLog(opCtx),
811 {},
812 kUninitializedStmtId,
813 {},
814 OplogSlot());
815 }
816
817 AuthorizationManager::get(opCtx->getServiceContext())
818 ->logOp(opCtx, "c", cmdNss, cmdObj, nullptr);
819 }
820
821 } // namespace mongo
822