Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit abb7765

Browse files
authored
RUBY-3299 Direct access to mongocrypt_binary_t for better decryption performance (#2899)
* add decryption benchmark * Access mongocrypt_binary_t fields directly for better performance Actual performance improvement is marginal for Ruby. * linter appeasement
1 parent a8fa54e commit abb7765

File tree

7 files changed

+150
-10
lines changed

7 files changed

+150
-10
lines changed

‎lib/mongo/crypt/binary.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ def write(data)
108108

109109
# Cannot write a string that's longer than the space currently allocated
110110
# by the mongocrypt_binary_t object
111-
str_p = Binding.mongocrypt_binary_data(ref)
112-
len = Binding.mongocrypt_binary_len(ref)
111+
str_p = Binding.get_binary_data_direct(ref)
112+
len = Binding.get_binary_len_direct(ref)
113113

114114
if len < data.bytesize
115115
raise ArgumentError.new(
116116
"Cannot write #{data.bytesize} bytes of data to a Binary object " +
117-
"that was initialized with #{Binding.mongocrypt_binary_len(@bin)} bytes."
117+
"that was initialized with #{Binding.get_binary_len_direct(@bin)} bytes."
118118
)
119119
end
120120

@@ -127,8 +127,8 @@ def write(data)
127127
#
128128
# @return [ String ] Data stored in the mongocrypt_binary_t as a string
129129
def to_s
130-
str_p = Binding.mongocrypt_binary_data(ref)
131-
len = Binding.mongocrypt_binary_len(ref)
130+
str_p = Binding.get_binary_data_direct(ref)
131+
len = Binding.get_binary_len_direct(ref)
132132
str_p.read_string(len)
133133
end
134134

‎lib/mongo/crypt/binding.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ def self.validate_version(lmc_version)
178178
# @return [ Integer ] The length of the data array.
179179
attach_function :mongocrypt_binary_len, [:pointer], :int
180180

181+
def self.get_binary_data_direct(mongocrypt_binary_t)
182+
mongocrypt_binary_t.get_pointer(0)
183+
end
184+
185+
def self.get_binary_len_direct(mongocrypt_binary_t)
186+
mongocrypt_binary_t.get_uint32(FFI::NativeType::POINTER.size)
187+
end
188+
181189
# @!method self.mongocrypt_binary_destroy(binary)
182190
# @api private
183191
#

‎profile/driver_bench/crypto/decrypt.rb

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# frozen_string_literal: true
2+
3+
require 'mongo'
4+
require_relative '../base'
5+
6+
module Mongo
7+
module DriverBench
8+
module Crypto
9+
# Benchmark for reporting the performance of decrypting a document with
10+
# a large number of encrypted fields.
11+
class Decrypt < Mongo::DriverBench::Base
12+
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
13+
KEY_VAULT_NAMESPACE = 'encryption.__keyVault'
14+
N = 10
15+
16+
def run
17+
doc = build_encrypted_doc
18+
19+
# warm up
20+
run_test(doc, 1)
21+
22+
[ 1, 2, 8, 64 ].each do |thread_count|
23+
run_test_with_thread_count(doc, thread_count)
24+
end
25+
end
26+
27+
private
28+
29+
def run_test_with_thread_count(doc, thread_count)
30+
results = []
31+
32+
N.times do
33+
threads = Array.new(thread_count) do
34+
Thread.new { Thread.current[:ops_sec] = run_test(doc, 1) }
35+
end
36+
37+
results << threads.each(&:join).sum { |t| t[:ops_sec] }
38+
end
39+
40+
median = results.sort[N / 2]
41+
puts "thread_count=#{thread_count}; median ops/sec=#{median}"
42+
end
43+
44+
def build_encrypted_doc
45+
data_key_id = client_encryption.create_data_key('local')
46+
47+
pairs = Array.new(1500) do |i|
48+
n = format('%04d', i + 1)
49+
key = "key#{n}"
50+
value = "value #{n}"
51+
52+
encrypted = client_encryption.encrypt(value,
53+
key_id: data_key_id,
54+
algorithm: ALGORITHM)
55+
56+
[ key, encrypted ]
57+
end
58+
59+
BSON::Document[pairs]
60+
end
61+
62+
def timeout_holder
63+
@timeout_holder ||= Mongo::CsotTimeoutHolder.new
64+
end
65+
66+
def encrypter
67+
@encrypter ||= Crypt::AutoEncrypter.new(
68+
client: new_client,
69+
key_vault_client: key_vault_client,
70+
key_vault_namespace: KEY_VAULT_NAMESPACE,
71+
kms_providers: kms_providers
72+
)
73+
end
74+
75+
def run_test(doc, duration)
76+
finish_at = Mongo::Utils.monotonic_time + duration
77+
count = 0
78+
79+
while Mongo::Utils.monotonic_time < finish_at
80+
encrypter.decrypt(doc, timeout_holder)
81+
count += 1
82+
end
83+
84+
count
85+
end
86+
87+
def key_vault_client
88+
@key_vault_client ||= new_client
89+
end
90+
91+
def kms_providers
92+
@kms_providers ||= { local: { key: SecureRandom.random_bytes(96) } }
93+
end
94+
95+
def client_encryption
96+
@client_encryption ||= Mongo::ClientEncryption.new(
97+
key_vault_client,
98+
key_vault_namespace: KEY_VAULT_NAMESPACE,
99+
kms_providers: kms_providers
100+
)
101+
end
102+
end
103+
end
104+
end
105+
end

‎profile/driver_bench/rake/tasks.rake

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# frozen_string_literal: true
22

3+
$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
4+
35
task driver_bench: %i[ driver_bench:data driver_bench:run ]
46

57
SPECS_REPO_URI = 'git@github.com:mongodb/specifications.git'
68
SPECS_PATH = File.expand_path('../../../specifications', __dir__)
79
DRIVER_BENCH_DATA = File.expand_path('../../data/driver_bench', __dir__)
810

11+
# rubocop:disable Metrics/BlockLength
912
namespace :driver_bench do
1013
desc 'Downloads the DriverBench data files, if necessary'
1114
task :data do
@@ -35,4 +38,12 @@ namespace :driver_bench do
3538

3639
Mongo::DriverBench::Suite.run!
3740
end
41+
42+
desc 'Runs the crypto benchmark'
43+
task :crypto do
44+
require_relative '../crypto/decrypt'
45+
46+
Mongo::DriverBench::Crypto::Decrypt.new.run
47+
end
3848
end
49+
# rubocop:enable Metrics/BlockLength

‎spec/mongo/crypt/binding/binary_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,28 @@
4343
end
4444
end
4545

46+
describe '#get_binary_data_direct' do
47+
let(:binary) { Mongo::Crypt::Binding.mongocrypt_binary_new_from_data(bytes_pointer, bytes.length) }
48+
49+
it 'returns the pointer to the data' do
50+
expect(Mongo::Crypt::Binding.get_binary_data_direct(binary)).to eq(bytes_pointer)
51+
end
52+
end
53+
4654
describe '#mongocrypt_binary_len' do
4755
let(:binary) { Mongo::Crypt::Binding.mongocrypt_binary_new_from_data(bytes_pointer, bytes.length) }
4856

4957
it 'returns the length of the data' do
5058
expect(Mongo::Crypt::Binding.mongocrypt_binary_len(binary)).to eq(bytes.length)
5159
end
5260
end
61+
62+
describe '#get_binary_len_direct' do
63+
let(:binary) { Mongo::Crypt::Binding.mongocrypt_binary_new_from_data(bytes_pointer, bytes.length) }
64+
65+
it 'returns the length of the data' do
66+
expect(Mongo::Crypt::Binding.get_binary_len_direct(binary)).to eq(bytes.length)
67+
end
68+
end
5369
end
5470
end

‎spec/mongo/crypt/binding/context_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@
228228
it 'returns a BSON document' do
229229
expect(result).to be true
230230

231-
data = Mongo::Crypt::Binding.mongocrypt_binary_data(out_binary)
232-
len = Mongo::Crypt::Binding.mongocrypt_binary_len(out_binary)
231+
data = Mongo::Crypt::Binding.get_binary_data_direct(out_binary)
232+
len = Mongo::Crypt::Binding.get_binary_len_direct(out_binary)
233233

234234
response = data.get_array_of_uint8(0, len).pack('C*')
235235
expect(response).to be_a_kind_of(String)

‎spec/mongo/crypt/helpers/mongo_crypt_spec_helper.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ def mongocrypt_binary_t_from(string)
3030
private
3131

3232
def string_from_binary(binary_p)
33-
str_p = Mongo::Crypt::Binding.mongocrypt_binary_data(binary_p)
34-
len = Mongo::Crypt::Binding.mongocrypt_binary_len(binary_p)
33+
str_p = Mongo::Crypt::Binding.get_binary_data_direct(binary_p)
34+
len = Mongo::Crypt::Binding.get_binary_len_direct(binary_p)
3535
str_p.read_string(len)
3636
end
3737
module_function :string_from_binary
3838

3939
def write_to_binary(binary_p, data)
40-
str_p = Mongo::Crypt::Binding.mongocrypt_binary_data(binary_p)
40+
str_p = Mongo::Crypt::Binding.get_binary_data_direct(binary_p)
4141
str_p.put_bytes(0, data)
4242
end
4343
module_function :write_to_binary

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /