Skip to content

calling eventlib/gevent patching#6322

Closed
avikam wants to merge 11 commits into
celery:masterfrom
avikam:master
Closed

calling eventlib/gevent patching#6322
avikam wants to merge 11 commits into
celery:masterfrom
avikam:master

Conversation

@avikam

@avikam avikam commented Aug 30, 2020

Copy link
Copy Markdown

Description

eventlet gPidbox is not running in 5.0.0rc1, as described here: #6225

Following the move to Click (https://github.com/celery/celery/pull/5718), I noticed that the patching code was commented out.
I'm not sure if the proposed way is a valid way to port the import patching, or the maintainers had other design in mind, but this fix is working for me.

@lgtm-com

lgtm-com Bot commented Aug 30, 2020

Copy link
Copy Markdown

This pull request introduces 1 alert when merging b4c82a7 into 5ab26db - view on LGTM.com

new alerts:

  • 1 for Module is imported with 'import' and 'import from'

@lgtm-com

lgtm-com Bot commented Aug 30, 2020

Copy link
Copy Markdown

This pull request introduces 1 alert when merging de1bf7d into 5ab26db - view on LGTM.com

new alerts:

  • 1 for Non-exception in 'except' clause

@lgtm-com

lgtm-com Bot commented Aug 31, 2020

Copy link
Copy Markdown

This pull request introduces 1 alert and fixes 1 when merging 32ee9f3 into 5ab26db - view on LGTM.com

new alerts:

  • 1 for Non-exception in 'except' clause

fixed alerts:

  • 1 for Non-exception in 'except' clause

@avikam
avikam marked this pull request as ready for review August 31, 2020 01:45
@lgtm-com

lgtm-com Bot commented Aug 31, 2020

Copy link
Copy Markdown

This pull request fixes 1 alert when merging dc8d20b into 5ab26db - view on LGTM.com

fixed alerts:

  • 1 for Non-exception in 'except' clause

@auvipy
auvipy requested a review from thedrow August 31, 2020 07:00
@auvipy auvipy added this to the 5.0.0 milestone Aug 31, 2020
@avikam
avikam marked this pull request as draft August 31, 2020 13:37
@avikam

avikam commented Aug 31, 2020

Copy link
Copy Markdown
Author

looks like it's too late, i got a max-recursion error trying to open an ssl connection.
drafting for now

@lgtm-com

lgtm-com Bot commented Aug 31, 2020

Copy link
Copy Markdown

This pull request introduces 1 alert and fixes 1 when merging af5d81c into 5fb1393 - view on LGTM.com

new alerts:

  • 1 for Non-exception in 'except' clause

fixed alerts:

  • 1 for Non-exception in 'except' clause

@auvipy

auvipy commented Aug 31, 2020

Copy link
Copy Markdown
Member

seems lazy app passing

@avikam

avikam commented Aug 31, 2020

Copy link
Copy Markdown
Author

seems lazy app passing

yes, now i'm thinking how can I port the user_options feature, since it requires instantiating the app on celery command which might be too early. (https://github.com/celery/celery/blob/master/celery/bin/celery.py#L110)
any idea how can I read this configuration without importing the entire app?

@lgtm-com

lgtm-com Bot commented Aug 31, 2020

Copy link
Copy Markdown

This pull request introduces 2 alerts and fixes 1 when merging 09403a7 into 5806976 - view on LGTM.com

new alerts:

  • 1 for Non-exception in 'except' clause
  • 1 for Module is imported with 'import' and 'import from'

fixed alerts:

  • 1 for Non-exception in 'except' clause

@lgtm-com

lgtm-com Bot commented Aug 31, 2020

Copy link
Copy Markdown

This pull request fixes 1 alert when merging cd48206 into 5806976 - view on LGTM.com

fixed alerts:

  • 1 for Non-exception in 'except' clause

@auvipy
auvipy self-requested a review September 1, 2020 01:50
@avikam

avikam commented Sep 1, 2020

Copy link
Copy Markdown
Author

This is still a draft, @auvipy, @thedrow - i'm happy to hear your feedback.

The idea of this change is to instantiate the app lazily so that eventlet/gevent will patch the standard library before the app is imported. To achieve that I used a cached property on the click-context.
However inside the main celery command (before bin.worker is evaluated), there is a reference to the app, which adds additional user options to worker+beat+event commands (side note - the documentation mentions argparse which I assume should be changed to Click?)
So to circumvent this reference, I manually parse the residual args that complies with user options on the worker command.
There is more work left to do, for example to project these user options on the help page, but I think it's achievable.
I was able to run a celery worker with this app:

from celery import Celery
from celery.bin.base import CeleryOption

app = Celery('tasks', backend='redis://localhost', broker='redis://localhost')

app.user_options['worker'].add(CeleryOption(('-z', '--hello'), help_group="User Options"))
app.user_options['worker'].add(CeleryOption(('--flag', ), is_flag=True, help_group="User Options"))

and with this commandline:
celery -A myapp worker -P eventlet -c 10 broker_url=redis:// --hello world --flag

@thedrow

thedrow commented Sep 1, 2020

Copy link
Copy Markdown
Contributor

Oh I completely forgot about gevent/eventlet patching.
It's good that you noticed.

The documentation wasn't updated yet. I'll read the entire issue first before I respond more thoroughly.

@thedrow thedrow mentioned this pull request Sep 1, 2020
23 tasks

@thedrow thedrow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some issues here are minor, some are major.

This is a release blocker for 5.0.0 GA so we need to urgently resolve this.

Comment thread celery/bin/base.py Outdated
Comment thread celery/bin/base.py Outdated
@property
@functools.lru_cache(None)
def app(self):
return self._app() or get_current_app()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please provide a docstring which explains what happens here.

Comment thread celery/bin/celery.py
worker.params.extend(ctx.obj.app.user_options.get('worker', []))
beat.params.extend(ctx.obj.app.user_options.get('beat', []))
events.params.extend(ctx.obj.app.user_options.get('events', []))
# worker.params.extend(ctx.obj.app.user_options.get('worker', []))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We need this to support preload options.

@avikam avikam Sep 1, 2020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

a compensation for that is done here:
https://github.com/celery/celery/pull/6322/files#diff-a6e64fe5a7c24a0e587e8c92e83b87b5R321

(of course, it is only a draft as it needs to be applied on beat and events as well. i wanted to get an approval for this idea before pursuing it)

there is a downside to this approach, but i'm not sure how else to get the user options only after the patching

Comment thread celery/bin/worker.py Outdated
Comment thread celery/bin/worker.py
def convert(self, value, param, ctx):
# Pools like eventlet/gevent needs to patch libs as early
# as possible.
maybe_patch_concurrency(value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm pretty sure that's the wrong hook for this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm open for suggestions.
My reasoning was to make it as close as possible to -P evaluation which is the only indication that patching is needed.

A possibility is to move this call to the command function itself, but then we can't reference the app in any of the click.options (bc it will cause the app to be imported and the patching to be too late)

Comment thread celery/bin/worker.py
'--statedb',
cls=CeleryOption,
type=click.Path(),
callback=lambda ctx, _, value: value or ctx.obj.app.conf.worker_state_db,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This cannot be removed. The default value must be derived from the app if it is not supplied.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's not removed, only moved below, so the app will not be loaded too soon. See the call to worker.start in this function

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's address the other issues first and see if we can find the way to write this code as Click intended it to be written.

Comment thread celery/bin/worker.py
Comment thread celery/bin/worker.py
@lgtm-com

lgtm-com Bot commented Sep 1, 2020

Copy link
Copy Markdown

This pull request introduces 1 alert and fixes 1 when merging d2def45 into 6248d87 - view on LGTM.com

new alerts:

  • 1 for Unused import

fixed alerts:

  • 1 for Module is imported with 'import' and 'import from'

@lgtm-com

lgtm-com Bot commented Sep 1, 2020

Copy link
Copy Markdown

This pull request fixes 1 alert when merging 7f650c3 into 6248d87 - view on LGTM.com

fixed alerts:

  • 1 for Module is imported with 'import' and 'import from'

@thedrow

thedrow commented Sep 1, 2020

Copy link
Copy Markdown
Contributor
diff --git a/celery/__init__.py b/celery/__init__.py
index 03a69a6b8..f2e406a9a 100644
--- a/celery/__init__.py
+++ b/celery/__init__.py
@@ -112,12 +112,6 @@ def _patch_gevent():
     import gevent.signal
 
     gevent.monkey.patch_all()
-    if gevent.version_info[0] == 0:  # pragma: no cover
-        # Signals aren't working in gevent versions <1.0,
-        # and aren't monkey patched by patch_all()
-        import signal
-
-        signal.signal = gevent.signal
 
 
 def maybe_patch_concurrency(argv=None, short_opts=None,
diff --git a/celery/__main__.py b/celery/__main__.py
index b0557b185..e865ea4bd 100644
--- a/celery/__main__.py
+++ b/celery/__main__.py
@@ -2,15 +2,15 @@
 
 import sys
 
-# from . import maybe_patch_concurrency
+from . import maybe_patch_concurrency
 
 __all__ = ('main',)
 
 
 def main():
     """Entrypoint to the ``celery`` umbrella command."""
-    # if 'multi' not in sys.argv:
-    #     maybe_patch_concurrency()
+    if 'multi' not in sys.argv:
+        maybe_patch_concurrency()
     from celery.bin.celery import main as _main
     sys.exit(_main())

Does this patch also work? Can you try to git apply this on master?
It is much simpler and reuses the old code.

@avikam

avikam commented Sep 1, 2020

Copy link
Copy Markdown
Author

yep, i thought the idea was to leave all argv responsibility to click, but this code works.
closing.

@avikam avikam closed this Sep 1, 2020
@thedrow

thedrow commented Sep 1, 2020

Copy link
Copy Markdown
Contributor

yep, i thought the idea was to leave all argv responsibility to click, but this code works.
closing.

Thank you for bringing this to our attention. 😺
Support for gevent and eventlet is going away in 6.0 anyway as we migrate to an async event loop which does not support them.
I'd rather not touch whatever works.

I'm going to release a new RC in a few moments.
Please try it out :)

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