@@ -54,15 +54,16 @@ def to_django_response(bolt_resp: BoltResponse) -> HttpResponse:
5454 return resp
5555
5656
57- from django .db import connections
57+ from django .db import close_old_connections
5858
5959
60- def release_thread_local_connections (logger : Logger , execution_type : str ):
61- connections . close_all ()
60+ def release_thread_local_connections (logger : Logger , execution_timing : str ):
61+ close_old_connections ()
6262 if logger .level <= logging .DEBUG :
6363 current : Thread = current_thread ()
6464 logger .debug (
65- f"Released thread-bound DB connections (thread name: { current .name } , execution type: { execution_type } )"
65+ "Released thread-bound old DB connections "
66+ f"(thread name: { current .name } , execution timing: { execution_timing } )"
6667 )
6768
6869
@@ -73,7 +74,7 @@ class DjangoListenerCompletionHandler(ListenerCompletionHandler):
7374 """
7475
7576 def handle (self , request : BoltRequest , response : Optional [BoltResponse ]) -> None :
76- release_thread_local_connections (request .context .logger , "listener" )
77+ release_thread_local_connections (request .context .logger , "listener-completion " )
7778
7879
7980class DjangoThreadLazyListenerRunner (ThreadLazyListenerRunner ):
@@ -89,7 +90,7 @@ def wrapped_func():
8990 func ()
9091 finally :
9192 release_thread_local_connections (
92- request .context .logger , "lazy-listener"
93+ request .context .logger , "lazy-listener-completion "
9394 )
9495
9596 self .executor .submit (wrapped_func )
@@ -120,14 +121,12 @@ def __init__(self, app: App): # type: ignore
120121 if current_completion_handler is not None and not isinstance (
121122 current_completion_handler , DefaultListenerCompletionHandler
122123 ):
124+ # As we run release_thread_local_connections() before listener executions,
125+ # it's okay to skip calling the same connection clean-up method at the listener completion.
123126 message = """As you've already set app.listener_runner.listener_completion_handler to your own one,
124127 Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerCompletionHandler.
125- We strongly recommend having the following lines of code in your listener_completion_handler:
126-
127- from django.db import connections
128- connections.close_all()
129128 """
130- self .app .logger .warning (message )
129+ self .app .logger .info (message )
131130 return
132131 # for proper management of thread-local Django DB connections
133132 self .app .listener_runner .listener_completion_handler = (
@@ -146,6 +145,13 @@ def handle(self, req: HttpRequest) -> HttpResponse:
146145 bolt_resp = oauth_flow .handle_callback (to_bolt_request (req ))
147146 return to_django_response (bolt_resp )
148147 elif req .method == "POST" :
148+ # As bolt-python utilizes threads for async `ack()` method execution,
149+ # we have to manually clean old/stale Django ORM connections bound to the "unmanaged" threads
150+ # Refer to https://github.com/slackapi/bolt-python/issues/280 for more details.
151+ release_thread_local_connections (
152+ self .app .logger , "before-listener-invocation"
153+ )
154+ # And then, run the App listener/lazy listener here
149155 bolt_resp : BoltResponse = self .app .dispatch (to_bolt_request (req ))
150156 return to_django_response (bolt_resp )
151157
0 commit comments