summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2022-03-02 18:35:40 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-31 22:00:10 +0000
commit70b3eb6d557c6153ae98c37281fdf3dae19e625c (patch)
tree1036403e4547c601e2d2d10392f0282ecf306679
parent2f2806eb4ca08b67392b6acc5d23c16e598c5f29 (diff)
downloadmongo-70b3eb6d557c6153ae98c37281fdf3dae19e625c.tar.gz
SERVER-63504 Add shard worker for ecoc compaction
-rw-r--r--jstests/core/views/views_all_commands.js7
-rw-r--r--jstests/replsets/db_reads_while_recovering_all_commands.js1
-rw-r--r--jstests/replsets/tenant_migration_concurrent_writes_on_donor.js1
-rw-r--r--jstests/sharding/compact_structured_encryption_data_coordinator.js110
-rw-r--r--jstests/sharding/database_versioning_all_commands.js1
-rw-r--r--jstests/sharding/libs/last_lts_mongos_commands.js1
-rw-r--r--jstests/sharding/read_write_concern_defaults_application.js1
-rw-r--r--jstests/sharding/safe_secondary_reads_drop_recreate.js2
-rw-r--r--jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js2
-rw-r--r--jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js2
-rw-r--r--src/mongo/db/commands/SConscript23
-rw-r--r--src/mongo/db/commands/fle2_compact.cpp112
-rw-r--r--src/mongo/db/commands/fle2_compact.h5
-rw-r--r--src/mongo/db/commands/fle2_compact_cmd.cpp145
-rw-r--r--src/mongo/db/namespace_string.cpp3
-rw-r--r--src/mongo/db/namespace_string.h3
-rw-r--r--src/mongo/db/s/SConscript4
-rw-r--r--src/mongo/db/s/compact_structured_encryption_data_coordinator.cpp159
-rw-r--r--src/mongo/db/s/compact_structured_encryption_data_coordinator.h100
-rw-r--r--src/mongo/db/s/compact_structured_encryption_data_coordinator.idl68
-rw-r--r--src/mongo/db/s/sharding_ddl_coordinator.idl1
-rw-r--r--src/mongo/db/s/sharding_ddl_coordinator_service.cpp5
-rw-r--r--src/mongo/db/s/shardsvr_compact_structured_encryption_data_command.cpp148
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/commands/cluster_fle2_compact_cmd.cpp117
25 files changed, 884 insertions, 138 deletions
diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js
index 0fe746c8047..d18e14b19e3 100644
--- a/jstests/core/views/views_all_commands.js
+++ b/jstests/core/views/views_all_commands.js
@@ -143,6 +143,7 @@ let viewsCommandTests = {
_recvChunkStatus: {skip: isAnInternalCommand},
_shardsvrAbortReshardCollection: {skip: isAnInternalCommand},
_shardsvrCloneCatalogData: {skip: isAnInternalCommand},
+ _shardsvrCompactStructuredEncryptionData: {skip: isAnInternalCommand},
_shardsvrDropCollection: {skip: isAnInternalCommand},
_shardsvrDropCollectionIfUUIDNotMatching: {skip: isUnrelated},
_shardsvrDropCollectionParticipant: {skip: isAnInternalCommand},
@@ -244,11 +245,7 @@ let viewsCommandTests = {
commitReshardCollection: {skip: isUnrelated},
commitTransaction: {skip: isUnrelated},
compact: {command: {compact: "view", force: true}, expectFailure: true, skipSharded: true},
- compactStructuredEncryptionData: {
- command: {compactStructuredEncryptionData: "view", compactionTokens: {}},
- expectFailure: true,
- skipSharded: true
- },
+ compactStructuredEncryptionData: {skip: isUnrelated},
configureFailPoint: {skip: isUnrelated},
configureCollectionAutoSplitter: {
skip: isUnrelated
diff --git a/jstests/replsets/db_reads_while_recovering_all_commands.js b/jstests/replsets/db_reads_while_recovering_all_commands.js
index 6a71fb16347..279d167edba 100644
--- a/jstests/replsets/db_reads_while_recovering_all_commands.js
+++ b/jstests/replsets/db_reads_while_recovering_all_commands.js
@@ -78,6 +78,7 @@ const allCommands = {
_shardsvrAbortReshardCollection: {skip: isPrimaryOnly},
_shardsvrCleanupReshardCollection: {skip: isPrimaryOnly},
_shardsvrCloneCatalogData: {skip: isPrimaryOnly},
+ _shardsvrCompactStructuredEncryptionData: {skip: isPrimaryOnly},
_shardsvrCommitReshardCollection: {skip: isPrimaryOnly},
_shardsvrDropCollection: {skip: isPrimaryOnly},
_shardsvrCreateCollection: {skip: isPrimaryOnly},
diff --git a/jstests/replsets/tenant_migration_concurrent_writes_on_donor.js b/jstests/replsets/tenant_migration_concurrent_writes_on_donor.js
index bda2513e908..49eb4baeb71 100644
--- a/jstests/replsets/tenant_migration_concurrent_writes_on_donor.js
+++ b/jstests/replsets/tenant_migration_concurrent_writes_on_donor.js
@@ -539,6 +539,7 @@ const testCases = {
_recvChunkStart: {skip: isNotRunOnUserDatabase},
_recvChunkStatus: {skip: isNotRunOnUserDatabase},
_shardsvrCloneCatalogData: {skip: isNotRunOnUserDatabase},
+ _shardsvrCompactStructuredEncryptionData: {skip: isOnlySupportedOnShardedCluster},
_shardsvrCreateCollection: {skip: isOnlySupportedOnShardedCluster},
_shardsvrCreateCollectionParticipant: {skip: isOnlySupportedOnShardedCluster},
_shardsvrMovePrimary: {skip: isNotRunOnUserDatabase},
diff --git a/jstests/sharding/compact_structured_encryption_data_coordinator.js b/jstests/sharding/compact_structured_encryption_data_coordinator.js
new file mode 100644
index 00000000000..efd17674749
--- /dev/null
+++ b/jstests/sharding/compact_structured_encryption_data_coordinator.js
@@ -0,0 +1,110 @@
+// Basic test that the CompactStructuredEncryptionDataCoordinator runs.
+// @tags: [requires_sharding,requires_fcv_60]
+
+(function() {
+'use strict';
+
+load('jstests/fle2/libs/encrypted_client_util.js');
+
+if (!TestData.setParameters.featureFlagFLE2) {
+ jsTest.log('Skipping test because feature flag is not enabled');
+ return;
+}
+
+const options = {
+ mongos: 1,
+ config: 1,
+ shards: 1,
+ rs: {nodes: [{}]},
+};
+
+const kHaveAuditing = buildInfo().modules.includes("enterprise");
+if (kHaveAuditing) {
+ jsTest.log('Including test for audit events since this is an enterprise build');
+ const nodeOpts = options.rs.nodes[0];
+ nodeOpts.auditDestination = 'file';
+ nodeOpts.auditPath = MongoRunner.dataPath + '/audit.log';
+ nodeOpts.auditFormat = 'JSON';
+}
+
+const st = new ShardingTest(options);
+
+const admin = st.s0.getDB('admin');
+
+// Setup collection with encrypted fields and a compactible metadata collection.
+const encryptedFields = {
+ fields: [
+ {path: "firstName", bsonType: "string", queries: {"queryType": "equality"}},
+ {path: "a.b.c", bsonType: "int", queries: {"queryType": "equality"}},
+ ]
+};
+
+const client = new EncryptedClient(st.s0, 'test');
+const test = client.getDB('test');
+
+assert.commandWorked(
+ client.createEncryptionCollection("encrypted", {encryptedFields: encryptedFields}));
+assert.commandWorked(test.createCollection("unencrypted"));
+
+assert.commandFailedWithCode(test.unencrypted.compact(), ErrorCodes.BadValue);
+
+const reply = assert.commandWorked(test.encrypted.compact());
+jsTest.log(reply);
+
+// Validate dummy data we expect the placeholder compaction algorithm to return.
+assert.eq(reply.stats.ecoc.read, 1);
+assert.eq(reply.stats.ecoc.deleted, 2);
+
+assert.eq(reply.stats.ecc.read, 3);
+assert.eq(reply.stats.ecc.inserted, 4);
+assert.eq(reply.stats.ecc.updated, 5);
+assert.eq(reply.stats.ecc.deleted, 6);
+
+assert.eq(reply.stats.esc.read, 7);
+assert.eq(reply.stats.esc.inserted, 8);
+assert.eq(reply.stats.esc.updated, 9);
+assert.eq(reply.stats.esc.deleted, 10);
+
+// The eccoc collection is gone, so we should return quickly with no work done.
+const nowork = assert.commandWorked(test.encrypted.compact());
+jsTest.log(nowork);
+
+assert.eq(nowork.stats.ecoc.read, 0);
+assert.eq(nowork.stats.ecoc.deleted, 0);
+
+assert.eq(nowork.stats.ecc.read, 0);
+assert.eq(nowork.stats.ecc.inserted, 0);
+assert.eq(nowork.stats.ecc.updated, 0);
+assert.eq(nowork.stats.ecc.deleted, 0);
+
+assert.eq(nowork.stats.esc.read, 0);
+assert.eq(nowork.stats.esc.inserted, 0);
+assert.eq(nowork.stats.esc.updated, 0);
+assert.eq(nowork.stats.esc.deleted, 0);
+
+if (kHaveAuditing) {
+ jsTest.log('Verifying audit contents');
+
+ // Check the audit log for the rename/drop events.
+ const audit = cat(options.rs.nodes[0].auditPath)
+ .split('\n')
+ .filter((l) => l !== '')
+ .map((l) => JSON.parse(l));
+ jsTest.log('Audit Log: ' + tojson(audit));
+
+ const renameEvents = audit.filter((ev) => (ev.atype === 'renameCollection') &&
+ (ev.param.old === 'test.fle2.encrypted.ecoc'));
+ assert.eq(renameEvents.length, 1, 'Invalid number of rename events: ' + tojson(renameEvents));
+ assert.eq(renameEvents[0].result, ErrorCodes.OK);
+ const tempNSS = renameEvents[0].param.new;
+
+ const dropEvents =
+ audit.filter((ev) => (ev.atype === 'dropCollection') && (ev.param.ns === tempNSS));
+ assert.eq(dropEvents.length, 1, 'Invalid number of drop events: ' + tojson(dropEvents));
+ assert.eq(dropEvents[0].result, ErrorCodes.OK);
+}
+
+st.stop();
+
+jsTest.log(reply);
+})();
diff --git a/jstests/sharding/database_versioning_all_commands.js b/jstests/sharding/database_versioning_all_commands.js
index 46ddccfe4fe..edd95248a8a 100644
--- a/jstests/sharding/database_versioning_all_commands.js
+++ b/jstests/sharding/database_versioning_all_commands.js
@@ -300,6 +300,7 @@ let testCases = {
commitReshardCollection: {skip: "always targets the config server"},
commitTransaction: {skip: "unversioned and uses special targetting rules"},
compact: {skip: "not allowed through mongos"},
+ compactStructuredEncryptionData: {skip: "requires encrypted collections"},
configureCollectionBalancing: {skip: "always targets the config server"},
configureFailPoint: {skip: "executes locally on mongos (not sent to any remote node)"},
connPoolStats: {skip: "executes locally on mongos (not sent to any remote node)"},
diff --git a/jstests/sharding/libs/last_lts_mongos_commands.js b/jstests/sharding/libs/last_lts_mongos_commands.js
index 86937f182db..90eb308fa4e 100644
--- a/jstests/sharding/libs/last_lts_mongos_commands.js
+++ b/jstests/sharding/libs/last_lts_mongos_commands.js
@@ -16,6 +16,7 @@ const commandsAddedToMongosSinceLastLTS = [
"appendOplogNote",
"cleanupReshardCollection",
"commitReshardCollection",
+ "compactStructuredEncryptionData",
"configureCollectionBalancing",
"moveRange",
"reshardCollection",
diff --git a/jstests/sharding/read_write_concern_defaults_application.js b/jstests/sharding/read_write_concern_defaults_application.js
index 7642ce34e25..b7804ac5e58 100644
--- a/jstests/sharding/read_write_concern_defaults_application.js
+++ b/jstests/sharding/read_write_concern_defaults_application.js
@@ -147,6 +147,7 @@ let testCases = {
_shardsvrCleanupReshardCollection: {skip: "internal command"},
_shardsvrCloneCatalogData: {skip: "internal command"},
_shardsvrCommitReshardCollection: {skip: "internal command"},
+ _shardsvrCompactStructuredEncryptionData: {skip: "internal command"},
_shardsvrCreateCollection: {skip: "internal command"},
_shardsvrCreateCollectionParticipant: {skip: "internal command"},
_shardsvrDropCollection: {skip: "internal command"},
diff --git a/jstests/sharding/safe_secondary_reads_drop_recreate.js b/jstests/sharding/safe_secondary_reads_drop_recreate.js
index 4835739324b..e7fd5f85963 100644
--- a/jstests/sharding/safe_secondary_reads_drop_recreate.js
+++ b/jstests/sharding/safe_secondary_reads_drop_recreate.js
@@ -82,6 +82,7 @@ let testCases = {
_killOperations: {skip: "does not return user data"},
_mergeAuthzCollections: {skip: "primary only"},
_migrateClone: {skip: "primary only"},
+ _shardsvrCompactStructuredEncryptionData: {skip: "primary only"},
_shardsvrMovePrimary: {skip: "primary only"},
_shardsvrMoveRange: {skip: "primary only"},
_recvChunkAbort: {skip: "primary only"},
@@ -136,6 +137,7 @@ let testCases = {
commitReshardCollection: {skip: "primary only"},
commitTransaction: {skip: "primary only"},
compact: {skip: "does not return user data"},
+ compactStructuredEncryptionData: {skip: "does not return user data"},
configureCollectionAutoSplitter: {
skip: "does not return user data"
}, // TODO SERVER-62374: remove this once 5.3 becomes last continuos release
diff --git a/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js b/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js
index ee81222de94..da5b4cb9d0d 100644
--- a/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js
+++ b/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js
@@ -92,6 +92,7 @@ let testCases = {
_killOperations: {skip: "does not return user data"},
_mergeAuthzCollections: {skip: "primary only"},
_migrateClone: {skip: "primary only"},
+ _shardsvrCompactStructuredEncryptionData: {skip: "primary only"},
_shardsvrMovePrimary: {skip: "primary only"},
_shardsvrMoveRange: {skip: "primary only"},
_recvChunkAbort: {skip: "primary only"},
@@ -156,6 +157,7 @@ let testCases = {
collMod: {skip: "primary only"},
collStats: {skip: "does not return user data"},
compact: {skip: "does not return user data"},
+ compactStructuredEncryptionData: {skip: "does not return user data"},
configureFailPoint: {skip: "does not return user data"},
connPoolStats: {skip: "does not return user data"},
connPoolSync: {skip: "does not return user data"},
diff --git a/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js b/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js
index 23eac4fe2c2..24991db2b9e 100644
--- a/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js
+++ b/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js
@@ -84,6 +84,7 @@ let testCases = {
_killOperations: {skip: "does not return user data"},
_mergeAuthzCollections: {skip: "primary only"},
_migrateClone: {skip: "primary only"},
+ _shardsvrCompactStructuredEncryptionData: {skip: "primary only"},
_shardsvrMovePrimary: {skip: "primary only"},
_shardsvrMoveRange: {skip: "primary only"},
_recvChunkAbort: {skip: "primary only"},
@@ -139,6 +140,7 @@ let testCases = {
commitReshardCollection: {skip: "primary only"},
commitTransaction: {skip: "primary only"},
compact: {skip: "does not return user data"},
+ compactStructuredEncryptionData: {skip: "does not return user data"},
configureCollectionAutoSplitter: {
skip: "does not return user data"
}, // TODO SERVER-62374: remove this once 5.3 becomes last continuos release
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 37055dd8a94..db92db44bac 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -85,6 +85,8 @@ env.Library(
source=[
'end_sessions_command.cpp',
'fail_point_cmd.cpp',
+ 'fle2_compact.cpp',
+ 'fle2_compact.idl',
'generic.cpp',
'generic.idl',
'hashcmd.cpp',
@@ -104,6 +106,7 @@ env.Library(
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
+ '$BUILD_DIR/mongo/crypto/encrypted_field_config',
'$BUILD_DIR/mongo/db/auth/auth',
'$BUILD_DIR/mongo/db/auth/authprivilege',
'$BUILD_DIR/mongo/db/commands',
@@ -114,9 +117,11 @@ env.Library(
'$BUILD_DIR/mongo/db/logical_session_id',
'$BUILD_DIR/mongo/db/logical_session_id_helpers',
'$BUILD_DIR/mongo/db/mongohasher',
+ '$BUILD_DIR/mongo/db/namespace_string',
'$BUILD_DIR/mongo/db/ops/write_ops_parsers',
'$BUILD_DIR/mongo/db/server_options_core',
'$BUILD_DIR/mongo/idl/basic_types',
+ '$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/idl/server_parameter',
'$BUILD_DIR/mongo/rpc/message',
'test_commands_enabled',
@@ -227,22 +232,6 @@ env.Library(
)
env.Library(
- target="fle2_compact",
- source=[
- 'fle2_compact.cpp',
- 'fle2_compact.idl',
- ],
- LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/crypto/encrypted_field_config',
- '$BUILD_DIR/mongo/db/auth/auth',
- '$BUILD_DIR/mongo/db/catalog/catalog_helpers',
- '$BUILD_DIR/mongo/db/catalog/collection_catalog',
- '$BUILD_DIR/mongo/db/catalog_raii',
- '$BUILD_DIR/mongo/db/commands',
- ]
-)
-
-env.Library(
target="mongod_fsync",
source=[
"fsync.cpp",
@@ -545,6 +534,7 @@ env.Library(
"dbhash.cpp",
"driverHelpers.cpp",
"internal_rename_if_options_and_indexes_match_cmd.cpp",
+ "fle2_compact_cmd.cpp",
"map_reduce_command.cpp",
"oplog_application_checks.cpp",
"oplog_note.cpp",
@@ -614,7 +604,6 @@ env.Library(
'$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/util/net/ssl_manager',
'core',
- 'fle2_compact',
'kill_common',
'map_reduce_agg',
'mongod_fcv',
diff --git a/src/mongo/db/commands/fle2_compact.cpp b/src/mongo/db/commands/fle2_compact.cpp
index 7588cbc6735..24256c6bc54 100644
--- a/src/mongo/db/commands/fle2_compact.cpp
+++ b/src/mongo/db/commands/fle2_compact.cpp
@@ -27,70 +27,14 @@
* it in the license file.
*/
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kStorage
-
#include "mongo/platform/basic.h"
#include "mongo/db/commands/fle2_compact.h"
#include "mongo/crypto/encryption_fields_gen.h"
-#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/catalog/collection_catalog.h"
-#include "mongo/db/catalog/rename_collection.h"
-#include "mongo/db/catalog_raii.h"
-#include "mongo/db/commands.h"
-#include "mongo/logv2/log.h"
namespace mongo {
-namespace {
-
-class CompactStructuredEncryptionDataCmd final
- : public TypedCommand<CompactStructuredEncryptionDataCmd> {
-public:
- using Request = CompactStructuredEncryptionData;
- using Reply = CompactStructuredEncryptionData::Reply;
- using TC = TypedCommand<CompactStructuredEncryptionDataCmd>;
-
- class Invocation final : public TC::InvocationBase {
- public:
- using TC::InvocationBase::InvocationBase;
- using TC::InvocationBase::request;
-
- Reply typedRun(OperationContext* opCtx) {
- auto swCompactStats = compactEncryptedCompactionCollection(opCtx, request());
- uassertStatusOK(swCompactStats);
- return Reply(swCompactStats.getValue());
- }
-
- private:
- bool supportsWriteConcern() const final {
- return false;
- }
-
- void doCheckAuthorization(OperationContext* opCtx) const final {
- auto* as = AuthorizationSession::get(opCtx->getClient());
- uassert(ErrorCodes::Unauthorized,
- "Not authorized to compact structured encryption data",
- as->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(request().getNamespace()),
- ActionType::compactStructuredEncryptionData));
- }
-
- NamespaceString ns() const final {
- return request().getNamespace();
- }
- };
-
- typename TC::AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
- return BasicCommand::AllowedOnSecondary::kNever;
- }
-
- bool adminOnly() const final {
- return false;
- }
-} compactStructuredEncryptionDataCmd;
-
-} // namespace
StatusWith<EncryptedStateCollectionsNamespaces>
EncryptedStateCollectionsNamespaces::createFromDataCollection(const Collection& edc) {
@@ -129,60 +73,4 @@ EncryptedStateCollectionsNamespaces::createFromDataCollection(const Collection&
return namespaces;
}
-StatusWith<CompactStats> compactEncryptedCompactionCollection(
- OperationContext* opCtx, const CompactStructuredEncryptionData& request) {
- mongo::ECOCStats ecocStats(0, 0);
- mongo::ECStats eccStats(0, 0, 0, 0);
- mongo::ECStats escStats(0, 0, 0, 0);
- const auto& edcNss = request.getNamespace();
-
- LOGV2(6319900, "Compacting the encrypted compaction collection", "namespace"_attr = edcNss);
-
- AutoGetDb autoDb(opCtx, edcNss.db(), MODE_IX);
- if (!autoDb.getDb()) {
- return Status(ErrorCodes::NamespaceNotFound, str::stream() << "database does not exist");
- }
-
- auto catalog = CollectionCatalog::get(opCtx);
- Lock::CollectionLock edcLock(opCtx, edcNss, MODE_IS);
-
- // Check the data collection exists and is not a view
- auto edc = catalog->lookupCollectionByNamespace(opCtx, edcNss);
- if (!edc) {
- if (catalog->lookupView(opCtx, edcNss)) {
- return Status(ErrorCodes::CommandNotSupportedOnView,
- "cannot compact structured encryption data on a view");
- }
- return Status(ErrorCodes::NamespaceNotFound, "collection does not exist");
- }
-
- uassert(6319903, "Feature flag FLE2 is not enabled", gFeatureFlagFLE2.isEnabledAndIgnoreFCV());
-
- auto swNamespaces = EncryptedStateCollectionsNamespaces::createFromDataCollection(*edc.get());
- if (!swNamespaces.isOK()) {
- return swNamespaces.getStatus();
- }
- auto& namespaces = swNamespaces.getValue();
-
- auto ecoc = catalog->lookupCollectionByNamespace(opCtx, namespaces.ecocNss);
- auto ecocRename = catalog->lookupCollectionByNamespace(opCtx, namespaces.ecocRenameNss);
-
- if (ecoc && !ecocRename) {
- LOGV2(6319901,
- "Renaming the encrypted compaction collection",
- "ecocNss"_attr = namespaces.ecocNss,
- "ecocRenameNss"_attr = namespaces.ecocRenameNss);
- RenameCollectionOptions renameOpts;
- validateAndRunRenameCollection(
- opCtx, namespaces.ecocNss, namespaces.ecocRenameNss, renameOpts);
- ecoc.reset();
- }
-
-
- LOGV2(6319902,
- "Done compacting the encrypted compaction collection",
- "namespace"_attr = request.getNamespace());
- return CompactStats(ecocStats, eccStats, escStats);
-}
-
} // namespace mongo
diff --git a/src/mongo/db/commands/fle2_compact.h b/src/mongo/db/commands/fle2_compact.h
index 35308325169..b994e22a5a2 100644
--- a/src/mongo/db/commands/fle2_compact.h
+++ b/src/mongo/db/commands/fle2_compact.h
@@ -30,8 +30,8 @@
#pragma once
#include "mongo/base/status_with.h"
+#include "mongo/db/catalog/collection.h"
#include "mongo/db/commands/fle2_compact_gen.h"
-#include "mongo/db/operation_context.h"
namespace mongo {
@@ -46,7 +46,4 @@ struct EncryptedStateCollectionsNamespaces {
NamespaceString ecocRenameNss;
};
-StatusWith<CompactStats> compactEncryptedCompactionCollection(
- OperationContext* opCtx, const CompactStructuredEncryptionData& request);
-
} // namespace mongo
diff --git a/src/mongo/db/commands/fle2_compact_cmd.cpp b/src/mongo/db/commands/fle2_compact_cmd.cpp
new file mode 100644
index 00000000000..ba3d8a7ed22
--- /dev/null
+++ b/src/mongo/db/commands/fle2_compact_cmd.cpp
@@ -0,0 +1,145 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/commands/fle2_compact.h"
+
+#include "mongo/crypto/encryption_fields_gen.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/catalog/collection_catalog.h"
+#include "mongo/db/catalog/rename_collection.h"
+#include "mongo/db/catalog_raii.h"
+#include "mongo/db/commands.h"
+#include "mongo/logv2/log.h"
+
+namespace mongo {
+namespace {
+
+CompactStats compactEncryptedCompactionCollection(OperationContext* opCtx,
+ const CompactStructuredEncryptionData& request) {
+ mongo::ECOCStats ecocStats(0, 0);
+ mongo::ECStats eccStats(0, 0, 0, 0);
+ mongo::ECStats escStats(0, 0, 0, 0);
+ const auto& edcNss = request.getNamespace();
+
+ LOGV2(6319900, "Compacting the encrypted compaction collection", "namespace"_attr = edcNss);
+
+ AutoGetDb autoDb(opCtx, edcNss.db(), MODE_IX);
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "Database '" << edcNss.db() << "' does not exist",
+ autoDb.getDb());
+
+ auto catalog = CollectionCatalog::get(opCtx);
+ Lock::CollectionLock edcLock(opCtx, edcNss, MODE_IS);
+
+ // Check the data collection exists and is not a view
+ auto edc = catalog->lookupCollectionByNamespace(opCtx, edcNss);
+ if (!edc) {
+ uassert(ErrorCodes::CommandNotSupportedOnView,
+ "Cannot compact structured encryption data on a view",
+ !catalog->lookupView(opCtx, edcNss));
+ uasserted(ErrorCodes::NamespaceNotFound,
+ str::stream() << "Collection '" << edcNss << "' does not exist");
+ }
+
+ uassert(6319903, "Feature flag FLE2 is not enabled", gFeatureFlagFLE2.isEnabledAndIgnoreFCV());
+
+ auto namespaces =
+ uassertStatusOK(EncryptedStateCollectionsNamespaces::createFromDataCollection(*edc.get()));
+
+
+ auto ecoc = catalog->lookupCollectionByNamespace(opCtx, namespaces.ecocNss);
+ auto ecocRename = catalog->lookupCollectionByNamespace(opCtx, namespaces.ecocRenameNss);
+
+ if (ecoc && !ecocRename) {
+ LOGV2(6319901,
+ "Renaming the encrypted compaction collection",
+ "ecocNss"_attr = namespaces.ecocNss,
+ "ecocRenameNss"_attr = namespaces.ecocRenameNss);
+ RenameCollectionOptions renameOpts;
+ validateAndRunRenameCollection(
+ opCtx, namespaces.ecocNss, namespaces.ecocRenameNss, renameOpts);
+ ecoc.reset();
+ }
+
+ LOGV2(6319902,
+ "Done compacting the encrypted compaction collection",
+ "namespace"_attr = request.getNamespace());
+ return CompactStats(ecocStats, eccStats, escStats);
+}
+
+class CompactStructuredEncryptionDataCmd final
+ : public TypedCommand<CompactStructuredEncryptionDataCmd> {
+public:
+ using Request = CompactStructuredEncryptionData;
+ using Reply = CompactStructuredEncryptionData::Reply;
+ using TC = TypedCommand<CompactStructuredEncryptionDataCmd>;
+
+ class Invocation final : public TC::InvocationBase {
+ public:
+ using TC::InvocationBase::InvocationBase;
+ using TC::InvocationBase::request;
+
+ Reply typedRun(OperationContext* opCtx) {
+ return Reply(compactEncryptedCompactionCollection(opCtx, request()));
+ }
+
+ private:
+ bool supportsWriteConcern() const final {
+ return false;
+ }
+
+ void doCheckAuthorization(OperationContext* opCtx) const final {
+ auto* as = AuthorizationSession::get(opCtx->getClient());
+ uassert(ErrorCodes::Unauthorized,
+ "Not authorized to compact structured encryption data",
+ as->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(request().getNamespace()),
+ ActionType::compactStructuredEncryptionData));
+ }
+
+ NamespaceString ns() const final {
+ return request().getNamespace();
+ }
+ };
+
+ typename TC::AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
+ return BasicCommand::AllowedOnSecondary::kNever;
+ }
+
+ bool adminOnly() const final {
+ return false;
+ }
+} compactStructuredEncryptionDataCmd;
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/namespace_string.cpp b/src/mongo/db/namespace_string.cpp
index ab4a511bc69..bd9d3a68d93 100644
--- a/src/mongo/db/namespace_string.cpp
+++ b/src/mongo/db/namespace_string.cpp
@@ -154,6 +154,9 @@ const NamespaceString NamespaceString::kConfigsvrCoordinatorsNamespace(
const NamespaceString NamespaceString::kUserWritesCriticalSectionsNamespace(
NamespaceString::kConfigDb, "user_writes_critical_sections");
+const NamespaceString NamespaceString::kCompactStructuredEncryptionCoordinatorNamespace(
+ NamespaceString::kConfigDb, "compact_structured_encryption_coordinator");
+
bool NamespaceString::isListCollectionsCursorNS() const {
return coll() == listCollectionsCursorCol;
}
diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h
index f511a6a3f04..6a9418f1775 100644
--- a/src/mongo/db/namespace_string.h
+++ b/src/mongo/db/namespace_string.h
@@ -213,6 +213,9 @@ public:
// Namespace used during the recovery procedure for the config server.
static const NamespaceString kConfigsvrRestoreNamespace;
+ // Namespace used for CompactParticipantCoordinator service.
+ static const NamespaceString kCompactStructuredEncryptionCoordinatorNamespace;
+
/**
* Constructs an empty NamespaceString.
*/
diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript
index 38cae8e2be5..96f60ade98d 100644
--- a/src/mongo/db/s/SConscript
+++ b/src/mongo/db/s/SConscript
@@ -320,6 +320,8 @@ env.Library(
'collmod_coordinator.cpp',
'collmod_coordinator_document.idl',
'collmod_coordinator_pre60_compatible.cpp',
+ 'compact_structured_encryption_data_coordinator.cpp',
+ 'compact_structured_encryption_data_coordinator.idl',
'config/set_cluster_parameter_coordinator.cpp',
'config/set_cluster_parameter_coordinator_document.idl',
'config/set_user_write_block_mode_coordinator.cpp',
@@ -399,6 +401,7 @@ env.Library(
'shardsvr_collmod_command.cpp',
'shardsvr_collmod_participant_command.cpp',
'shardsvr_commit_reshard_collection_command.cpp',
+ 'shardsvr_compact_structured_encryption_data_command.cpp',
'shardsvr_create_collection_command.cpp',
'shardsvr_create_collection_participant_command.cpp',
'shardsvr_drop_collection_command.cpp',
@@ -428,6 +431,7 @@ env.Library(
'$BUILD_DIR/mongo/db/commands/core',
'$BUILD_DIR/mongo/db/commands/create_command',
'$BUILD_DIR/mongo/db/commands/mongod_fcv',
+ '$BUILD_DIR/mongo/db/commands/rename_collection_idl',
'$BUILD_DIR/mongo/db/commands/server_status',
'$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/commands/txn_cmd_request',
diff --git a/src/mongo/db/s/compact_structured_encryption_data_coordinator.cpp b/src/mongo/db/s/compact_structured_encryption_data_coordinator.cpp
new file mode 100644
index 00000000000..ba8c48cbef1
--- /dev/null
+++ b/src/mongo/db/s/compact_structured_encryption_data_coordinator.cpp
@@ -0,0 +1,159 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/s/compact_structured_encryption_data_coordinator.h"
+
+#include "mongo/base/checked_cast.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/catalog/collection_catalog.h"
+#include "mongo/db/commands/rename_collection_gen.h"
+#include "mongo/db/dbdirectclient.h"
+#include "mongo/db/drop_gen.h"
+#include "mongo/db/persistent_task_store.h"
+#include "mongo/logv2/log.h"
+
+namespace mongo {
+namespace {
+
+const auto kMajorityWriteConcern = BSON("writeConcern" << BSON("w"
+ << "majority"));
+/**
+ * Issue a simple success/fail command such as renameCollection or drop
+ * using majority write concern.
+ */
+template <typename Request>
+void doRunCommand(OperationContext* opCtx, StringData dbname, const Request& request) {
+ DBDirectClient client(opCtx);
+ BSONObj cmd = request.toBSON(kMajorityWriteConcern);
+ auto reply = client.runCommand(OpMsgRequest::fromDBAndBody(dbname, cmd))->getCommandReply();
+ uassertStatusOK(getStatusFromCommandResult(reply));
+}
+
+void doRenameOperation(const CompactStructuredEncryptionDataState& state) {
+ const auto& ecocNss = state.getEcocNss();
+ auto opCtx = cc().makeOperationContext();
+ auto catalog = CollectionCatalog::get(opCtx.get());
+ auto uuid = catalog->lookupUUIDByNSS(opCtx.get(), ecocNss);
+ if (!uuid) {
+ // Nothing to rename.
+ LOGV2_DEBUG(6350492,
+ 5,
+ "Skipping rename stage as there is no source collection",
+ "ecocNss"_attr = ecocNss);
+ return;
+ } else if (uuid.get() != state.getEcocUuid()) {
+ // The generation of the collection to be compacted is different than the one which was
+ // requested.
+ LOGV2_DEBUG(6350491,
+ 5,
+ "Skipping rename of mismatched collection uuid",
+ "ecocNss"_attr = ecocNss,
+ "uuid"_attr = uuid.get(),
+ "expectedUUID"_attr = state.getEcocUuid());
+ return;
+ }
+
+ // Otherwise, perform the rename so long as the target namespace does not exist.
+ RenameCollectionCommand cmd(ecocNss, state.getEcocRenameNss());
+ cmd.setDropTarget(false);
+ cmd.setCollectionUUID(state.getEcocUuid());
+
+ doRunCommand(opCtx.get(), ecocNss.db(), cmd);
+}
+
+void doDropOperation(const CompactStructuredEncryptionDataState& state) {
+ auto ecocNss = state.getEcocRenameNss();
+ Drop cmd(ecocNss);
+ cmd.setCollectionUUID(state.getEcocUuid());
+ auto opCtx = cc().makeOperationContext();
+ doRunCommand(opCtx.get(), ecocNss.db(), cmd);
+}
+
+} // namespace
+
+boost::optional<BSONObj> CompactStructuredEncryptionDataCoordinator::reportForCurrentOp(
+ MongoProcessInterface::CurrentOpConnectionsMode connMode,
+ MongoProcessInterface::CurrentOpSessionsMode sessionMode) noexcept {
+ BSONObjBuilder bob;
+ bob.append("type", "op");
+ bob.append("desc", "CompactStructuredEncryptionDataCoordinator");
+ bob.append("op", "command");
+ bob.append("nss", _doc.getId().getNss().ns());
+ bob.append("ecocNss", _doc.getEcocNss().ns());
+ bob.append("ecocUuid", _doc.getEcocUuid().toString());
+ bob.append("ecocRenameNss", _doc.getEcocRenameNss().ns());
+ bob.append("currentPhase", _doc.getPhase());
+ bob.append("active", true);
+ return bob.obj();
+}
+
+void CompactStructuredEncryptionDataCoordinator::_enterPhase(Phase newPhase) {
+ StateDoc doc(_doc);
+ doc.setPhase(newPhase);
+
+ LOGV2_DEBUG(6350490,
+ 2,
+ "Transitioning phase for CompactStructuredEncryptionDataCoordinator",
+ "nss"_attr = _doc.getId().getNss().ns(),
+ "ecocNss"_attr = _doc.getEcocNss().ns(),
+ "ecocUuid"_attr = _doc.getEcocUuid(),
+ "ecocRenameNss"_attr = _doc.getEcocRenameNss().ns(),
+ "compactionTokens"_attr = _doc.getCompactionTokens(),
+ "oldPhase"_attr = CompactStructuredEncryptionDataPhase_serializer(_doc.getPhase()),
+ "newPhase"_attr = CompactStructuredEncryptionDataPhase_serializer(newPhase));
+
+ if (_doc.getPhase() == Phase::kRenameEcocForCompact) {
+ _doc = _insertStateDocument(std::move(doc));
+ return;
+ }
+ auto opCtx = cc().makeOperationContext();
+ _doc = _updateStateDocument(opCtx.get(), std::move(doc));
+}
+
+ExecutorFuture<void> CompactStructuredEncryptionDataCoordinator::_runImpl(
+ std::shared_ptr<executor::ScopedTaskExecutor> executor,
+ const CancellationToken& token) noexcept {
+ return ExecutorFuture<void>(**executor)
+ .then(_executePhase(Phase::kRenameEcocForCompact, doRenameOperation))
+ .then(_executePhase(Phase::kCompactStructuredEncryptionData,
+ [this, anchor = shared_from_this()](const auto&) {
+ // This will be implemented in a later phase.
+ ECOCStats ecocStats(1, 2);
+ ECStats eccStats(3, 4, 5, 6);
+ ECStats escStats(7, 8, 9, 10);
+ _response = CompactStats(ecocStats, eccStats, escStats);
+ }))
+ .then(_executePhase(Phase::kDropTempCollection, doDropOperation));
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/s/compact_structured_encryption_data_coordinator.h b/src/mongo/db/s/compact_structured_encryption_data_coordinator.h
new file mode 100644
index 00000000000..cd5a2319250
--- /dev/null
+++ b/src/mongo/db/s/compact_structured_encryption_data_coordinator.h
@@ -0,0 +1,100 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <memory>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/commands/fle2_compact_gen.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/s/compact_structured_encryption_data_coordinator_gen.h"
+#include "mongo/db/s/sharding_ddl_coordinator.h"
+
+namespace mongo {
+
+class CompactStructuredEncryptionDataCoordinator final : public ShardingDDLCoordinator {
+public:
+ static constexpr auto kStateContext = "CompactStructuredEncryptionDataState"_sd;
+ using StateDoc = CompactStructuredEncryptionDataState;
+ using Phase = CompactStructuredEncryptionDataPhaseEnum;
+
+ CompactStructuredEncryptionDataCoordinator(ShardingDDLCoordinatorService* service,
+ const BSONObj& doc)
+ : ShardingDDLCoordinator(service, doc), _doc(StateDoc::parse({kStateContext}, doc)) {}
+
+ boost::optional<BSONObj> reportForCurrentOp(
+ MongoProcessInterface::CurrentOpConnectionsMode connMode,
+ MongoProcessInterface::CurrentOpSessionsMode sessionMode) noexcept final;
+
+ CompactStructuredEncryptionDataCommandReply getResponse(OperationContext* opCtx) {
+ getCompletionFuture().get(opCtx);
+ invariant(_response);
+ return *_response;
+ }
+
+ const NamespaceString& nss() const {
+ return _doc.getId().getNss();
+ }
+
+ void checkIfOptionsConflict(const BSONObj& doc) const final {}
+
+private:
+ void _enterPhase(Phase newPhase);
+
+ template <typename Func>
+ auto _executePhase(const Phase& newPhase, Func&& func) {
+ return [=] {
+ const auto& currPhase = _doc.getPhase();
+ if (currPhase > newPhase) {
+ return;
+ }
+ if (currPhase < newPhase) {
+ _enterPhase(newPhase);
+ }
+
+ return func(_doc);
+ };
+ }
+
+private:
+ ShardingDDLCoordinatorMetadata const& metadata() const final {
+ return _doc.getShardingDDLCoordinatorMetadata();
+ }
+
+ ExecutorFuture<void> _runImpl(std::shared_ptr<executor::ScopedTaskExecutor> executor,
+ const CancellationToken& token) noexcept final;
+
+private:
+ StateDoc _doc;
+ boost::optional<CompactStructuredEncryptionDataCommandReply> _response;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/s/compact_structured_encryption_data_coordinator.idl b/src/mongo/db/s/compact_structured_encryption_data_coordinator.idl
new file mode 100644
index 00000000000..a22a235495b
--- /dev/null
+++ b/src/mongo/db/s/compact_structured_encryption_data_coordinator.idl
@@ -0,0 +1,68 @@
+# Copyright (C) 2021-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# As a special exception, the copyright holders give permission to link the
+# code of portions of this program with the OpenSSL library under certain
+# conditions as described in each individual source file and distribute
+# linked combinations including the program with the OpenSSL library. You
+# must comply with the Server Side Public License in all respects for
+# all of the code used other than as permitted herein. If you modify file(s)
+# with this exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version. If you delete this
+# exception statement from all source files in the program, then also delete
+# it in the license file.
+#
+
+global:
+ cpp_namespace: "mongo"
+
+imports:
+ - "mongo/idl/basic_types.idl"
+ - "mongo/db/commands/fle2_compact.idl"
+ - "mongo/db/s/sharding_ddl_coordinator.idl"
+
+enums:
+ CompactStructuredEncryptionDataPhase:
+ description: "The current phase of the compactStructuredEncryptionData pipeline"
+ type: string
+ values:
+ kRenameEcocForCompact: "rename-ecoc-for-compact"
+ kCompactStructuredEncryptionData: "compact-structured-encryption-data"
+ kDropTempCollection: "drop-temp-collection"
+
+structs:
+ CompactStructuredEncryptionDataState:
+ description: "Represents the state of the compactStructuredEncryptionData pipeline"
+ strict: true
+ chained_structs:
+ ShardingDDLCoordinatorMetadata: ShardingDDLCoordinatorMetadata
+ fields:
+ phase:
+ description: "Current phase"
+ type: CompactStructuredEncryptionDataPhase
+ default: kRenameEcocForCompact
+ ecocNss:
+ description: "Collection containing compaction metadata to perform compact with"
+ type: namespacestring
+ ecocUuid:
+ description: "UUID of the collection identified by ecocNss"
+ type: uuid
+ ecocRenameNss:
+ description: "Temporary name to use while performing compaction"
+ type: namespacestring
+ compactionTokens:
+ description: "Compaction tokens for the compact operation"
+ type: object_owned
diff --git a/src/mongo/db/s/sharding_ddl_coordinator.idl b/src/mongo/db/s/sharding_ddl_coordinator.idl
index eb0b7e88590..3a918da262d 100644
--- a/src/mongo/db/s/sharding_ddl_coordinator.idl
+++ b/src/mongo/db/s/sharding_ddl_coordinator.idl
@@ -58,6 +58,7 @@ enums:
kCollMod: "collMod_V2"
kReshardCollection: "reshardCollection"
kReshardCollectionNoResilient: "reshardCollectionNoResilient"
+ kCompactStructuredEncryptionData: "compactStructuredEncryptionData"
types:
ForwardableOperationMetadata:
diff --git a/src/mongo/db/s/sharding_ddl_coordinator_service.cpp b/src/mongo/db/s/sharding_ddl_coordinator_service.cpp
index 60e288f2082..5fb5a6fb6ac 100644
--- a/src/mongo/db/s/sharding_ddl_coordinator_service.cpp
+++ b/src/mongo/db/s/sharding_ddl_coordinator_service.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/pipeline/expression_context.h"
#include "mongo/db/s/collmod_coordinator.h"
#include "mongo/db/s/collmod_coordinator_pre60_compatible.h"
+#include "mongo/db/s/compact_structured_encryption_data_coordinator.h"
#include "mongo/db/s/create_collection_coordinator.h"
#include "mongo/db/s/database_sharding_state.h"
#include "mongo/db/s/drop_collection_coordinator.h"
@@ -106,6 +107,10 @@ std::shared_ptr<ShardingDDLCoordinator> constructShardingDDLCoordinatorInstance(
return std::make_shared<ReshardCollectionCoordinator_NORESILIENT>(
service, std::move(initialState));
break;
+ case DDLCoordinatorTypeEnum::kCompactStructuredEncryptionData:
+ return std::make_shared<CompactStructuredEncryptionDataCoordinator>(
+ service, std::move(initialState));
+ break;
default:
uasserted(ErrorCodes::BadValue,
str::stream()
diff --git a/src/mongo/db/s/shardsvr_compact_structured_encryption_data_command.cpp b/src/mongo/db/s/shardsvr_compact_structured_encryption_data_command.cpp
new file mode 100644
index 00000000000..f777a7fd309
--- /dev/null
+++ b/src/mongo/db/s/shardsvr_compact_structured_encryption_data_command.cpp
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
+
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/catalog/collection_catalog.h"
+#include "mongo/db/catalog_raii.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/commands/fle2_compact.h"
+#include "mongo/db/commands/fle2_compact_gen.h"
+#include "mongo/db/s/compact_structured_encryption_data_coordinator.h"
+#include "mongo/db/s/compact_structured_encryption_data_coordinator_gen.h"
+#include "mongo/logv2/log.h"
+#include "mongo/s/cluster_commands_helpers.h"
+
+namespace mongo {
+namespace {
+
+class _shardsvrCompactStructuredEncryptionDataCommand final
+ : public TypedCommand<_shardsvrCompactStructuredEncryptionDataCommand> {
+public:
+ using Request = CompactStructuredEncryptionData;
+ using Reply = typename Request::Reply;
+
+ _shardsvrCompactStructuredEncryptionDataCommand()
+ : TypedCommand("_shardsvrCompactStructuredEncryptionData"_sd) {}
+
+ bool skipApiVersionCheck() const final {
+ // Internal command (server to server).
+ return true;
+ }
+
+ std::string help() const final {
+ return "Internal command. Do not call directly. Compacts a ECOC collection.";
+ }
+
+ bool adminOnly() const final {
+ return false;
+ }
+
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
+ return AllowedOnSecondary::kNever;
+ }
+
+ class Invocation final : public InvocationBase {
+ public:
+ using InvocationBase::InvocationBase;
+
+ Reply typedRun(OperationContext* opCtx) {
+ uassert(6350499,
+ "Feature flag FLE2 is not enabled",
+ gFeatureFlagFLE2.isEnabledAndIgnoreFCV());
+
+ auto compact = makeRequest(opCtx);
+ if (!compact) {
+ // Nothing to do.
+ return {{{0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}};
+ }
+
+ return checked_pointer_cast<CompactStructuredEncryptionDataCoordinator>(
+ ShardingDDLCoordinatorService::getService(opCtx)->getOrCreateInstance(
+ opCtx, compact->toBSON()))
+ ->getResponse(opCtx);
+ }
+
+ private:
+ boost::optional<CompactStructuredEncryptionDataState> makeRequest(OperationContext* opCtx) {
+ const auto& req = request();
+ const auto& nss = req.getNamespace();
+
+ AutoGetCollection baseColl(opCtx, nss, MODE_IX);
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "Unknown collection: " << nss,
+ baseColl.getCollection());
+
+ auto namespaces =
+ uassertStatusOK(EncryptedStateCollectionsNamespaces::createFromDataCollection(
+ *(baseColl.getCollection().get())));
+
+ AutoGetCollection ecocColl(opCtx, namespaces.ecocNss, MODE_IX);
+ if (!ecocColl.getCollection()) {
+ return boost::none;
+ }
+ auto ecocUUID = ecocColl->uuid();
+
+ // Append UUID to rename namespace.
+ NamespaceString renameNss(namespaces.ecocRenameNss.db(),
+ str::stream() << namespaces.ecocRenameNss << '.' << ecocUUID);
+
+ CompactStructuredEncryptionDataState compact;
+ compact.setShardingDDLCoordinatorMetadata(
+ {{nss, DDLCoordinatorTypeEnum::kCompactStructuredEncryptionData}});
+ compact.setEcocNss(namespaces.ecocNss);
+ compact.setEcocUuid(std::move(ecocUUID));
+ compact.setEcocRenameNss(renameNss);
+ compact.setCompactionTokens(req.getCompactionTokens().getOwned());
+
+ return compact;
+ }
+
+ NamespaceString ns() const override {
+ return request().getNamespace();
+ }
+
+ bool supportsWriteConcern() const override {
+ return true;
+ }
+
+ void doCheckAuthorization(OperationContext* opCtx) const override {
+ uassert(ErrorCodes::Unauthorized,
+ "Unauthorized",
+ AuthorizationSession::get(opCtx->getClient())
+ ->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
+ ActionType::internal));
+ }
+ };
+
+} shardsvrCompactStructuredEncryptionDataCommand;
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index b9f96eb6a29..385824d19f6 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -57,6 +57,7 @@ env.Library(
'cluster_filemd5_cmd.cpp',
'cluster_find_and_modify_cmd.cpp',
'cluster_find_cmd_s.cpp',
+ 'cluster_fle2_compact_cmd.cpp',
'cluster_fsync_cmd.cpp',
'cluster_ftdc_commands.cpp',
'cluster_get_last_error_cmd.cpp',
diff --git a/src/mongo/s/commands/cluster_fle2_compact_cmd.cpp b/src/mongo/s/commands/cluster_fle2_compact_cmd.cpp
new file mode 100644
index 00000000000..b1cecbf555e
--- /dev/null
+++ b/src/mongo/s/commands/cluster_fle2_compact_cmd.cpp
@@ -0,0 +1,117 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/commands/fle2_compact_gen.h"
+#include "mongo/s/cluster_commands_helpers.h"
+#include "mongo/s/grid.h"
+
+namespace mongo {
+namespace {
+
+class ClusterCompactStructuredEncryptionDataCmd final
+ : public TypedCommand<ClusterCompactStructuredEncryptionDataCmd> {
+public:
+ using Request = CompactStructuredEncryptionData;
+ using Reply = CompactStructuredEncryptionData::Reply;
+
+ class Invocation final : public InvocationBase {
+ public:
+ using InvocationBase::InvocationBase;
+
+ Reply typedRun(OperationContext* opCtx);
+
+ private:
+ bool supportsWriteConcern() const final {
+ return false;
+ }
+
+ void doCheckAuthorization(OperationContext* opCtx) const final {
+ auto* as = AuthorizationSession::get(opCtx->getClient());
+ uassert(ErrorCodes::Unauthorized,
+ "Not authorized to compact structured encryption data",
+ as->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(request().getNamespace()),
+ ActionType::compactStructuredEncryptionData));
+ }
+
+ NamespaceString ns() const final {
+ return request().getNamespace();
+ }
+ };
+
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
+ return BasicCommand::AllowedOnSecondary::kNever;
+ }
+
+ bool adminOnly() const final {
+ return false;
+ }
+} clusterCompactStructuredEncryptionDataCmd;
+
+using Cmd = ClusterCompactStructuredEncryptionDataCmd;
+Cmd::Reply Cmd::Invocation::typedRun(OperationContext* opCtx) {
+ auto nss = request().getNamespace();
+ const auto dbInfo =
+ uassertStatusOK(Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, nss.db()));
+
+ // Rewrite command verb to _shardSvrCompactStructuredEnccryptionData.
+ auto cmd = request().toBSON({});
+ BSONObjBuilder req;
+ for (const auto& elem : cmd) {
+ if (elem.fieldNameStringData() == Request::kCommandName) {
+ req.appendAs(elem, "_shardsvrCompactStructuredEncryptionData");
+ } else {
+ req.append(elem);
+ }
+ }
+
+ auto response = uassertStatusOK(
+ executeCommandAgainstDatabasePrimary(
+ opCtx,
+ nss.db(),
+ dbInfo,
+ CommandHelpers::appendMajorityWriteConcern(req.obj(), opCtx->getWriteConcern()),
+ ReadPreferenceSetting(ReadPreference::PrimaryOnly),
+ Shard::RetryPolicy::kIdempotent)
+ .swResponse);
+
+ BSONObjBuilder result;
+ CommandHelpers::filterCommandReplyForPassthrough(response.data, &result);
+
+ auto reply = result.obj();
+ uassertStatusOK(getStatusFromCommandResult(reply));
+ return Reply::parse({Request::kCommandName}, reply.removeField("ok"_sd));
+}
+
+} // namespace
+} // namespace mongo