-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate mongodb-crypt module #1487
Merged
Merged
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
ceb18e5
Integrate mongodb-crypt module into mongo-java-driver as a new Gradle…
vbabanin fb7c733
Integrate mongocrypt tests.
vbabanin 0750e20
Prevent downloading binaries on each local run.
vbabanin 578a0a8
Merge branch 'master' into JAVA-5582
vbabanin 4d967e3
Update manifest.
vbabanin ed15a21
Merge branch 'master' into JAVA-5582
vbabanin b71f621
Add slf4j to manifest.
vbabanin 1233c1b
Remove gitignore due to having one in root directory.
vbabanin cc32a17
- Fix checkstyle issues.
vbabanin b8bad4e
Remove redundant benchmark.
vbabanin ee7d96c
Fix checkstyle issues.
vbabanin 1e38914
Ensure incremental builds for unzip and downloadJava tasks.
vbabanin a99def1
Remove redundant configs.
vbabanin d390438
Format code.
vbabanin fb6f31f
Merge branch 'master' into JAVA-5582
vbabanin 070fba8
Move mongocrypt benchmark.
vbabanin 5495133
Delete whole resources dir instead of individual files.
vbabanin b0438fd
Add runtimeElemens to GraalVM script, as this configuration is meant …
vbabanin 10f3cf5
PR nits
rozza 7b5738b
Spotbugs naming convention fix
rozza 204330b
Remove logback from dependencies.
vbabanin a8cee8d
Fix driver-core pom optional.
rozza acf2205
Merge branch 'master' into JAVA-5582
rozza 6699721
Merge branch 'master' into JAVA-5582
rozza 28c5b83
Add mongocrypt support to driver-sync and reactive streams driver
rozza 6e3e706
Changed the project name in the configuration to align with the artif…
vbabanin a8699de
Move mongocrypt to mongodb-crypt to work around optional project name…
rozza File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
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
224 changes: 224 additions & 0 deletions
224
driver-benchmarks/src/main/com/mongodb/benchmark/framework/MongoCryptBenchmarkRunner.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
package com.mongodb.benchmark.framework; | ||
|
||
/* | ||
* Copyright 2023-present MongoDB, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
import com.mongodb.crypt.capi.CAPI; | ||
import com.mongodb.crypt.capi.MongoCrypt; | ||
import com.mongodb.crypt.capi.MongoCryptContext; | ||
import com.mongodb.crypt.capi.MongoCryptOptions; | ||
import com.mongodb.crypt.capi.MongoCrypts; | ||
import com.mongodb.crypt.capi.MongoExplicitEncryptOptions; | ||
import com.mongodb.crypt.capi.MongoLocalKmsProviderOptions; | ||
import org.bson.BsonBinary; | ||
import org.bson.BsonBinarySubType; | ||
import org.bson.BsonDocument; | ||
import org.bson.BsonString; | ||
import org.bson.BsonValue; | ||
import org.bson.RawBsonDocument; | ||
|
||
import java.net.URL; | ||
import java.nio.ByteBuffer; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.time.ZoneOffset; | ||
import java.time.ZonedDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.util.ArrayList; | ||
import java.util.Base64; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
public class MongoCryptBenchmarkRunner { | ||
static final int NUM_FIELDS = 1500; | ||
static final int NUM_WARMUP_SECS = 2; | ||
static final int NUM_SECS = 10; | ||
static final byte[] LOCAL_MASTER_KEY = new byte[]{ | ||
-99, -108, 75, 13, -109, -48, -59, 68, -91, 114, -3, 50, 27, -108, 48, -112, 35, 53, | ||
115, 124, -16, -10, -62, -12, -38, 35, 86, -25, -113, 4, -52, -6, -34, 117, -76, 81, | ||
-121, -13, -117, -105, -41, 75, 68, 59, -84, 57, -94, -58, 77, -111, 0, 62, -47, -6, 74, | ||
48, -63, -46, -58, 94, -5, -84, 65, -14, 72, 19, 60, -101, 80, -4, -89, 36, 122, 46, 2, | ||
99, -93, -58, 22, 37, 81, 80, 120, 62, 15, -40, 110, -124, -90, -20, -115, 45, 36, 71, | ||
-27, -81 | ||
}; | ||
|
||
private static String getFileAsString(final String fileName) { | ||
try { | ||
URL resource = BenchmarkRunner.class.getResource("/" + fileName); | ||
if (resource == null) { | ||
throw new RuntimeException("Could not find file " + fileName); | ||
} | ||
return new String(Files.readAllBytes(Paths.get(resource.toURI()))); | ||
} catch (Throwable t) { | ||
throw new RuntimeException("Could not parse file " + fileName, t); | ||
} | ||
} | ||
|
||
private static BsonDocument getResourceAsDocument(final String fileName) { | ||
return BsonDocument.parse(getFileAsString(fileName)); | ||
} | ||
|
||
private static MongoCrypt createMongoCrypt() { | ||
return MongoCrypts.create(MongoCryptOptions | ||
.builder() | ||
.localKmsProviderOptions(MongoLocalKmsProviderOptions.builder() | ||
.localMasterKey(ByteBuffer.wrap(LOCAL_MASTER_KEY)) | ||
.build()) | ||
.build()); | ||
} | ||
|
||
// DecryptTask decrypts a document repeatedly for a specified number of seconds and records ops/sec. | ||
private static class DecryptTask implements Runnable { | ||
public DecryptTask(MongoCrypt mongoCrypt, BsonDocument toDecrypt, int numSecs, CountDownLatch doneSignal) { | ||
this.mongoCrypt = mongoCrypt; | ||
this.toDecrypt = toDecrypt; | ||
this.opsPerSecs = new ArrayList<Long>(numSecs); | ||
this.numSecs = numSecs; | ||
this.doneSignal = doneSignal; | ||
} | ||
|
||
public void run() { | ||
for (int i = 0; i < numSecs; i++) { | ||
long opsPerSec = 0; | ||
long start = System.nanoTime(); | ||
// Run for one second. | ||
while (System.nanoTime() - start < 1_000_000_000) { | ||
try (MongoCryptContext ctx = mongoCrypt.createDecryptionContext(toDecrypt)) { | ||
assert ctx.getState() == MongoCryptContext.State.READY; | ||
ctx.finish(); | ||
opsPerSec++; | ||
} | ||
} | ||
opsPerSecs.add(opsPerSec); | ||
} | ||
doneSignal.countDown(); | ||
} | ||
|
||
public long getMedianOpsPerSecs() { | ||
if (opsPerSecs.size() == 0) { | ||
throw new IllegalStateException("opsPerSecs is empty. Was `run` called?"); | ||
} | ||
Collections.sort(opsPerSecs); | ||
return opsPerSecs.get(numSecs / 2); | ||
} | ||
|
||
private MongoCrypt mongoCrypt; | ||
private BsonDocument toDecrypt; | ||
private ArrayList<Long> opsPerSecs; | ||
private int numSecs; | ||
private CountDownLatch doneSignal; | ||
} | ||
|
||
public List<MongocryptBecnhmarkResult> run() throws InterruptedException { | ||
System.out.printf("BenchmarkRunner is using libmongocrypt version=%s, NUM_WARMUP_SECS=%d, NUM_SECS=%d%n", | ||
CAPI.mongocrypt_version(null).toString(), NUM_WARMUP_SECS, NUM_SECS); | ||
// `keyDocument` is a Data Encryption Key (DEK) encrypted with the Key Encryption Key (KEK) `LOCAL_MASTER_KEY`. | ||
BsonDocument keyDocument = getResourceAsDocument("keyDocument.json"); | ||
try (MongoCrypt mongoCrypt = createMongoCrypt()) { | ||
// `encrypted` will contain encrypted fields. | ||
BsonDocument encrypted = new BsonDocument(); | ||
{ | ||
for (int i = 0; i < NUM_FIELDS; i++) { | ||
MongoExplicitEncryptOptions options = MongoExplicitEncryptOptions.builder() | ||
.keyId(new BsonBinary(BsonBinarySubType.UUID_STANDARD, Base64.getDecoder().decode("YWFhYWFhYWFhYWFhYWFhYQ=="))) | ||
.algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") | ||
.build(); | ||
BsonDocument toEncrypt = new BsonDocument("v", new BsonString(String.format("value %04d", i))); | ||
try (MongoCryptContext ctx = mongoCrypt.createExplicitEncryptionContext(toEncrypt, options)) { | ||
// If mongocrypt_t has not yet cached the DEK, supply it. | ||
if (MongoCryptContext.State.NEED_MONGO_KEYS == ctx.getState()) { | ||
ctx.addMongoOperationResult(keyDocument); | ||
ctx.completeMongoOperation(); | ||
} | ||
assert ctx.getState() == MongoCryptContext.State.READY; | ||
RawBsonDocument result = ctx.finish(); | ||
BsonValue encryptedValue = result.get("v"); | ||
String key = String.format("key%04d", i); | ||
encrypted.append(key, encryptedValue); | ||
} | ||
} | ||
} | ||
|
||
// Warm up benchmark and discard the result. | ||
DecryptTask warmup = new DecryptTask(mongoCrypt, encrypted, NUM_WARMUP_SECS, new CountDownLatch(1)); | ||
warmup.run(); | ||
|
||
// Decrypt `encrypted` and measure ops/sec. | ||
// Check with varying thread counts to measure impact of a shared pool of Cipher instances. | ||
int[] threadCounts = {1, 2, 8, 64}; | ||
ArrayList<Long> totalMedianOpsPerSecs = new ArrayList<Long>(threadCounts.length); | ||
ArrayList<String> createdAts = new ArrayList<String>(threadCounts.length); | ||
ArrayList<String> completedAts = new ArrayList<String>(threadCounts.length); | ||
|
||
for (int threadCount : threadCounts) { | ||
ExecutorService executorService = Executors.newFixedThreadPool(threadCount); | ||
CountDownLatch doneSignal = new CountDownLatch(threadCount); | ||
ArrayList<DecryptTask> decryptTasks = new ArrayList<DecryptTask>(threadCount); | ||
createdAts.add(ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT)); | ||
|
||
for (int i = 0; i < threadCount; i++) { | ||
DecryptTask decryptTask = new DecryptTask(mongoCrypt, encrypted, NUM_SECS, doneSignal); | ||
decryptTasks.add(decryptTask); | ||
executorService.submit(decryptTask); | ||
} | ||
|
||
// Await completion of all tasks. Tasks are expected to complete shortly after NUM_SECS. Time out `await` if time exceeds 2 * NUM_SECS. | ||
boolean ok = doneSignal.await(NUM_SECS * 2, TimeUnit.SECONDS); | ||
assert ok; | ||
completedAts.add(ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT)); | ||
// Sum the median ops/secs of all tasks to get total throughput. | ||
long totalMedianOpsPerSec = 0; | ||
for (DecryptTask decryptTask : decryptTasks) { | ||
totalMedianOpsPerSec += decryptTask.getMedianOpsPerSecs(); | ||
} | ||
System.out.printf("threadCount=%d. Decrypting 1500 fields median ops/sec : %d%n", threadCount, totalMedianOpsPerSec); | ||
totalMedianOpsPerSecs.add(totalMedianOpsPerSec); | ||
executorService.shutdown(); | ||
ok = executorService.awaitTermination(NUM_SECS * 2, TimeUnit.SECONDS); | ||
assert ok; | ||
} | ||
|
||
// Print the results in JSON that can be accepted by the `perf.send` command. | ||
// See https://docs.devprod.prod.corp.mongodb.com/evergreen/Project-Configuration/Project-Commands#perfsend for the expected `perf.send` input. | ||
List<MongocryptBecnhmarkResult> results = new ArrayList<>(threadCounts.length); | ||
for (int i = 0; i < threadCounts.length; i++) { | ||
int threadCount = threadCounts[i]; | ||
long totalMedianOpsPerSec = totalMedianOpsPerSecs.get(i); | ||
String createdAt = createdAts.get(i); | ||
String completedAt = completedAts.get(i); | ||
|
||
MongocryptBecnhmarkResult result = new MongocryptBecnhmarkResult( | ||
"java_decrypt_1500", | ||
threadCount, | ||
totalMedianOpsPerSec, | ||
createdAt, | ||
completedAt, | ||
"medianOpsPerSec", | ||
"THROUGHPUT"); | ||
|
||
results.add(result); | ||
} | ||
System.out.println("Results: " + results); | ||
return results; | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please note, the benchmark runner is directly copied from mongocrypt with minor adjustments (added MongoCryptBenchmarkResult). It has not been fully refactored to better align with the benchmark framework; we may consider refactoring in a separate scope. Functionally, it executes the tests, which can be verified by running performance tests on this build and checking the trends graph.