Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use django.db close_old_connections in place of explicit connections.close #509

Closed
wants to merge 1 commit into from

Conversation

ross
Copy link

@ross ross commented Nov 1, 2021

I was looking to enable the django db CONN_MAX_AGE to avoid the overhead of reconnecting to the database (TLS encrypted) for every event being handled similar to how I have in the past with normal django web requests.

I quickly realized this wasn't working as I continued to see the mysql connection counters on the server rising for each request. I dug into the log message about closing connections and ran across release_thread_local_connections

def release_thread_local_connections(logger: Logger, execution_type: str):

That method appears to explicitly close all connections after each event is handled which makes CONN_MAX_AGE ineffective.

This PR makes a relatively minor change to release_thread_local_connections to instead call the same request_finished handler the signal mentioned in #280 (comment), django.db.close_old_connections which appears to be easy/safe since it ignores all args with **kwargs.

This opens things up to connection reuse via CONN_MAX_AGE and otherwise gets the full django connection cleanup behavior on errors instead of explicitly closing the connection for each event handled.

/cc #280 #281 which introduced the explicit close
/cc @seratch who authored ^ and would likely know if this makes sense

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Document pages under /docs
  • Others

Requirements (place an x in each [ ])

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

  • I've read and understood the Contributing Guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've run ./scripts/install_all_and_run_tests.sh after making the changes.

This opens things up to connection reuse via `CONN_MAX_AGE` and
otherwise gets the full django connection cleanup behavior instead of
explicitly closing the connection for each event handled.
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@ross
Copy link
Author

ross commented Nov 1, 2021

Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.

I don't sign legal documents as part of OSS contributions. CLA aren't necessary and are bad for contributors. Very few true OSS projects require them, I've only really seen them on company sponsored projects like this. They're bad practice, CLA vs DCO.

Feel free to port/redo the trivial changes in this PR yourself as you see fit.

@seratch
Copy link
Member

seratch commented Nov 1, 2021

Hi @ross, thanks a lot for taking the time to share this feedback and a suggestion to resolve the issue!

I've checked close_old_connections and I totally agree that using the method is far better than the current approach. One thing I noticed is that, if we switch to the method, perhaps we may want to call the method before middleware/listener execution as well (or only before the execution). Refer to django/channels#1227 for details. Some Slack apps may not receive incoming requests for a few hours / days. It's not rare. For this reason, doing the same would be necessary for safety.

Feel free to port/redo the trivial changes in this PR yourself as you see fit.

Regarding the CLA, we respect your opinion about it and thank you very much for saying this. We'll work on a new pull request based on your suggestion. If you are happy to use your time a little bit more, your reviews on our forthcoming pull request would be appreciated.

@seratch seratch added this to the 1.9.5 milestone Nov 1, 2021
@seratch seratch self-assigned this Nov 1, 2021
@seratch seratch marked this pull request as draft November 1, 2021 02:47
@ross
Copy link
Author

ross commented Nov 1, 2021

I've checked close_old_connections and I totally agree that using the method is far better than the current approach. One thing I noticed is that, if we switch to the method, perhaps we may want to call the method before middleware/listener execution as well (or only before the execution). Refer to django/channels#1227 for details. Some Slack apps may not receive incoming requests for a few hours / days. It's not rare. For this reason, doing the same would be necessary for safety.

Thanks. I was totally focused on the closing aspect and didn't even look at the start signal enough to see that it was calling the method as well. Definitely makes sense to do it similarly for slack_bolt even handlers.

Regarding the CLA, we respect your opinion about it and thank you very much for saying this. We'll work on a new pull request based on your suggestion. If you are happy to use your time a little bit more, your reviews on our forthcoming pull request would be appreciated.

Wfm. Happy to take a look at and test PRs. Thanks.

@ross
Copy link
Author

ross commented Nov 2, 2021

Not directly related to this PR/issue, but after failing to find any information about using Django from Threads or ThreadPoolExecutors in searches I decided to create a python module django_thread with simple derivatives of those classes that take care of the connection management. If you all wanted you could switch to using django_thread.ThreadPoolExecutor to get the correct pre and post close_old_connections calls wrapped around job execution transparently. I know the places you've hooked in here are part of the wider framework so that may make sense to just continue doing that.

@seratch
Copy link
Member

seratch commented Nov 2, 2021

@ross Thanks for sharing this! Your solution for this Django issue is very clean and looks great to me 👍 If your idea will be included out-of-the-box in the Django framework in the future (=encouraging a Django specific thread executor for threading), it should be so helpful not only for this Slack app use case but also for the entire community.

seratch added a commit to seratch/bolt-python that referenced this pull request Nov 2, 2021
@ross
Copy link
Author

ross commented Nov 2, 2021

Closed in favor of #512

@ross ross closed this Nov 2, 2021
@ross ross deleted the django-close-old-connections branch November 2, 2021 15:11
@ross ross mentioned this pull request Nov 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants