Skip to content

Perf | Reduce allocations in Always Encrypted key handling, reorganise#4256

Open
edwardneal wants to merge 24 commits intodotnet:mainfrom
edwardneal:perf/ae-reorg/keys
Open

Perf | Reduce allocations in Always Encrypted key handling, reorganise#4256
edwardneal wants to merge 24 commits intodotnet:mainfrom
edwardneal:perf/ae-reorg/keys

Conversation

@edwardneal
Copy link
Copy Markdown
Contributor

Description

This reduces the number of memory allocations performed by Always Encrypted functionality, focusing primarily upon the encryption keys.

It also introduces nullability annotations to the touched files, makes some minor documentation improvements, removes the redundant SqlClient or Sql prefixes of internal types and moves them into the AlwaysEncrypted namespace. These don't result in any public API changes or any changes to the calling contract for SqlColumnEncryptionKeyStoreProvider.

The PR's diff appears larger than it truly is; it may be easier to review commit-by-commit.

In sequence:

  • An encryption provider's ColumnEncryptionKeyCacheTtl was being set to a new TimeSpan instance rather than TimeSpan.Zero.
  • Parallel retrievals of encryption keys from their cache were serialized, even though the underlying cache was thread-safe (it's backed by a ConcurrentDictionary.) I've added a fast-path cache lookup before it takes the lock, which reduces contention slightly once the cache has warmed up.
    • The cache also calls SqlColumnEncryptionKeyStoreProvider.DecryptColumnEncryptionKey. We retain the lock in order to preserve the guarantee that this method call will always be serialized.
  • The encryption key used by SqlAeadAes256CbcHmac256Algorithm was being passed the constant algorithm name in the constructor. These two components are tightly coupled, so I removed the redundant parameter name. This had knock-on effects - it meant that the string.Format could be replaced with a constant string, and that the Unicode encoding of this could be cached.
  • Three SymmetricKey allocations were used to store the IV, MAC and cryptographic keys. These acted as a lightweight wrapper, providing no defensive copies or immutability guarantees. We now just store the byte arrays directly.
  • SqlSecurityUtility.GetHMACWithSHA256 used the netfx cryptographic methods, then allocated and copied. On netcore, we can use the oneshots and eliminate all allocations (and in some circumstances, the copy.)

The microbenchmark highlights the impact when instantiating a AeadAes256CbcHmac256EncryptionKey under cold cache conditions: a measurable reduction in GC pressure and a modest throughput improvement.

Method Mean Error StdDev Ratio RatioSD Completed Work Items Lock Contentions Gen0 Allocated Alloc Ratio
PR 1.299 μs 0.0161 μs 0.0151 μs 0.60 0.01 - - 0.0248 216 B 0.07
main 2.170 μs 0.0405 μs 0.0398 μs 1.00 0.03 - - 0.3738 3136 B 1.00

Security considerations

This makes changes which are very close to the core SqlAeadAes256CbcHmac256Algorithm implementation. I've deliberately scoped this PR to exclude any changes to this. While I think there are other performance improvements to be made in that area, they don't stand alone in the same way that these do and need their own discussion/review.

The CEK derivation logic remains unchanged, as does the key validation logic and key usage ordering.

Issues

None.

Testing

Existing automated tests continue to pass. This doesn't introduce any new functionality or make any behavioural changes, so I've not added any new tests.

@edwardneal edwardneal requested a review from a team as a code owner May 3, 2026 01:40
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board May 3, 2026
@apoorvdeshmukh
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 39.82301% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.30%. Comparing base (7a01dbe) to head (c1e0699).
⚠️ Report is 14 commits behind head on main.

Files with missing lines Patch % Lines
...ata/SqlClient/AlwaysEncrypted/SymmetricKeyCache.cs 0.00% 54 Missing ⚠️
...waysEncrypted/AeadAes256CbcHmac256EncryptionKey.cs 82.60% 4 Missing ⚠️
...nt/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs 0.00% 3 Missing ⚠️
...src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs 82.35% 3 Missing ⚠️
...ent/AlwaysEncrypted/AeadAes256CbcHmac256Factory.cs 50.00% 2 Missing ⚠️
.../AlwaysEncrypted/EncryptionAlgorithmFactoryList.cs 0.00% 1 Missing ⚠️
.../Microsoft/Data/SqlClient/SqlCommand.Encryption.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4256      +/-   ##
==========================================
- Coverage   66.02%   64.30%   -1.72%     
==========================================
  Files         277      272       -5     
  Lines       42988    65786   +22798     
==========================================
+ Hits        28382    42304   +13922     
- Misses      14606    23482    +8876     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 64.30% <39.82%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cheenamalhotra cheenamalhotra added the Performance 📈 Issues that are targeted to performance improvements. label May 7, 2026
@cheenamalhotra cheenamalhotra added this to the 7.1.0-preview3 milestone May 7, 2026
@cheenamalhotra cheenamalhotra moved this from To triage to In review in SqlClient Board May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Performance 📈 Issues that are targeted to performance improvements.

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

4 participants