Looking forward to support for Swoole coroutines
clemask opened this issue · 37 comments
We really look forward to Octane supporting Swoole coroutines, which will bring higher performance and solve more business scenarios. We hope the official will include this in the roadmap as soon as possible.
Feel free to work on it! 👍
Octane is a great project that is pushing Laravel in a new direction in the realm of the internet, making Laravel more powerful. However, there seem to be some issues with the way it's being handled in certain aspects. Octane's support for Swoole has excited many developers, including myself, envisioning a strong combination of Laravel's ecosystem with Swoole's coroutine advantages.
One crucial point that is not clearly stated in the official documentation is the inability to use Swoole's coroutine. This omission is significant because developers might unknowingly configure coroutine parameters, expecting Octane to work seamlessly with Swoole's coroutine features. However, Octane does not disable the creation of Swoole services with coroutine parameters, leading to subtle issues in the production environment. Problems like network connections being used by other coroutines and issues with Monolog log cycling detection can arise. In the end, developers are forced to disable coroutines as a quick solution, which is a severe incident.
I hope the official team promptly considers adding coroutine support to their roadmap. It's a fantastic feature that not only enhances Laravel but is also crucial for the broader PHP ecosystem.
I hope the official team promptly considers adding coroutine support to their roadmap.
This isn't a trivial thing to do. Right now we don't have the time to work on this ourselves but we'd love to see PR's kickstarting this.
Hey @driesvints, are you familiar with @themsaid's talk from Laracon 2021 on how Swoole coroutines could be integrated into Octane? This is the timestamp: https://youtu.be/-l8o4KMaS4c?feature=shared&t=1792
That change has already been merged into Swoole core: swoole/swoole-src#4330
I'm not sure how difficult it would be to implement, but it'd increase Laravel's performance by a whole order of magnitude.
Welcoming prs 👍
If you want octane to support coroutine, you must first make database, redis, etc. support connection pooling.
I guess the problem is Laravel supports a lot of DB and cache backends and it’d be hard to implement pooling for them all…
@taylorotwell @driesvints @nunomaduro Would the core team be interested in having pooling for a select few drivers? Maybe just MySQL and Redis to start. It’d mean coroutines could only be enabled when using those drivers, but Hyperf can handle 100k req/second. Even if coroutines ‘only’ bumped Laravel’s concurrency to 10k-20k, that’d be nuts and create some serious buzz. That’s faster than Node…
FYI @huangdijia is a key Hyperf contributor: https://github.com/hyperf/hyperf/commits/master. He might be willing to help if you guys want to move ahead.
@binaryfire I review all PRs - merging just depends on added complexity / maintenance burden. 👍
It would be amazing feature for highload apps!
Can this be related? 👀
No that’s not related. Running a few tasks concurrently is different to workers that can properly handle multiple requests per second, which requires db connection pooling etc. I don’t think there’s much interest from the core team in increasing Laravel’s performance beyond the current Octane implementation, which only helps by keeping the app booted in memory. It doesn’t use Swoole coroutines at all.
If you want to handle 40k - 100k req/s in real life workloads instead of Octane’s 500-ish, you’ll need to either run a bunch of Octane instances behind a load balancer, or look at a proper coroutine framework like Hyperf. The performance difference is night and day.
People who say ~500 req/s is enough just aren’t building certain kinds of apps. Eg. a log ingestion app, API gateways etc. Even a core team member has written articles about adding Go to a Laravel app stack to handle endpoints which receive high numbers of requests, which clearly demonstrates Laravel’s performance limitations.
Shipping more features seems to be a higher priority than a major upgrade to performance. This kind of change, and the refactoring that would be required (eg. adding db connection pooling) won’t ever come from a third party PR. It would need be be driven by the core team.
Hyperf is heavily inspired by Laravel so there are plenty of code references there the core team could look at.
Can this be related? 👀
No that’s not related. Running a few tasks concurrently is different to workers that can properly handle multiple requests per second, db connection pooling etc. I don’t think there’s much interest from the core team in increasing Laravel’s performance beyond Octane, which only helps by keeping the app booted in memory.
So if you want to handle 40k - 100k req/s in real life workloads instead of Octane’s 500-ish, you’ll need to either run a bunch of Octane instances behind a load balancer, or look at a PHP framework like Hyperf.
People who say 500 req/s is enough just aren’t building certain kinds of apps. Eg. a log ingestion app, an API gateway etc. Even a member of the core team has written articles about adding Go to a Laravel app stack to handle endpoints which receive high numbers of requests, which is saying something.
Shipping more features seems to be a higher priority than any major upgrades to performance. This kind of change and the refactoring (eg. adding db connection pooling) won’t come from a third party PR. It would need be be driven by the core team.
The design idea of octane is still based on fpm,To get better performance, you need to redesign
Managed to get Laravel working with multiple Swoole coroutines in one process today. I still have allot of cleaning up to do before a PR, but I am confident it could be ok. Currently we got it working for a custom Kernel that processes sqs jobs for aws lambda, but to get it working for the http kernel is trivial. By Friday, I'll have it up and running in multiple processes as well (probably already works, but needs further testing).
@taylorotwell , @nunomaduro , is this still of interest?
Managed to get Laravel working with multiple Swoole coroutines in one process today. I still have allot of cleaning up to do before a PR, but I am confident it could be ok. Currently we got it working for a custom Kernel that processes sqs jobs for aws lambda, but to get it working for the http kernel is trivial. By Friday, I'll have it up and running in multiple processes as well (probably already works, but needs further testing).
@taylorotwell , @nunomaduro , is this still of interest?
This will be the most long-awaited and amazing feature for all highload Laravel projects!
This kind of change, and the refactoring that would be required (eg. adding db connection pooling) won’t ever come from a third party PR. It would need be be driven by the core team.
Interestingly enough I did run into this today with testing multiple processes with multiple coroutines each. Database pooling is kinda of a solved problem upstream in openswoole at least (I am using openswoole atm).
https://github.com/openswoole/openswoole/blob/master/example/src/Coroutine/MySQLClientPool.php
Also available: Redis, Postgres.
Going to implement a mysql and redis pool next, which seems to be the last step I need to get the whole thing running blazingly fast. It is actually quite amazing how fast everything is.
@SMFloris Nice! I’d suggest using Swoole rather than Openswoole for testing - all Swoole development happens there. The whole “security” thing was blown completely out of proportion by the Openswoole guy who now just repackages Swoole by himself to try and steal some brand recognition.
Also one of the core Swoole maintainers (@deminy) has said he’s happy to answer questions to help: swoole/swoole-src#4330 (comment)
Re: the performance difference - yeah I’ve used Hyperf for several high load projects and the difference is night and day. It can handle approx 40x more req/s than Laravel Octane thanks to being built with coroutines from the ground up. Pretty comparable to Go. Companies are building game server backends with it, which is pretty nuts for a PHP framework.
IMHO properly implementing coroutines in Laravel would open up the framework to a whole group of devs who would never have considered PHP otherwise.
@huangdijia is a core Hyperf contributor and might be willing to review your PR too
I’d also suggest posting some benchmarks with your PR to show the performance benefits.
@binaryfire thanks for your input! Will use Swoole going forward.
I was just thinking that the level of speed is at the level of Go as well.
What I'll do after implementing the pooling connections as well (I'm trying not to hack to much, doing things the Laravel way as much as possible) is that I'll create a repo with a docker compose so that anyone can run the benchmark easily.
What sort of things would be nice to benchmark? I'm thinking of a route that simply returns an OK status and a route that does some inserts or reads from the database. I personally want to compare: simple Laravel behind nginx, laravel octane as it currently is and laravel octane with all the bells and whistles from coroutines.
This weekend I made some headway;
Turns out there are allot more stuff to wire together than I thought initially.
- First of all, Swoole has some proxy classes for connection pools (i.e. PDOProxy, PDOStatementProxy) and the Connection classes in Laravel need to have their methods' definitions rewritten to also include these. Largely, though, I was able to use the existing MysqlConnection class. I don't know if there already is an interface for the PDO class.
- Then, there are allot of abstractions to take care of in octane specifically; not allot, but enough to give me a hard time.
- Some things will require some more work (like, Octane::concurrently and the task worker)
Why not using coroutine hooks?
It don't need to change code base, just hooking the PHP C code interface.
We can enable it by calling \OpenSwoole\Runtime::enableCoroutine();
before worker start.
I've tried it as issue #906 mentioned but encountered some issues in binding process.
@SMFloris can you possibly push your changes so another person can pick up from where you are at or assist.
To anyone being passionate about this feature, I recommend reading https://openswoole.com/article/isolating-variables-with-coroutine-context.
As I see it, simply flipping the switch to do Runtime::enableCoroutine
and implementing connection pooling would result in a buggy behaviour throughout the app, due to the number of static variables that would essentially get shared between all of the coroutines. You can look at how Octane currently has to reset the state of the container inside each worker before processing a request.
There appears to be an architectural problem with static variables which wouldn't let us use coroutines as of right now. All of that state which is being reset has to be stored in the coroutine context rather than globally.
I'll also look into this later.
Upd:
Not just static variables, many of the container instances have to be retrieved from the coroutine state rather than just reset.
Can it be migrated safely, easily and quickly?
No yet. The project is at alpha stage.
Can it be migrated safely, easily and quickly?
Currently the laravel ecosystem is not supported, such as horizon, telescope, etc.
No yet. The project is at alpha stage.
Hyperf is a completely different framework. The starter project Deeka linked to is just arranged in a way that’ll be familiar to Laravel devs. Directory structure etc.
You can’t swap out Laravel for Hyperf in an exisiting project. It would require significant rewriting.
No yet. The project is at alpha stage.
Hyperf is a completely different framework. The starter project Deeka linked to is just arranged in a way that’ll be familiar to Laravel devs. Directory structure etc.
You can’t swap out Laravel for Hyperf in an exisiting project. It would require significant rewriting.
We migrated a module from our Laravel-Monolith to Hyperf, to run it as a separate Service.
We only had to setup the bootstrapping (Services/middlewares and so on).
For the business logic, we copied over the models, the controllers and everything was working almost out of the box.
We migrated a module from our Laravel-Monolith to Hyperf, to run it as a separate Service.
We only had to setup the bootstrapping (Services/middlewares and so on).
For the business logic, we copied over the models, the controllers and everything was working almost out of the box.
Yeah it's great for microservices. We have several high traffic services using Hyperf - the performance difference is massive. The framework was heavily inspired by Laravel and uses a forked coroutine-friendly version of Eloquent so it's easy for Laravel devs to pick up. Things like models are easy to move over but there are many additional considerations when building complex apps eg. the way Context works.
Not everything in the ecosystem can be ported over eg Livewire: hyperf/hyperf#5517. We've gone all in on Livewire so unfortunately that makes Hyperf a no-go for our core apps. Also there's no first-party auth package since the framework is geared towards microservices. But it's our first choice for anything we have to break out.
Can it be migrated safely, easily and quickly?
Currently the laravel ecosystem is not supported, such as horizon, telescope, etc.
I don’t know the usage of Horizon and Telescope. Compared with those using Laravel, it should be relatively low. It would be very exciting if it could support the migration of Laravel projects. @huangdijia
I don’t know the usage of Horizon and Telescope. Compared with those using Laravel, it should be relatively low. It would be very exciting if it could support the migration of Laravel projects. @huangdijia
Welcome to learn about and use Hyperf. It can be accessed via the documentation (https://hyperf.wiki) to Learn about it.
Hello everyone,
I'm the creator of the Laravel Hyperf project. This project is currently in its final stage before the first dev release. I've migrated most components from Laravel to make them coroutine-friendly while striving to maintain a similar development experience to Laravel.
From my perspective, implementing full coroutine support in the Laravel framework presents significant challenges for several reasons:
-
Laravel's components weren't designed with coroutines in mind. This means all states would need to be isolated within the coroutine context, as @osbre mentioned. Laravel Octane's approach of resetting states after each request doesn't address this fully. For instance, if a singleton object needs to be shared across coroutines, the reset mechanism becomes ineffective.
-
Swoole introduced a
max_concurrency
setting (see here) to enable Laravel Octane to support coroutines with limited restrictions. Whenmax_concurrency
is set to 1, the worker won't accept new requests until the current one is completed. While this ensures coroutine safety and allows for state resets, it essentially makes coroutines an alternative concurrency feature in Laravel without improving QPS performance, as a worker can still only handle one request at a time. -
Even if Laravel Octane were to support full coroutines in the future, it would necessitate significant breaking changes in Laravel components. All states would need to be safely stored using context, which would have a substantial impact on the third-party ecosystem.
The complexity of migrating from Laravel to Laravel Hyperf depends on the extent of state management and third-party packages used in your project. If you're considering migration, keep in mind:
- Laravel Hyperf is a distinct framework, so a cost-free migration process is unrealistic.
- Laravel packages can't be used directly in Laravel Hyperf due to coroutine safety issues.
- The Laravel Hyperf ecosystem is still in its early stages. If you require specific Laravel packages that haven't been migrated to Laravel Hyperf yet, you may need to do it yourself.
In conclusion, I highly recommend trying Laravel Hyperf for new projects with intensive I/O requirements. However, if you're planning to migrate an existing Laravel project, be prepared for necessary modifications and consider the ecosystem implications carefully.
For current Hyperf users, you can also refer to firendsofhyperf for some useful packages.
Even if Laravel Octane were to support full coroutines in the future, it would necessitate significant breaking changes in Laravel components. All states would need to be safely stored using context, which would have a substantial impact on the third-party ecosystem.
No need to introduce breaking changes, They need to fork laravel/framework and continue developing the fork version. @themsaid
Trying to integrate Swoole into PHP-FPM-based (PHP-) Frameworks (like, Laravel) is entirely (and, philosophically) a wrong direction to go.
People who want to enjoy modern (CSP based) Asynchronous PHP-Programming will need to shift the paradigm of their thought Process from PHP-FPM based frameworks to "Asynchronous Programming Paradigm".
They (PHP-FPM and Swoole) both utilise entirely different OS-concepts and entirely different programming concepts, and entirely different Application Architectures. Swoole is just like Go, VertX, Netty, Erlang, Tornado etc.