Skip to content

Instantly share code, notes, and snippets.

@jyemin
Last active March 10, 2023 22:24
Show Gist options
  • Save jyemin/d509b307db4108d2daa3d813f5ae827f to your computer and use it in GitHub Desktop.
Save jyemin/d509b307db4108d2daa3d813f5ae827f to your computer and use it in GitHub Desktop.
A performance test comparing no encryption, manual encryption, and implicit encryption
/*
* Copyright 2023 MongoDB, Inc.
*/
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.ClientEncryptionSettings;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.vault.EncryptOptions;
import com.mongodb.client.vault.ClientEncryption;
import com.mongodb.client.vault.ClientEncryptions;
import org.bson.BsonBinary;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.bson.BsonValue;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
/**
* Executes a workload of inserts, where each document containing one short string field.
* It executes 100 insert operations, each with 10,000 documents (you can change this easily by modifying a couple of
* lines in the main method.
* The workload is run three times, once with no encryption, once with manual encryption, and once with implicit
* encryption.
* It uses a local key and AEAD_AES_256_CBC_HMAC_SHA_512-Random encryption algorithm
*/
public class QueryableEncryptionBenchmark {
public static void main(String[] args) {
byte[] key = new byte[96];
new Random().nextBytes(key);
var clientEncryption = ClientEncryptions.create(
ClientEncryptionSettings.builder()
.keyVaultNamespace("test.key.vault")
.keyVaultMongoClientSettings(MongoClientSettings.builder().build())
.kmsProviders(Map.of("local", Map.of("key", key)))
.build());
var dataKey = clientEncryption.createDataKey("local");
// This just controls how long the workload runs for
int numInsertOperations = 100;
// As this number is decreased towards 1, the advantage of implicit decryption disappears
int numDocumentsPerInsert = 10_000;
executeWorkloadWithNoEncryption(numInsertOperations, numDocumentsPerInsert);
executeWorkloadWithManualEncryption(clientEncryption, dataKey, numInsertOperations, numDocumentsPerInsert);
executeWorkloadWithImplicitEncryption(key, dataKey, numInsertOperations, numDocumentsPerInsert);
}
private static void executeWorkloadWithNoEncryption(int numInsertOperations, int numDocumentsPerInsert) {
System.out.println();
System.out.println("Workload with no encryption");
executeWorkload(MongoClientSettings.builder().build(), value -> value, numInsertOperations, numDocumentsPerInsert);
}
private static void executeWorkloadWithManualEncryption(ClientEncryption clientEncryption, BsonBinary dataKey,
int numInsertOperations, int numDocumentsPerInsert) {
System.out.println();
System.out.println("Workload with manual encryption");
executeWorkload(MongoClientSettings.builder().build(),
value -> clientEncryption.encrypt(value,
new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Random").keyId(dataKey)),
numInsertOperations, numDocumentsPerInsert);
}
private static void executeWorkloadWithImplicitEncryption(byte[] key, BsonBinary dataKey,
int numInsertOperations, int numDocumentsPerInsert) {
System.out.println();
System.out.println("Workload with implicit encryption");
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
.keyVaultNamespace("test.key.vault")
.kmsProviders(Map.of("local", Map.of("key", key)))
.schemaMap(Map.of("test.csfleExplicit", BsonDocument.parse(
"{"
+ " properties: {"
+ " ssn: {"
+ " encrypt: {"
+ " keyId: [{"
+ " \"$binary\": {"
+ " base64: \"" + Base64.getEncoder().encodeToString(dataKey.getData()) + "\","
+ " subType: \"04\""
+ " }"
+ " }],"
+ " bsonType: \"string\","
+ " algorithm: \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\""
+ " }"
+ " }"
+ " },"
+ " bsonType: \"object\""
+ "}")))
.build();
MongoClientSettings settings = MongoClientSettings.builder()
.autoEncryptionSettings(autoEncryptionSettings)
.build();
executeWorkload(settings, value -> value, numInsertOperations, numDocumentsPerInsert);
}
private static void executeWorkload(MongoClientSettings settings, Function<BsonValue, BsonValue> manualEncrypter,
int numInsertOperations, int numDocumentsPerInsert) {
System.out.println("Num insert operations: " + numInsertOperations);
System.out.println("Num documents per insert: " + numDocumentsPerInsert);
try (var client = MongoClients.create(settings)) {
MongoDatabase db = client.getDatabase("test");
var collection = db.getCollection("csfleExplicit", BsonDocument.class);
collection.drop();
db.createCollection("csfleExplicit");
long startTime = System.nanoTime();
for (int j = 0; j < numInsertOperations; j++) {
var documents = new ArrayList<BsonDocument>();
for (int i = 0; i < numDocumentsPerInsert; i++) {
var doc = new BsonDocument();
BsonString ssn = new BsonString("123-45-7890");
BsonValue possiblyEncrypted = manualEncrypter.apply(ssn);
doc.append("ssn", possiblyEncrypted);
documents.add(doc);
}
collection.insertMany(documents);
}
long endTime = System.nanoTime();
System.out.println("Elapsed time: " + (endTime - startTime) / 1_000_000_000.0 + " sec");
}
}
}
@jyemin
Copy link
Author

jyemin commented Mar 10, 2023

Output on Mac Mini (your mileage may vary):

Workload with no encryption
Num insert operations: 100
Num documents per insert: 10000
Elapsed time: 4.464727229 sec

Workload with manual encryption
Num insert operations: 100
Num documents per insert: 10000
Elapsed time: 35.903701774 sec

Workload with implicit encryption
Num insert operations: 100
Num documents per insert: 10000
Elapsed time: 25.174933898 sec

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment