@@ -37,6 +37,10 @@ <h1 class="title">Module <code>slack_bolt.adapter.django.handler</code></h1>
37
37
from slack_bolt.error import BoltError
38
38
from slack_bolt.lazy_listener import ThreadLazyListenerRunner
39
39
from slack_bolt.lazy_listener.internals import build_runnable_function
40
+ from slack_bolt.listener.listener_start_handler import (
41
+ ListenerStartHandler,
42
+ DefaultListenerStartHandler,
43
+ )
40
44
from slack_bolt.listener.listener_completion_handler import (
41
45
ListenerCompletionHandler,
42
46
DefaultListenerCompletionHandler,
@@ -82,26 +86,37 @@ <h1 class="title">Module <code>slack_bolt.adapter.django.handler</code></h1>
82
86
return resp
83
87
84
88
85
- from django.db import connections
89
+ from django.db import close_old_connections
86
90
87
91
88
- def release_thread_local_connections(logger: Logger, execution_type : str):
89
- connections.close_all ()
92
+ def release_thread_local_connections(logger: Logger, execution_timing : str):
93
+ close_old_connections ()
90
94
if logger.level <= logging.DEBUG:
91
95
current: Thread = current_thread()
92
96
logger.debug(
93
- f"Released thread-bound DB connections (thread name: {current.name}, execution type: {execution_type})"
97
+ "Released thread-bound old DB connections "
98
+ f"(thread name: {current.name}, execution timing: {execution_timing})"
94
99
)
95
100
96
101
102
+ class DjangoListenerStartHandler(ListenerStartHandler):
103
+ """Django sets DB connections as a thread-local variable per thread.
104
+ If the thread is not managed on the Django app side, the connections won't be released by Django.
105
+ This handler releases the connections every time a ThreadListenerRunner execution completes.
106
+ """
107
+
108
+ def handle(self, request: BoltRequest, response: Optional[BoltResponse]) -> None:
109
+ release_thread_local_connections(request.context.logger, "listener-start")
110
+
111
+
97
112
class DjangoListenerCompletionHandler(ListenerCompletionHandler):
98
113
"""Django sets DB connections as a thread-local variable per thread.
99
114
If the thread is not managed on the Django app side, the connections won't be released by Django.
100
115
This handler releases the connections every time a ThreadListenerRunner execution completes.
101
116
"""
102
117
103
118
def handle(self, request: BoltRequest, response: Optional[BoltResponse]) -> None:
104
- release_thread_local_connections(request.context.logger, "listener")
119
+ release_thread_local_connections(request.context.logger, "listener-completion ")
105
120
106
121
107
122
class DjangoThreadLazyListenerRunner(ThreadLazyListenerRunner):
@@ -113,11 +128,14 @@ <h1 class="title">Module <code>slack_bolt.adapter.django.handler</code></h1>
113
128
)
114
129
115
130
def wrapped_func():
131
+ release_thread_local_connections(
132
+ request.context.logger, "before-lazy-listener"
133
+ )
116
134
try:
117
135
func()
118
136
finally:
119
137
release_thread_local_connections(
120
- request.context.logger, "lazy-listener"
138
+ request.context.logger, "lazy-listener-completion "
121
139
)
122
140
123
141
self.executor.submit(wrapped_func)
@@ -144,18 +162,39 @@ <h1 class="title">Module <code>slack_bolt.adapter.django.handler</code></h1>
144
162
self.app.logger.debug("App.process_before_response is set to True")
145
163
return
146
164
165
+ current_start_handler = listener_runner.listener_start_handler
166
+ if current_start_handler is not None and not isinstance(
167
+ current_start_handler, DefaultListenerStartHandler
168
+ ):
169
+ # As we run release_thread_local_connections() before listener executions,
170
+ # it's okay to skip calling the same connection clean-up method at the listener completion.
171
+ message = """As you've already set app.listener_runner.listener_start_handler to your own one,
172
+ Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerStartHandler.
173
+
174
+ If you go with your own handler here, we highly recommend having the following lines of code
175
+ in your handle() method to clean up unmanaged stale/old database connections:
176
+
177
+ from django.db import close_old_connections
178
+ close_old_connections()
179
+ """
180
+ self.app.logger.info(message)
181
+ else:
182
+ # for proper management of thread-local Django DB connections
183
+ self.app.listener_runner.listener_start_handler = (
184
+ DjangoListenerStartHandler()
185
+ )
186
+ self.app.logger.debug("DjangoListenerStartHandler has been enabled")
187
+
147
188
current_completion_handler = listener_runner.listener_completion_handler
148
189
if current_completion_handler is not None and not isinstance(
149
190
current_completion_handler, DefaultListenerCompletionHandler
150
191
):
192
+ # As we run release_thread_local_connections() before listener executions,
193
+ # it's okay to skip calling the same connection clean-up method at the listener completion.
151
194
message = """As you've already set app.listener_runner.listener_completion_handler to your own one,
152
195
Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerCompletionHandler.
153
- We strongly recommend having the following lines of code in your listener_completion_handler:
154
-
155
- from django.db import connections
156
- connections.close_all()
157
196
"""
158
- self.app.logger.warning (message)
197
+ self.app.logger.info (message)
159
198
return
160
199
# for proper management of thread-local Django DB connections
161
200
self.app.listener_runner.listener_completion_handler = (
@@ -188,20 +227,21 @@ <h1 class="title">Module <code>slack_bolt.adapter.django.handler</code></h1>
188
227
< h2 class ="section-title " id ="header-functions "> Functions</ h2 >
189
228
< dl >
190
229
< dt id ="slack_bolt.adapter.django.handler.release_thread_local_connections "> < code class ="name flex ">
191
- < span > def < span class ="ident "> release_thread_local_connections</ span > </ span > (< span > logger: logging.Logger, execution_type : str)</ span >
230
+ < span > def < span class ="ident "> release_thread_local_connections</ span > </ span > (< span > logger: logging.Logger, execution_timing : str)</ span >
192
231
</ code > </ dt >
193
232
< dd >
194
233
< div class ="desc "> </ div >
195
234
< details class ="source ">
196
235
< summary >
197
236
< span > Expand source code</ span >
198
237
</ summary >
199
- < pre > < code class ="python "> def release_thread_local_connections(logger: Logger, execution_type : str):
200
- connections.close_all ()
238
+ < pre > < code class ="python "> def release_thread_local_connections(logger: Logger, execution_timing : str):
239
+ close_old_connections ()
201
240
if logger.level <= logging.DEBUG:
202
241
current: Thread = current_thread()
203
242
logger.debug(
204
- f"Released thread-bound DB connections (thread name: {current.name}, execution type: {execution_type})"
243
+ "Released thread-bound old DB connections "
244
+ f"(thread name: {current.name}, execution timing: {execution_timing})"
205
245
)</ code > </ pre >
206
246
</ details >
207
247
</ dd >
@@ -281,7 +321,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
281
321
"""
282
322
283
323
def handle(self, request: BoltRequest, response: Optional[BoltResponse]) -> None:
284
- release_thread_local_connections(request.context.logger, "listener")</ code > </ pre >
324
+ release_thread_local_connections(request.context.logger, "listener-completion ")</ code > </ pre >
285
325
</ details >
286
326
< h3 > Ancestors</ h3 >
287
327
< ul class ="hlist ">
@@ -296,6 +336,39 @@ <h3>Inherited members</h3>
296
336
</ li >
297
337
</ ul >
298
338
</ dd >
339
+ < dt id ="slack_bolt.adapter.django.handler.DjangoListenerStartHandler "> < code class ="flex name class ">
340
+ < span > class < span class ="ident "> DjangoListenerStartHandler</ span > </ span >
341
+ </ code > </ dt >
342
+ < dd >
343
+ < div class ="desc "> < p > Django sets DB connections as a thread-local variable per thread.
344
+ If the thread is not managed on the Django app side, the connections won't be released by Django.
345
+ This handler releases the connections every time a ThreadListenerRunner execution completes.</ p > </ div >
346
+ < details class ="source ">
347
+ < summary >
348
+ < span > Expand source code</ span >
349
+ </ summary >
350
+ < pre > < code class ="python "> class DjangoListenerStartHandler(ListenerStartHandler):
351
+ """Django sets DB connections as a thread-local variable per thread.
352
+ If the thread is not managed on the Django app side, the connections won't be released by Django.
353
+ This handler releases the connections every time a ThreadListenerRunner execution completes.
354
+ """
355
+
356
+ def handle(self, request: BoltRequest, response: Optional[BoltResponse]) -> None:
357
+ release_thread_local_connections(request.context.logger, "listener-start")</ code > </ pre >
358
+ </ details >
359
+ < h3 > Ancestors</ h3 >
360
+ < ul class ="hlist ">
361
+ < li > < a title ="slack_bolt.listener.listener_start_handler.ListenerStartHandler " href ="../../listener/listener_start_handler.html#slack_bolt.listener.listener_start_handler.ListenerStartHandler "> ListenerStartHandler</ a > </ li >
362
+ </ ul >
363
+ < h3 > Inherited members</ h3 >
364
+ < ul class ="hlist ">
365
+ < li > < code > < b > < a title ="slack_bolt.listener.listener_start_handler.ListenerStartHandler " href ="../../listener/listener_start_handler.html#slack_bolt.listener.listener_start_handler.ListenerStartHandler "> ListenerStartHandler</ a > </ b > </ code > :
366
+ < ul class ="hlist ">
367
+ < li > < code > < a title ="slack_bolt.listener.listener_start_handler.ListenerStartHandler.handle " href ="../../listener/listener_start_handler.html#slack_bolt.listener.listener_start_handler.ListenerStartHandler.handle "> handle</ a > </ code > </ li >
368
+ </ ul >
369
+ </ li >
370
+ </ ul >
371
+ </ dd >
299
372
< dt id ="slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner "> < code class ="flex name class ">
300
373
< span > class < span class ="ident "> DjangoThreadLazyListenerRunner</ span > </ span >
301
374
< span > (</ span > < span > logger: logging.Logger, executor: concurrent.futures._base.Executor)</ span >
@@ -315,11 +388,14 @@ <h3>Inherited members</h3>
315
388
)
316
389
317
390
def wrapped_func():
391
+ release_thread_local_connections(
392
+ request.context.logger, "before-lazy-listener"
393
+ )
318
394
try:
319
395
func()
320
396
finally:
321
397
release_thread_local_connections(
322
- request.context.logger, "lazy-listener"
398
+ request.context.logger, "lazy-listener-completion "
323
399
)
324
400
325
401
self.executor.submit(wrapped_func)</ code > </ pre >
@@ -377,18 +453,39 @@ <h3>Inherited members</h3>
377
453
self.app.logger.debug("App.process_before_response is set to True")
378
454
return
379
455
456
+ current_start_handler = listener_runner.listener_start_handler
457
+ if current_start_handler is not None and not isinstance(
458
+ current_start_handler, DefaultListenerStartHandler
459
+ ):
460
+ # As we run release_thread_local_connections() before listener executions,
461
+ # it's okay to skip calling the same connection clean-up method at the listener completion.
462
+ message = """As you've already set app.listener_runner.listener_start_handler to your own one,
463
+ Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerStartHandler.
464
+
465
+ If you go with your own handler here, we highly recommend having the following lines of code
466
+ in your handle() method to clean up unmanaged stale/old database connections:
467
+
468
+ from django.db import close_old_connections
469
+ close_old_connections()
470
+ """
471
+ self.app.logger.info(message)
472
+ else:
473
+ # for proper management of thread-local Django DB connections
474
+ self.app.listener_runner.listener_start_handler = (
475
+ DjangoListenerStartHandler()
476
+ )
477
+ self.app.logger.debug("DjangoListenerStartHandler has been enabled")
478
+
380
479
current_completion_handler = listener_runner.listener_completion_handler
381
480
if current_completion_handler is not None and not isinstance(
382
481
current_completion_handler, DefaultListenerCompletionHandler
383
482
):
483
+ # As we run release_thread_local_connections() before listener executions,
484
+ # it's okay to skip calling the same connection clean-up method at the listener completion.
384
485
message = """As you've already set app.listener_runner.listener_completion_handler to your own one,
385
486
Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerCompletionHandler.
386
- We strongly recommend having the following lines of code in your listener_completion_handler:
387
-
388
- from django.db import connections
389
- connections.close_all()
390
487
"""
391
- self.app.logger.warning (message)
488
+ self.app.logger.info (message)
392
489
return
393
490
# for proper management of thread-local Django DB connections
394
491
self.app.listener_runner.listener_completion_handler = (
@@ -469,6 +566,9 @@ <h1>Index</h1>
469
566
< h4 > < code > < a title ="slack_bolt.adapter.django.handler.DjangoListenerCompletionHandler " href ="#slack_bolt.adapter.django.handler.DjangoListenerCompletionHandler "> DjangoListenerCompletionHandler</ a > </ code > </ h4 >
470
567
</ li >
471
568
< li >
569
+ < h4 > < code > < a title ="slack_bolt.adapter.django.handler.DjangoListenerStartHandler " href ="#slack_bolt.adapter.django.handler.DjangoListenerStartHandler "> DjangoListenerStartHandler</ a > </ code > </ h4 >
570
+ </ li >
571
+ < li >
472
572
< h4 > < code > < a title ="slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner " href ="#slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner "> DjangoThreadLazyListenerRunner</ a > </ code > </ h4 >
473
573
< ul class ="">
474
574
< li > < code > < a title ="slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner.logger " href ="#slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner.logger "> logger</ a > </ code > </ li >
0 commit comments