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 concurrent futures to reduce blocking IO #37

Merged
merged 7 commits into from
Aug 20, 2018

Conversation

codingjoe
Copy link
Collaborator

Especially the queue inspection can be currently very slow,
because a transport to the message broker needs to be opened
and messages are exchanged. Especially in multi queue setup
these operations should happen in a concurrent manner.

Especially the queue inspection can be currently very slow,
because a transport to the message broker needs to be opened
and messages are exchanged. Especially in multi queue setup
these operations should happen in a concurrent manner.
@codingjoe
Copy link
Collaborator Author

Hi @ryanhiebert I hope you like it. It should give this beauty a couple extra horse powers. It currently takes use 3200ms to process this view. I will give you an update on how the performance improves with concurrent IO.

@codingjoe
Copy link
Collaborator Author

Sorry for all the commits, I was so lazy I coded it in a browser :P
Anyhow, I tested it. Works, and is faster.
Best! Joe

@codingjoe
Copy link
Collaborator Author

/ping @ryanhiebert

Copy link
Owner

@ryanhiebert ryanhiebert left a comment

Choose a reason for hiding this comment

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

It looks really good to me, so I'm just thinking through to try and see if there are possible unintended consequences. Thank you for your work on this, and for reminding me about it.

Have you tried this code in your own application yet? I'd love some verification that it's working for somebody before I merge and release it.

install_requires=['six'],
install_requires=[
'six',
'futures; python_version == "2.7"',
Copy link
Owner

Choose a reason for hiding this comment

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

I didn't know about this syntax. This is cool, and helped me find this article: https://hynek.me/articles/conditional-python-dependencies/

with ThreadPoolExecutor() as executor:
# Execute all procs in parallel to avoid blocking IO
# especially celery which needs to open a transport to AMQP.
return list(executor.map(_run, procs.items()))
Copy link
Owner

Choose a reason for hiding this comment

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

Is there any downside to this? Are the app and connection used by Celery thread-safe? We use the same app that celery does to get the connection, etc. I don't want to end up flooding the RabbitMQ server with connections, and I'm just trying to think of other possible downsides. I'm always slow to use threads, because I don't use them very often and I don't have a lot of practice with thinking through possible issues with them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That is a good point. I recently had an issue with connection stacking regarding the DB even if the DB was not used, see revsys/django-health-check#182

I checked both amqp and librabbitmq which uses rabbitmq-c which seem to be thread safe.

The DB issue only happens in configuration edge cases, but I am happy to add the bit that ensures they are closed if you want me to.

Copy link
Owner

Choose a reason for hiding this comment

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

Does that mean that you have not yet used this branch in production? How will the affect other Procs, for queues other than Celery?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The only other Proc we use uses Redis. I can not vouch that this will not have any side effects on other third party packages.
We are currently not using it on production, I will setup a staging environment tho and stress test the endpoint.

Can you assign the PR to me, that way I won't for get. I am going on vacation on Friday. I don't know if I can manage before I leave.

Copy link
Owner

Choose a reason for hiding this comment

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

@codingjoe : Have you been able to test it in a staging or production environment?

@ryanhiebert
Copy link
Owner

Perhaps this needs to be a setting, so that if we find that there are bugs with race conditions, etc, it will only affect those who have opted into using this feature. It does seems like a pretty cool thing to do.

@ryanhiebert
Copy link
Owner

I'd really like this to be behind a setting. @codingjoe : are you willing to add such a setting, so that we can be sure that things won't break for existing users, even if there's some weirdness in one of the backend types that isn't compatible with threads?

@codingjoe
Copy link
Collaborator Author

That's a great idea. I'll do that. I will be disabled by default to ensure backwards compatibility.

@ryanhiebert
Copy link
Owner

I have Slack periodically reminding me about this, so it doesn't fall off my radar, so I'm passing that reminder along to you, @codingjoe . Do you have an idea of when you might be able to look at it, so I don't keep bugging you when you know you won't be able to get to it yet?

@codingjoe
Copy link
Collaborator Author

@ryanhiebert I am on DjangoCon now, have plenty time to pick up my pending contributions. I'll work on it right now.

@codingjoe
Copy link
Collaborator Author

@ryanhiebert do you want this to be a Django setting? It's tricky, since it's implemented in the core library. I might need to inherit some parts in the contrib section to ensure it's working in Django properly.

@codingjoe
Copy link
Collaborator Author

@ryanhiebert ok, this should do it. BTW, I say the the custom json encoder is not really needed anywhere since you don't encode datetimes. You might want to consider dropping it.

@codingjoe
Copy link
Collaborator Author

Oh I also took the liberty to use a JsonResponse. Its there since 1.8, I could find an official list of supported versions. But the oldest supported Django version (support by Django itself) is 1.11. So this should be fine.

@codingjoe
Copy link
Collaborator Author

ping @ryanhiebert

@ryanhiebert
Copy link
Owner

I'm sorry that I haven't responded on this. The design looks great. It turns out this is going to break my own usage of the library, because in my $WORK code I use that native_dump_procs function, and that's why I didn't get to it like I should have. Thank you for the ping, I needed it.

It looks really good, despite the hangup that has been slowing me down, and I think it's the right way to go.

@ryanhiebert ryanhiebert merged commit 956b79a into ryanhiebert:master Aug 20, 2018
@codingjoe codingjoe deleted the patch-1 branch August 21, 2018 11:40
codingjoe added a commit that referenced this pull request Oct 10, 2018
- Use concurrent futures to reduce blocking IO (#37)
- Add a test suite (#41)
- Fix RabbitMQ connection `TimeoutError` (#42).
  Acquire and dispose broker connection per request
@codingjoe
Copy link
Collaborator Author

@ryanhiebert little anecdote: This change + disabling worker pool inspection increased performance on our site by 206x from >3s to <15ms ;)

@ryanhiebert
Copy link
Owner

Sweet! The worker inspection is what really kills the time, so that doesn't surprise me in the least. I'm not sure, though, that you necessarily gained much just from this change. Problem is that I don't get a reliable number, that won't shut down running tasks, unless I use the worker inspection, so its kinda a non-starter to do that for my use-cases.

@codingjoe
Copy link
Collaborator Author

Yes, that is right. We run many queues tho. So concurrently did cut it by 10x
Skipping inspection by another 20x. We don't really need the numbers to be perfect. Since we only scale on queue size for slow tasks and high volume. We scale high speed queues based on message rate. We wrote your own Procs for that. I'll port them upstream once it get the time. Message rate can be really helpful if you want ensure a certain msg ack time.

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.

2 participants