From 1d003871814a7647da15499a9fcc7da58afc01c9 Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Thu, 12 Sep 2024 12:37:42 -0600 Subject: [PATCH] GODRIVER-2907 Add bulk decryption benchmarks --- testdata/mongocrypt/command.json | 2 +- testdata/mongocrypt/local-key-document.json | 24 +++ x/mongo/driver/mongocrypt/binary.go | 2 +- x/mongo/driver/mongocrypt/binary_test.go | 191 ++++++++++++++++++++ x/mongo/driver/mongocrypt/mem.prof | Bin 0 -> 2138 bytes 5 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 testdata/mongocrypt/local-key-document.json create mode 100644 x/mongo/driver/mongocrypt/binary_test.go create mode 100644 x/mongo/driver/mongocrypt/mem.prof diff --git a/testdata/mongocrypt/command.json b/testdata/mongocrypt/command.json index d04bf7799a..0fa3fee556 100644 --- a/testdata/mongocrypt/command.json +++ b/testdata/mongocrypt/command.json @@ -3,4 +3,4 @@ "filter": { "ssn": "457-55-5462" } -} \ No newline at end of file +} diff --git a/testdata/mongocrypt/local-key-document.json b/testdata/mongocrypt/local-key-document.json new file mode 100644 index 0000000000..b84b05c25f --- /dev/null +++ b/testdata/mongocrypt/local-key-document.json @@ -0,0 +1,24 @@ +{ + "_id": { + "$binary": { + "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "ACR7Hm33dDOAAD7l2ubZhSpSUWK8BkALUY+qW3UgBAEcTV8sBwZnaAWnzDsmrX55dgmYHWfynDlJogC/e33u6pbhyXvFTs5ow9OLCuCWBJ39T/Ivm3kMaZJybkejY0V+uc4UEdHvVVz/SbitVnzs2WXdMGmo1/HmDRrxGYZjewFslquv8wtUHF5pyB+QDlQBd/al9M444/8bJZFbMSmtIg==", + "subType": "00" + } + }, + "creationDate": { + "$date": "2023-08-21T14:28:20.875Z" + }, + "updateDate": { + "$date": "2023-08-21T14:28:20.875Z" + }, + "status": 0, + "masterKey": { + "provider": "local" + } +} diff --git a/x/mongo/driver/mongocrypt/binary.go b/x/mongo/driver/mongocrypt/binary.go index 4e4b51d74b..7abc976457 100644 --- a/x/mongo/driver/mongocrypt/binary.go +++ b/x/mongo/driver/mongocrypt/binary.go @@ -11,6 +11,7 @@ package mongocrypt /* #include +#include // Include for memcpy #include */ import "C" @@ -31,7 +32,6 @@ func newBinary() *binary { } } -// newBinaryFromBytes creates a binary instance from a byte buffer. func newBinaryFromBytes(data []byte) *binary { if len(data) == 0 { return newBinary() diff --git a/x/mongo/driver/mongocrypt/binary_test.go b/x/mongo/driver/mongocrypt/binary_test.go new file mode 100644 index 0000000000..a424e49d1b --- /dev/null +++ b/x/mongo/driver/mongocrypt/binary_test.go @@ -0,0 +1,191 @@ +// Copyright (C) MongoDB, Inc. 2017-present. +// +// 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 + +//go:build cse +// +build cse + +package mongocrypt + +import ( + "fmt" + "io/ioutil" + "path" + "runtime" + "testing" + "time" + + "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/require" + "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options" +) + +func resourceToDocumentB(b *testing.B, filename string) bsoncore.Document { + b.Helper() + + content, err := ioutil.ReadFile(path.Join(resourcesDir, filename)) + require.NoError(b, err) + + var doc bsoncore.Document + + err = bson.UnmarshalExtJSON(content, false, &doc) + require.NoError(b, err) + + return doc +} + +func addMongoKeysForBenchmark(b *testing.B, encryptCtx *Context) { + b.Helper() + + if encryptCtx.State() != NeedMongoKeys { + return + } + + _, err := encryptCtx.NextOperation() + require.NoError(b, err) + + // feed result and finish op + err = encryptCtx.AddOperationResult(resourceToDocumentB(b, "local-key-document.json")) + require.NoError(b, err) + + err = encryptCtx.CompleteOperation() + require.NoError(b, err) +} + +// Encrypt 1500 string values of the form "value ". +func encryptBenchmarkDoc(b *testing.B, crypt *MongoCrypt, iter int) bsoncore.Document { + b.Helper() + + const algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + const numOfKeys = 1500 + + // create explicit encryption context and check initial state + keyID := bson.Binary{ + Subtype: 0x04, // 0x04 is UUID subtype + Data: []byte("aaaaaaaaaaaaaaaa"), + } + + encryptOpts := options.ExplicitEncryption().SetAlgorithm(algorithm).SetKeyID(keyID) + doc := bsoncore.NewDocumentBuilder().AppendString("v", fmt.Sprintf("value %v", iter)).Build() + + encryptCtx, err := crypt.CreateExplicitEncryptionContext(doc, encryptOpts) + require.NoError(b, err) + + defer encryptCtx.Close() + + addMongoKeysForBenchmark(b, encryptCtx) + + // perform final encryption + encryptedDoc, err := encryptCtx.Finish() + require.NoError(b, err) + + return encryptedDoc +} + +func decryptBenchmarkDoc(b *testing.B, crypt *MongoCrypt, encryptedDoc bsoncore.Document) bsoncore.Document { + b.Helper() + + // create explicit decryption context and check initial state + decryptCtx, err := crypt.CreateDecryptionContext(encryptedDoc) + require.NoError(b, err) + + defer decryptCtx.Close() + + // perform final decryption + decryptedDoc, err := decryptCtx.Finish() + require.NoError(b, err) + + return decryptedDoc +} + +// create a document of the form: +// { "key1": , "key2": , ... } +func makeFullEncryptBenchmarkDocs(b *testing.B, crypt *MongoCrypt, count int) bsoncore.Document { + bldr := bsoncore.NewDocumentBuilder() + + for i := 0; i < count; i++ { + encDoc := encryptBenchmarkDoc(b, crypt, i) + bldr.AppendValue(fmt.Sprintf("key%v", i), encDoc.Lookup("v")) + } + + return bldr.Build() +} + +// Create a MongoCrypt object for benchmarking bulk decryption. +func newBenchmarkCrypt(b *testing.B) *MongoCrypt { + key := []byte{ + 0x9d, 0x94, 0x4b, 0x0d, 0x93, 0xd0, 0xc5, 0x44, + 0xa5, 0x72, 0xfd, 0x32, 0x1b, 0x94, 0x30, 0x90, + 0x23, 0x35, 0x73, 0x7c, 0xf0, 0xf6, 0xc2, 0xf4, + 0xda, 0x23, 0x56, 0xe7, 0x8f, 0x04, 0xcc, 0xfa, + 0xde, 0x75, 0xb4, 0x51, 0x87, 0xf3, 0x8b, 0x97, + 0xd7, 0x4b, 0x44, 0x3b, 0xac, 0x39, 0xa2, 0xc6, + 0x4d, 0x91, 0x00, 0x3e, 0xd1, 0xfa, 0x4a, 0x30, + 0xc1, 0xd2, 0xc6, 0x5e, 0xfb, 0xac, 0x41, 0xf2, + 0x48, 0x13, 0x3c, 0x9b, 0x50, 0xfc, 0xa7, 0x24, + 0x7a, 0x2e, 0x02, 0x63, 0xa3, 0xc6, 0x16, 0x25, + 0x51, 0x50, 0x78, 0x3e, 0x0f, 0xd8, 0x6e, 0x84, + 0xa6, 0xec, 0x8d, 0x2d, 0x24, 0x47, 0xe5, 0xaf} + + localProvider := bsoncore.NewDocumentBuilder(). + AppendBinary("key", 0, key). + Build() + + kmsProviders := bsoncore.NewDocumentBuilder(). + AppendDocument("local", localProvider). + Build() + + cryptOpts := options.MongoCrypt().SetKmsProviders(kmsProviders) + + crypt, err := NewMongoCrypt(cryptOpts) + + require.NoError(b, err) + assert.NotNil(b, crypt) + + return crypt +} + +func BenchmarkBulkDecryption(b *testing.B) { + crypt := newBenchmarkCrypt(b) + defer crypt.Close() + + // Set up the benchmark data: + encryptedDoc := makeFullEncryptBenchmarkDocs(b, crypt, 2) + + repeatDecrypt := func(b *testing.B, dur time.Duration) { + for start := time.Now(); time.Since(start) < dur; { + decryptBenchmarkDoc(b, crypt, encryptedDoc) + } + } + + // Warm up the benchmark + repeatDecrypt(b, time.Second) + + benchmarks := []struct { + threads int + }{ + {threads: 1}, + {threads: 2}, + {threads: 8}, + {threads: 64}, + } + + for _, bench := range benchmarks { + b.Run(fmt.Sprintf("%v threads", bench.threads), func(b *testing.B) { + runtime.GOMAXPROCS(bench.threads) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + repeatDecrypt(b, time.Second) + } + }) + }) + } +} diff --git a/x/mongo/driver/mongocrypt/mem.prof b/x/mongo/driver/mongocrypt/mem.prof new file mode 100644 index 0000000000000000000000000000000000000000..bdd54147c40a64c96251af101969c619fc2eda1e GIT binary patch literal 2138 zcmV-g2&MNQiwFP!00004|FlNh%Q)5P!64R6-ll zD6N!KN*b!DX(OdorM42)%-Vp`qL_mqomlU)@AE#-`@XX?`&a<^z`w(FNjQM2ArKCr zAo#-pR3oly!CwkWQRKs~{qhG#Knfxsd}A^A1|!G^2qO}rZ!VIe$dAv=o)EYnzE>x3 zKZJ3Zgy=Wxq$sM!R}Y*Jcs0Bg7I-y8aD;^DTVW}R0{GyQKM{BUKK2Pb0Cl*I)YD78 zSUiT7fBjj)f8l`7UmZvUksrGoq&N!VYj3 z!ogroEea(9$d5saqcA>p=4Bx*0*8H40M)^EK@b6qF=?Yu_y)tM4iY#)+Ua%)Pz0Ym z?b)vvkA_h_bl?usPA4Qlb@<4OKNZDkfX9HxHb5utB#m@jilTaa?3`z$7M}NkFV+!@ z{BYl)>KFcce%4o26|N0O>gwm^B_%)&`09_mWR0*$&^1CA?jr59SBj!WeC)Rz;cj8S~$6c2jsR`E9LNw-;IhKfp_Pcd2 zxaT=k%T?XL+GC-2o~!@Eem7WE6-L7UJje^sgrEBHM@RUuHNj?4fF_WzM0)A46h+PW z_ZL1CiksodfaqH@^x;0zPY(yAC~Cn+T@P=8E1u#OxCh@u`sw$%qgH(NtcSP4N7W*} z74F6Nl79MLwG>5heD$D*$Kh!Yj|0V&^wW2FJjUGQC*T>6KLJB{h%Ba$S4)7}@x|jD51@AF67JhU z#xhwAIjsOPQ&6s(ZN^e|`d#7F+|fymwoe+&tCosh&ylA^y0Nm0~= z|MmCx1>ObEc=27JVwEhRM|~2YZv520Ie!3k!zPiv8B>dWYFNswm!RG}k!AiW6+($P^fO_$< zKYM5|ObfIZR^e6TK3bHbs1INK4PRkF)CWKJRQJJZyqercPjc7&`0Vk^!c{+fyI#2N zhx_sU;z8i1(+5#jLX7i1tn)Hwxug`PbvH)?#-u%hi~`fj!V);Tr1nAWV~vZb=Ej^<3cXc{>)n~_Z`r-PvrB`}bZ-AlcvU&2c5&%>BPY|rkpX#I z+1RRCTA{!SbD8h??(h%@OhHXiV!lGo3S?nG&-(^VW)) z>7du10on1$g{LLz#9bc%u5-iP(|Jc+bIWl+UY9rW_N2P8VqFH?Ga}}Don;oi?o{54 zTMW1wof{`_0IjmIS&e`4R!TH3rLO)ID;e7~WIDLZHjQyzGYn=8$OfBsCd}Q;P#?VQ zwsSs(v!!j1j`#Wh7AAsCI~&HgY~HF_HkYAgr!+%n_#@DuBpfgkKQ77ZS{eQ2_Df3Wu~sXt#}c$jcHk4PyYXy=CFx;@s8`s9WhbQ zp@j(2&C-m}qOPf|kGfgPd-;|wG~bx1my67BMoo*U$=i)mmoFqjE0vg$ozLX0_f-oC z)J==chmA4a%rdV$TPiD0mhIHa1t&elXC$2!gqGL})-FVi$nD9n)|Cxi_O8Wkb)+&$ z{6)ZhiDTyQLR8Q}FRe8&mtS35S?W3ee6=sAi)EvzS@xt>;4f{yN|;rt>+4iJ`GQ9E z;rZkbdT+lw*nBKzn`KL96XyJAsb9S93NGk~%y%2^u<~O(XV_S@N1bVP%cl&-EfvWV z%yu5Se$4iEJUOK{a&mq^bGDWFj;8kA`b0MWXwFo3C?zXDr8!KQ(kx}CS;+E_3pJ-K z%d9A-i^<_+I<<1w-o2S!!+T59!!t?#FIQ3~l$<$~Dd!8>)RGm+q`GWna*2{jCQ~vMp>btSxO