…lines 1–31 unchanged (31 lines)
32#include "mongo/db/op_observer/op_observer_registry.h"
33#include "mongo/db/op_observer/operation_logger_impl.h"
34#include "mongo/db/repl/apply_ops.h"
35#include "mongo/db/repl/apply_ops_command_info.h"
36#include "mongo/db/repl/oplog_entry.h"
37#include "mongo/db/repl/oplog_interface_local.h"
38#include "mongo/db/replicated_fast_count/replicated_fast_count_delta_utils.h"
39#include "mongo/db/replicated_fast_count/replicated_fast_count_enabled.h"
40#include "mongo/db/replicated_fast_count/replicated_fast_count_init.h"
41#include "mongo/db/replicated_fast_count/replicated_fast_count_manager.h"
42#include "mongo/db/replicated_fast_count/replicated_fast_count_test_helpers.h"
43#include "mongo/db/replicated_fast_count/replicated_fast_count_uncommitted_changes.h"
44#include "mongo/db/shard_role/shard_catalog/catalog_raii.h"
45#include "mongo/db/shard_role/shard_catalog/catalog_test_fixture.h"
…lines 46–922 unchanged (877 lines)
923 replicated_fast_count_test_helpers::checkCommittedFastCountChanges(
924 internalUuid, _fastCountManager, docsToInsertCount, expectedSize);
925 replicated_fast_count_test_helpers::checkUncommittedFastCountChanges(
926 _opCtx, internalUuid, 0, 0);
927 }
928}
929
930/**
931 * Tests that operations with replicated size and count are logged with 'sizeMetadata' (information
932 * stored in the top-level 'm' field) in their oplog entries.
933 */
934class SizeMetadataLoggingTest : public ReplicatedFastCountTest {};
935
936TEST_F(SizeMetadataLoggingTest, BasicInsertOplogEntry) {
937 RAIIServerParameterControllerForTest featureFlag("featureFlagReplicatedFastCount", true);
938 AutoGetCollection coll(_opCtx, _nss1, LockMode::MODE_IX);
939
940 const auto docToInsert = docGeneratorForInsert(0);
941 const auto expectedSizeDelta = docToInsert.objsize();
942
943 {
944 WriteUnitOfWork wuow{_opCtx};
945 ASSERT_OK(Helpers::insert(_opCtx, *coll, docToInsert));
946 wuow.commit();
947 }
948
949 const auto insertOplogEntry = replicated_fast_count_test_helpers::getMostRecentOplogEntry(
950 _opCtx, _nss1, repl::OpTypeEnum::kInsert);
951 ASSERT_TRUE(insertOplogEntry.has_value());
952
953 // Confirm the generated oplog entry includes replicated size count information.
954 replicated_fast_count_test_helpers::assertReplicatedSizeCountMeta(*insertOplogEntry,
955 expectedSizeDelta);
956}
957
958TEST_F(SizeMetadataLoggingTest, BasicUpdateOplogEntry) {
959 RAIIServerParameterControllerForTest featureFlag("featureFlagReplicatedFastCount", true);
960 auto coll = acquireCollection(
961 _opCtx,
962 CollectionAcquisitionRequest::fromOpCtx(_opCtx, _nss1, AcquisitionPrerequisites::kWrite),
963 MODE_IX);
964 std::string smallNote("hi");
965 const auto docInitial = BSON("_id" << 0 << "note" << smallNote);
966 {
967 WriteUnitOfWork wuow{_opCtx};
968 ASSERT_OK(Helpers::insert(_opCtx, coll.getCollectionPtr(), docInitial));
969 wuow.commit();
970 }
971
972 std::string largerNote("a slightly larger note to increase the size when we update");
973 {
974 WriteUnitOfWork wuow{_opCtx};
975 Helpers::update(_opCtx, coll, BSON("_id" << 0), BSON("$set" << BSON("note" << largerNote)));
976 wuow.commit();
977 }
978 const auto docAfterUpdate = Helpers::findOneForTesting(_opCtx, coll, BSON("_id" << 0));
979 const auto expectedSizeDelta = docAfterUpdate.objsize() - docInitial.objsize();
980
981 const auto updateOplogEntry = replicated_fast_count_test_helpers::getMostRecentOplogEntry(
982 _opCtx, _nss1, repl::OpTypeEnum::kUpdate);
983 ASSERT_TRUE(updateOplogEntry.has_value());
984
985 // Confirm the generated oplog entry includes replicated size count information.
986 replicated_fast_count_test_helpers::assertReplicatedSizeCountMeta(*updateOplogEntry,
987 expectedSizeDelta);
988}
989
990TEST_F(SizeMetadataLoggingTest, BasicUpdateOplogEntryWithNegativeDelta) {
991 RAIIServerParameterControllerForTest featureFlag("featureFlagReplicatedFastCount", true);
992 auto coll = acquireCollection(
993 _opCtx,
994 CollectionAcquisitionRequest::fromOpCtx(_opCtx, _nss1, AcquisitionPrerequisites::kWrite),
995 MODE_IX);
996 std::string largeNote(
997 "a slightly large note to force the doc to start out as larger before the update");
998 const auto docInitial = BSON("_id" << 0 << "note" << largeNote);
999 {
1000 WriteUnitOfWork wuow{_opCtx};
1001 ASSERT_OK(Helpers::insert(_opCtx, coll.getCollectionPtr(), docInitial));
1002 wuow.commit();
1003 }
1004
1005 std::string smallerNote("hi");
1006 {
1007 WriteUnitOfWork wuow{_opCtx};
1008 Helpers::update(
1009 _opCtx, coll, BSON("_id" << 0), BSON("$set" << BSON("note" << smallerNote)));
1010 wuow.commit();
1011 }
1012 const auto docAfterUpdate = Helpers::findOneForTesting(_opCtx, coll, BSON("_id" << 0));
1013 const auto expectedSizeDelta = docAfterUpdate.objsize() - docInitial.objsize();
1014
1015 const auto updateOplogEntry = replicated_fast_count_test_helpers::getMostRecentOplogEntry(
1016 _opCtx, _nss1, repl::OpTypeEnum::kUpdate);
1017 ASSERT_TRUE(updateOplogEntry.has_value());
1018 replicated_fast_count_test_helpers::assertReplicatedSizeCountMeta(*updateOplogEntry,
1019 expectedSizeDelta);
1020}
1021
1022TEST_F(SizeMetadataLoggingTest, BasicDeleteOplogEntry) {
1023 RAIIServerParameterControllerForTest featureFlag("featureFlagReplicatedFastCount", true);
1024 auto coll = acquireCollection(
1025 _opCtx,
1026 CollectionAcquisitionRequest::fromOpCtx(_opCtx, _nss1, AcquisitionPrerequisites::kWrite),
1027 MODE_IX);
1028 const auto doc = BSON("_id" << 0 << "x" << 0);
1029 {
1030 WriteUnitOfWork wuow{_opCtx};
1031 ASSERT_OK(Helpers::insert(_opCtx, coll.getCollectionPtr(), doc));
1032 wuow.commit();
1033 }
1034
1035 const auto rid = Helpers::findOne(_opCtx, coll, BSON("_id" << 0));
1036 ASSERT_FALSE(rid.isNull());
1037
1038 {
1039 WriteUnitOfWork wuow{_opCtx};
1040 Helpers::deleteByRid(_opCtx, coll, rid);
1041 wuow.commit();
1042 }
1043 const auto expectedSizeDelta = doc.objsize();
1044 const auto deleteOplogEntry = replicated_fast_count_test_helpers::getMostRecentOplogEntry(
1045 _opCtx, _nss1, repl::OpTypeEnum::kDelete);
1046 ASSERT_TRUE(deleteOplogEntry.has_value());
1047 replicated_fast_count_test_helpers::assertReplicatedSizeCountMeta(*deleteOplogEntry,
1048 -expectedSizeDelta);
1049}
1050
1051TEST_F(SizeMetadataLoggingTest, BasicGroupCommit) {
1052 RAIIServerParameterControllerForTest featureFlag("featureFlagReplicatedFastCount", true);
1053 auto coll = acquireCollection(
1054 _opCtx,
1055 CollectionAcquisitionRequest::fromOpCtx(_opCtx, _nss1, AcquisitionPrerequisites::kWrite),
1056 MODE_IX);
1057 const auto doc1 = BSON("_id" << 0 << "x" << 0);
1058 const auto doc2 = BSON("_id" << 1 << "abcdefg" << 1);
1059 {
1060 WriteUnitOfWork wuow{_opCtx, WriteUnitOfWork::kGroupForPossiblyRetryableOperations};
1061 ASSERT_OK(Helpers::insert(_opCtx, coll.getCollectionPtr(), doc1));
1062 ASSERT_OK(Helpers::insert(_opCtx, coll.getCollectionPtr(), doc2));
1063 wuow.commit();
1064 }
1065 std::vector<replicated_fast_count_test_helpers::OpValidationSpec> expectedOps{
1066 {.uuid = _uuid1, .opType = repl::OpTypeEnum::kInsert, .expectedSizeDelta = doc1.objsize()},
1067 {.uuid = _uuid1, .opType = repl::OpTypeEnum::kInsert, .expectedSizeDelta = doc2.objsize()}};
1068
1069 const auto applyOpsOplogEntry =
1070 replicated_fast_count_test_helpers::getLatestApplyOpsForNss(_opCtx, _nss1);
1071 std::vector<repl::OplogEntry> innerEntries;
1072 repl::ApplyOps::extractOperationsTo(
1073 applyOpsOplogEntry, applyOpsOplogEntry.getEntry().toBSON(), &innerEntries);
1074 replicated_fast_count_test_helpers::assertOpsMatchSpecs(innerEntries, expectedOps);
1075}
1076
1077using ReplicatedFastCountDeltaUtilsTest = ReplicatedFastCountTest;
1078
1079repl::OplogEntrySizeMetadata makeOperationSizeMetadata(int32_t replicatedSizeDelta) {
1080 repl::OplogEntrySizeMetadata m;
1081 m.setSz(replicatedSizeDelta);
1082 return m;
1083}
1084
1085repl::OplogEntry makeOplogEntryWithSizeMeta(const NamespaceString& nss,
1086 repl::OpTypeEnum opType,
1087 int32_t sizeDelta) {
1088
1089 auto sizeMetadata = makeOperationSizeMetadata(sizeDelta);
1090 return repl::DurableOplogEntry{repl::DurableOplogEntryParams{
1091 .opTime = repl::OpTime(),
1092 .opType = opType,
1093 .nss = nss,
1094 .oField = BSONObj(),
1095 .sizeMetadata = sizeMetadata,
1096 .wallClockTime = Date_t::now(),
1097 }};
1098}
1099
1100TEST_F(ReplicatedFastCountDeltaUtilsTest, ExtractSizeCountDeltaForInsert) {
1101 const int32_t sizeDelta = 400;
1102 const auto insertOp = makeOplogEntryWithSizeMeta(_nss1, repl::OpTypeEnum::kInsert, sizeDelta);
1103 const auto extractedSizeCount = replicated_fast_count::extractSizeCountDeltaForOp(insertOp);
1104 ASSERT(extractedSizeCount.has_value());
1105
1106 // Insert means count increases by 1.
1107 ASSERT_EQ(1, extractedSizeCount->count);
1108 ASSERT_EQ(sizeDelta, extractedSizeCount->size);
1109}
1110
1111TEST_F(ReplicatedFastCountDeltaUtilsTest, ExtractSizeCountDeltaForUpdate) {
1112 const int32_t sizeDelta = 400;
1113 const auto insertOp = makeOplogEntryWithSizeMeta(_nss1, repl::OpTypeEnum::kUpdate, sizeDelta);
1114 const auto extractedSizeCount = replicated_fast_count::extractSizeCountDeltaForOp(insertOp);
1115 ASSERT(extractedSizeCount.has_value());
1116
1117 // Updates imply no new documents, count delta is 0.
1118 ASSERT_EQ(0, extractedSizeCount->count);
1119 ASSERT_EQ(sizeDelta, extractedSizeCount->size);
1120}
1121
1122TEST_F(ReplicatedFastCountDeltaUtilsTest, ExtractSizeCountDeltaForDelete) {
1123 const int32_t sizeDelta = 400;
1124 const auto insertOp = makeOplogEntryWithSizeMeta(_nss1, repl::OpTypeEnum::kDelete, sizeDelta);
1125 const auto extractedSizeCount = replicated_fast_count::extractSizeCountDeltaForOp(insertOp);
1126 ASSERT(extractedSizeCount.has_value());
1127
1128 // Delete implies one less document.
1129 ASSERT_EQ(-1, extractedSizeCount->count);
1130 ASSERT_EQ(sizeDelta, extractedSizeCount->size);
1131}
1132
1133TEST_F(ReplicatedFastCountDeltaUtilsTest, NoSizeCountDeltaWhenAbsentFromOplogEntry) {
1134 // 'OpTypeEnum::kInsert' supports replicated fast count information, but none is extracted
1135 // because the 'm' field is absent from the oplog entry.
1136 repl::OplogEntry insertOpNoSizeMetadata{repl::DurableOplogEntry{repl::DurableOplogEntryParams{
1137 .opTime = repl::OpTime(),
1138 .opType = repl::OpTypeEnum::kInsert,
1139 .nss = _nss1,
1140 .oField = BSONObj(),
1141 .wallClockTime = Date_t::now(),
1142 }}};
1143 const auto extractedSizeCount =
1144 replicated_fast_count::extractSizeCountDeltaForOp(insertOpNoSizeMetadata);
1145 ASSERT_FALSE(insertOpNoSizeMetadata.getSizeMetadata().has_value());
1146}
1147
1148TEST_F(ReplicatedFastCountDeltaUtilsTest, NoSizeCountDeltaWhenAbsentAndIncompatibleOpType) {
1149 // 'OpTypeEnum::kCommand' does not support top level 'sizeMetadata' field 'm', and in absence of
1150 // the 'sizeMetadata', nothing is returned when trying to extract size count deltas.
1151 repl::OplogEntry commandOpNoSizeMetadata{repl::DurableOplogEntry{repl::DurableOplogEntryParams{
1152 .opTime = repl::OpTime(),
1153 .opType = repl::OpTypeEnum::kCommand,
1154 .nss = _nss1,
1155 .oField = BSONObj(),
1156 .wallClockTime = Date_t::now(),
1157 }}};
1158 const auto extractedSizeCount =
1159 replicated_fast_count::extractSizeCountDeltaForOp(commandOpNoSizeMetadata);
1160 ASSERT_FALSE(commandOpNoSizeMetadata.getSizeMetadata().has_value());
1161}
1162
1163TEST_F(ReplicatedFastCountDeltaUtilsTest, ExtractSizeCountDeltaOnUnsupportedOpType) {
1164 const auto oplogEntry =
1165 makeOplogEntryWithSizeMeta(_nss1, repl::OpTypeEnum::kNoop, 400 /* sizeDelta */);
1166
1167 // Size metadata is only supported for 'insert', 'delete', and 'update' operations. All other
1168 // operations are incompatible with a top-level 'm' field.
1169 // TODO SERVER-121175: Include truncates in accepted opTypes comment.
1170 ASSERT_THROWS_CODE(
1171 replicated_fast_count::extractSizeCountDeltaForOp(oplogEntry), DBException, 12115900);
1172}
1173
1174TEST_F(ReplicatedFastCountDeltaUtilsTest, ExtractSizeCountDeltaOnNonEligibleNss) {
1175 const NamespaceString localNss =
1176 NamespaceString::createNamespaceString_forTest("local", "coll1");
1177 EXPECT_FALSE(isReplicatedFastCountEligible(localNss));
1178
1179 const auto oplogEntry =
1180 makeOplogEntryWithSizeMeta(localNss, repl::OpTypeEnum::kNoop, 400 /* sizeDelta */);
1181
1182 // Local namespaces are not eligible for replicated size count.
1183 ASSERT_THROWS_CODE(
1184 replicated_fast_count::extractSizeCountDeltaForOp(oplogEntry), DBException, 12115900);
1185}
1186
1187TEST_F(ReplicatedFastCountDeltaUtilsTest,
1188 ExtractSizeCountDeltaOnNonEligibleNssWithoutSizeMetadata) {
1189 const NamespaceString localNss =
1190 NamespaceString::createNamespaceString_forTest("local", "coll1");
1191 EXPECT_FALSE(isReplicatedFastCountEligible(localNss));
1192
1193 repl::OplogEntry insertOpLocalNs{repl::DurableOplogEntry{repl::DurableOplogEntryParams{
1194 .opTime = repl::OpTime(),
1195 .opType = repl::OpTypeEnum::kInsert,
1196 .nss = localNss,
1197 .oField = BSONObj(),
1198 .wallClockTime = Date_t::now(),
1199 }}};
1200
1201 // Local namespace without sizeMetadata shouldn't throw an error.
1202 ASSERT_FALSE(replicated_fast_count::extractSizeCountDeltaForOp(insertOpLocalNs).has_value());
1203}
1204
1205using ReplicatedFastCountDeathTest = ReplicatedFastCountTest;
1206
1207// TODO SERVER-120203: Re-enable this test.
1208// DEATH_TEST_REGEX_F(ReplicatedFastCountDeathTest,
1209// CannotHaveNegativeCommittedSizeOrCount,
…lines 1210–1220 unchanged (11 lines)