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