Skip to content

perf(crypto): offload Argon2/Fernet to threadpool via asyncio.to_thread#2157

Merged
crivetimihai merged 4 commits intoIBM:mainfrom
ESnark:perf/1836-offload-crypto-threadpool
Jan 25, 2026
Merged

perf(crypto): offload Argon2/Fernet to threadpool via asyncio.to_thread#2157
crivetimihai merged 4 commits intoIBM:mainfrom
ESnark:perf/1836-offload-crypto-threadpool

Conversation

@ESnark
Copy link
Copy Markdown
Contributor

@ESnark ESnark commented Jan 18, 2026

✨ Feature / Enhancement PR

🔗 Epic / Issue

Closes #1836


🚀 Summary (1-2 sentences)

Add async wrappers (hash_password_async, verify_password_async, encrypt_secret_async, decrypt_secret_async) and update all call sites to use them, preventing event loop blocking during CPU-intensive crypto operations.


🧪 Checks

  • make lint passes
  • make test passes
  • CHANGELOG updated (if user-facing)

📓 Notes (optional)

@ESnark
Copy link
Copy Markdown
Contributor Author

ESnark commented Jan 18, 2026

@crivetimihai Should I remove the sync methods from the service classes? They're no longer used in production code—only in some unit tests. If yes, I'll update those test files as well.

@crivetimihai crivetimihai added this to the Release 1.0.0-RC1 milestone Jan 20, 2026
@crivetimihai crivetimihai self-assigned this Jan 25, 2026
ESnark and others added 4 commits January 25, 2026 15:21
Add async wrappers (hash_password_async, verify_password_async,
encrypt_secret_async, decrypt_secret_async) and update all call sites
to use them, preventing event loop blocking during CPU-intensive
crypto operations.

Closes IBM#1836

Signed-off-by: ESnark <31977180+ESnark@users.noreply.github.com>
Update test mocks to use async versions of password service and
encryption service methods (hash_password_async, verify_password_async,
encrypt_secret_async) following the changes in the crypto offload PR.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
The crypto offload PR made SSOService.create_provider() and
update_provider() async, but forgot to update call sites:

- mcpgateway/routers/sso.py: add await in admin endpoints
- mcpgateway/utils/sso_bootstrap.py: convert to async, add awaits
- mcpgateway/main.py: make attempt_to_bootstrap_sso_providers async

Without this fix, the router endpoints would return coroutine objects
instead of provider objects, causing runtime errors (500) when
accessing provider.id. The bootstrap would silently skip provider
creation with "coroutine was never awaited" warnings.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Add test coverage for the async crypto operations introduced by the
crypto offload PR:

- test_async_crypto_wrappers.py: Tests for hash_password_async,
  verify_password_async, encrypt_secret_async, decrypt_secret_async
  including roundtrip verification and sync/async compatibility

- test_sso_bootstrap.py: Tests for async SSO bootstrap ensuring
  create_provider and update_provider are properly awaited

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai crivetimihai force-pushed the perf/1836-offload-crypto-threadpool branch from 3815c5e to cbb101d Compare January 25, 2026 16:36
@crivetimihai
Copy link
Copy Markdown
Member

Thanks for the contribution @ESnark! 🎉

I've reviewed, rebased, and tested this PR. The core design is sound and follows established patterns in the codebase (we have 86+ existing uses of asyncio.to_thread with similar wrapper approaches).

I made the following additions/fixes during review:

Bug Fixes

  1. Missing await in SSO routes (mcpgateway/routers/sso.py)

    • The PR made SSOService.create_provider() and update_provider() async, but forgot to add await at the call sites in the admin endpoints
    • Without this fix, the endpoints would return coroutine objects instead of providers, causing 500 errors
  2. Missing await in SSO bootstrap (mcpgateway/utils/sso_bootstrap.py, mcpgateway/main.py)

    • bootstrap_sso_providers() called the now-async methods without awaiting
    • Converted to async and properly awaited in the lifespan startup
  3. Test mock updates (tests/unit/mcpgateway/services/test_email_auth_basic.py, tests/unit/mcpgateway/test_admin.py, tests/unit/mcpgateway/routers/test_email_auth_router.py)

    • Updated test mocks to use AsyncMock for the new async methods

New Test Coverage

  1. Async crypto wrapper tests (tests/unit/mcpgateway/services/test_async_crypto_wrappers.py)

    • 13 tests covering hash_password_async, verify_password_async, encrypt_secret_async, decrypt_secret_async
    • Includes roundtrip verification and sync/async compatibility tests
  2. SSO bootstrap tests (tests/unit/mcpgateway/utils/test_sso_bootstrap.py)

    • 6 tests ensuring create_provider and update_provider are properly awaited

All 5,387 tests pass. Ready for final review and merge!

@crivetimihai crivetimihai merged commit 98b31d3 into IBM:main Jan 25, 2026
52 checks passed
kcostell06 pushed a commit to kcostell06/mcp-context-forge that referenced this pull request Feb 24, 2026
…ad (IBM#2157)

* perf(crypto): offload Argon2/Fernet to threadpool via asyncio.to_thread

Add async wrappers (hash_password_async, verify_password_async,
encrypt_secret_async, decrypt_secret_async) and update all call sites
to use them, preventing event loop blocking during CPU-intensive
crypto operations.

Closes IBM#1836

Signed-off-by: ESnark <31977180+ESnark@users.noreply.github.com>

* fix(tests): update tests for async crypto operations

Update test mocks to use async versions of password service and
encryption service methods (hash_password_async, verify_password_async,
encrypt_secret_async) following the changes in the crypto offload PR.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix(sso): add missing await for async create/update provider methods

The crypto offload PR made SSOService.create_provider() and
update_provider() async, but forgot to update call sites:

- mcpgateway/routers/sso.py: add await in admin endpoints
- mcpgateway/utils/sso_bootstrap.py: convert to async, add awaits
- mcpgateway/main.py: make attempt_to_bootstrap_sso_providers async

Without this fix, the router endpoints would return coroutine objects
instead of provider objects, causing runtime errors (500) when
accessing provider.id. The bootstrap would silently skip provider
creation with "coroutine was never awaited" warnings.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* test(crypto): add tests for async crypto wrappers and SSO bootstrap

Add test coverage for the async crypto operations introduced by the
crypto offload PR:

- test_async_crypto_wrappers.py: Tests for hash_password_async,
  verify_password_async, encrypt_secret_async, decrypt_secret_async
  including roundtrip verification and sync/async compatibility

- test_sso_bootstrap.py: Tests for async SSO bootstrap ensuring
  create_provider and update_provider are properly awaited

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: ESnark <31977180+ESnark@users.noreply.github.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
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.

[PERFORMANCE]: Offload CPU-bound crypto (Argon2/Fernet) to threadpool

2 participants