Last active
March 10, 2023 22:24
-
-
Save jyemin/d509b307db4108d2daa3d813f5ae827f to your computer and use it in GitHub Desktop.
A performance test comparing no encryption, manual encryption, and implicit encryption
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output on Mac Mini (your mileage may vary):