Skip to content

Performance: Memoize Encryptor in ActiveRecordEncryptedString::Type#26

Open
Bhacaz wants to merge 2 commits intokamillle:masterfrom
Bhacaz:encryptor_memoization
Open

Performance: Memoize Encryptor in ActiveRecordEncryptedString::Type#26
Bhacaz wants to merge 2 commits intokamillle:masterfrom
Bhacaz:encryptor_memoization

Conversation

@Bhacaz
Copy link

@Bhacaz Bhacaz commented May 7, 2025

Hi, I would like to suggest a small change that has a big impact on performance.

Currently, instantiating the encryptor, especially due to ActiveSupport::KeyGenerator#generate_key, is a CPU-intensive operation. This can lead to significant overhead if an encryptor is initialized repeatedly for the same attribute type.

Rails attribute types, like ActiveRecordEncryptedString::Type, are (if I understand it correctly) typically instantiated once per attribute definition (e.g., attribute :secret_field, :encrypted_string). This PR leverages this by memoizing the encryptor instance variable (e.g., using @encryptor ||= ...).

With the help of the specs I benchmark that change

it 'benchmark' do
  Benchmark.bm do |x|
    x.report('encrypt') do
      100.times do |i|
        dummy_klass.create!(plain: plain, encrypted: "test#{i}")
      end
    end
    x.report('decrypt') do
      dummy_klass.pluck(:encrypted)
    end
  end
end

# Without
#              user     system      total        real
# encrypt  6.315960   0.063629   6.379589 (  6.431360)
# decrypt  3.325287   0.023737   3.349024 (  3.364617)
#
# With memorization
#              user     system      total        real
# encrypt  0.126073   0.020950   0.147023 (  0.147913)
# decrypt  0.005078   0.001927   0.007005 (  0.007034)

This how I found out

Details

it 'flamegraph' do
  100.times do |i|
    dummy_klass.create!(plain: plain, encrypted: "test#{i}")
  end
  flamegraph { dummy_klass.pluck(:encrypted) }
end

image

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant