Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add engine_callback to database backend SessionManager#10135

Open
avolant wants to merge 8 commits intocelery:mainfrom
avolant:avolant/engine-callback
Open

Add engine_callback to database backend SessionManager#10135
avolant wants to merge 8 commits intocelery:mainfrom
avolant:avolant/engine-callback

Conversation

@avolant
Copy link

@avolant avolant commented Feb 19, 2026

Add a new engine_callback option to the database result backend that allows users to customize SQLAlchemy engines after creation. The callback is invoked on every engine creation, including after invalidation.

This enables use cases like JWT-based authentication where a do_connect event listener needs to be registered on each engine to inject fresh credentials before every physical database connection.

Supported via config (database_engine_callback) as a dotted path or callable, and via constructor kwarg (engine_callback).

Note: Before submitting this pull request, please review our contributing
guidelines
.

Description

Add a new `engine_callback` option to the database result backend that
allows users to customize SQLAlchemy engines after creation. The callback
is invoked on every engine creation, including after invalidation.

This enables use cases like JWT-based authentication where a
`do_connect` event listener needs to be registered on each engine to
inject fresh credentials before every physical database connection.

Supported via config (`database_engine_callback`) as a dotted path or
callable, and via constructor kwarg (`engine_callback`).
@jaiganeshs21
Copy link
Contributor

Thanks for the PR @avolant

engine_callback looks fine, but we should validate it before storing it.
If someone sets a truthy non-callable (like int 42), it’ll just crash with a TypeError.

Can we check it in DatabaseBackend.__init__ and raise ImproperlyConfigured if it’s not callable?

@jaiganeshs21
Copy link
Contributor

Since it’s user-facing, it’d be great if we updated the docs in this PR as well.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an engine_callback feature to the database result backend's SessionManager, enabling users to customize SQLAlchemy engines after creation. This is particularly useful for scenarios like JWT-based authentication where event listeners need to be registered on each engine to inject fresh credentials.

Changes:

  • Added engine_callback parameter to SessionManager.__init__() and invocation logic in get_engine() for both forked and non-forked modes
  • Integrated callback configuration in DatabaseBackend.__init__() with support for dotted-path strings and callables from config or constructor kwargs
  • Added database_engine_callback configuration option in app defaults

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
celery/backends/database/session.py Added engine_callback parameter to SessionManager and invocation logic in both forked/non-forked engine creation paths
celery/backends/database/init.py Integrated engine_callback from config or kwargs, with support for dotted-path strings via symbol_by_name
celery/app/defaults.py Added engine_callback configuration option to the database namespace
t/unit/backends/test_database.py Comprehensive test suite covering callback from config/kwargs, dotted paths, fork behavior, invalidation, caching, ordering, and do_connect integration

Copy link
Member

@auvipy auvipy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this pr need to resolve merge conflicts after this is merged b1007a4 . also what existing issue this is going to fix?

@avolant
Copy link
Author

avolant commented Feb 25, 2026

Good point, added a callable() check in DatabaseBackend.__init__ in the latest push — raises ImproperlyConfigured if it’s set but not callable.

@avolant
Copy link
Author

avolant commented Feb 25, 2026

Added docs for database_engine_callback in docs/userguide/configuration.rst in the latest push.

@avolant
Copy link
Author

avolant commented Feb 25, 2026

Merge conflicts resolved in the latest push (the main conflict was around echo_pool/max_overflow kwarg filtering in get_engine).

also what existing issue this is going to fix?

There is no existing issue (But I can create one if you need me to).
I want to have this new capability: Some deployments need to customize the SQLAlchemy engine for per-connection auth (e.g. register a do_connect listener to inject JWT tokens or use IAM auth). There’s currently no hook to do that. Happy to open an issue first if that’s the preferred workflow.

@codecov
Copy link

codecov bot commented Feb 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.79%. Comparing base (2b3c6fa) to head (392f070).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #10135   +/-   ##
=======================================
  Coverage   87.78%   87.79%           
=======================================
  Files         153      153           
  Lines       19432    19444   +12     
  Branches     2233     2237    +4     
=======================================
+ Hits        17058    17070   +12     
  Misses       2076     2076           
  Partials      298      298           
Flag Coverage Δ
unittests 87.77% <100.00%> (+<0.01%) ⬆️

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.

@auvipy
Copy link
Member

auvipy commented Feb 26, 2026

@ChickenBenny hey, would you mind reviewing this PR and let us know if this is needed?

@ChickenBenny
Copy link
Contributor

ChickenBenny commented Feb 26, 2026

This feature is valuable for modern cloud deployments. It solves the pain point of dynamic database authentication (like AWS RDS IAM or HashiCorp Vault short-lived tokens) by allowing us to hook into SQLAlchemy's do_connect event.

LGTM from my side. Minor notes:

  • The ImproperlyConfigured path appears uncovered per the codecov report — could we add a unit test for it?
  • Consider opening an issue to link this PR for traceability

@avolant
Copy link
Author

avolant commented Feb 26, 2026

Added a test for the ImproperlyConfigured path in the latest push.

Also opened #10148 for traceability.

@auvipy auvipy added this to the 5.7.0 milestone Mar 1, 2026
@auvipy auvipy requested a review from Copilot March 1, 2026 04:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

t/unit/backends/test_database.py:784

  • This docstring says the do_connect handler “modifies connection params”, but the test only records cparams without changing them. Consider either updating the handler/assertions to verify an actual modification, or adjusting the docstring to reflect what’s being tested (that the event fires).
        """Integration test: engine_callback registers a do_connect event
        that modifies connection params on every new connection."""
        connect_calls = []

Comment on lines 49 to 52
engine = self._engines[dburi] = create_engine(dburi, **kwargs)
if self.engine_callback:
self.engine_callback(engine)
return engine
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In forked mode, the new engine is inserted into self._engines before the callback runs. If engine_callback(engine) raises, a partially configured engine remains cached and subsequent calls will return it without reapplying the callback. Consider only caching the engine after the callback succeeds, or popping it from the cache on exception.

Also, if self.engine_callback: can skip valid callables with falsy truthiness; checking self.engine_callback is not None would be more robust.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please cross check this?

Comment on lines +59 to +61
engine = create_engine(dburi, poolclass=NullPool, **kwargs)
if self.engine_callback:
self.engine_callback(engine)
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the non-forked code path, if self.engine_callback: can skip valid callables that define falsy truthiness (e.g., callable objects implementing __bool__). Using an explicit is not None check would avoid surprising behavior.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this?

.. setting:: database_engine_callback

``database_engine_callback``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

versionadd:: 5.7 annotation is mandatory for this

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.

5 participants