SlideShare ist ein Scribd-Unternehmen logo
1 von 49
Downloaden Sie, um offline zu lesen
Intro
Lifestory:
Production monolith
migration to Swoole!
● What existing problems need to solve?
● Why choose Swoole?
● How solve it with Swoole?
● What unexpected troubles could happens?
● Show methods to avoid then (or not?)
● Talk about theory
● Examples in a context of a real project
Intro
Side Effects of
- PHP no dying (long-lived)
- Concurrency Async
- Shared Memory access
Intro
● ZF2 / Laminas / Mezzio / PSR-15 / ..
● PHP 5.6 -> … -> 7.4 --> 8.0 + swoole
● PostgreSQL / ElasticSearch Typesense / Redis / RabbitMQ / PubSub
● Docker / k8s / autoscale / Google Cloud
● Files: 2.5k, LOC: 200k, LLOC: 30k
Intro. About project
Intro. About project
Current AVG metrics:
- GA online users: ~3500
- API RPS: ~200
- DB transactions: ~3000/sec
- Products count: ~25kk
Consuming resources:
- PHP: 60 vCPU
- ElasticSearch: 40 vCPU
- PostgreSQL: 16 vCPU
- Others: 20 vCPU
● Not optimized resources usage.
● Hard tuning horizontal scale.
● Over-complicated infrastructure
● Not well performance (TTFB)
Why? Problems
Database
PHP PHP
PHP PHP
Elastic Elastic
Elastic
Other
4x times
● Not optimized resources usage.
● Hard tuning horizontal scale.
● Over-complicated infrastructure
● Not well performance (TTFB)
Why? Problems
PHP
4 vCPU
PHP
16 vCPU
VS
● Not optimized resources usage.
● Hard tuning horizontal scale.
● Over-complicated infrastructure
● Not well performance (TTFB)
Why? Problems
Docker, k8s, Terraform, Helm, GitHub
Actions, Envs: prod+stage+devs+local, Go,
Typescript
● Not optimized resources usage.
● Hard tuning horizontal scale.
● Over-complicated infrastructure
● Not well performance (TTFB)
Why? Problems
● ASYNC entire ecosystem
● Performance
● Easy to start
- Coroutine based concurrent asynchronous IO
- Event Loop
- Process management
- In-memory storage and management
- Async TCP/UDP/HTTP/WebSocket/HTTP2/FastCGI client
and servers
- Async Task API
- Channels, Locks, Timer, Scheduler
NO PECL: ext-pcntl, ext-pthreads, ext-event
Why Swoole?
Milestone 1: PHP no die
● Run HTTP Server
○ replace NGINX+FPM
○ simplify infrastructure (less DevOps, easy building & k8s configs)
○ change (unified) operations: CI / CD / local env
● Prepare bootstrap
● Implement best practices in shared memory usage to avoid side-effects
Plan
● Server Mode: SWOOLE_PROCESS / SWOOLE_BASE
● Dispatch Mode: 1-9 (Round-Robin, Fixed, Preemptive, etc)
● Worker Num: 1-1000 (CPU*2)
● Max Request: 0-XXX (0)
Other Options: Limits, Timeouts, Memory buffers...
php bin/http-server.php
Swoole HTTP Server
https://www.swoole.co.uk/docs/modules/swoole-server/configuration
MUST SEE:
<?php
$server = new SwooleHTTPServer("127.0.0.1", 9501);
$server->on('Request', function(Swoole/Server/Request $request, Swoole/Server/Response $response)
{
$response->end('<h1>Hello World!</h1>');
});
$server->start();
● Scan config files, env, run reflection, attributes, build DI, generate proxy, warm caches:
● NO NEED cache layer anymore
● NOW it before real start http server
(if no traffic: readiness probe=negative)
PSR-7 HTTP Messages
PSR-15 Middleware
PSR-11 Container
bootstrap in master
http request in worker process
Bootstrap app once
fork state
What are the problems?
- NO SUPER GLOBALS ($_SERVER, ...)
- No PHP Session (it is CLI SAPI)
- Stateful services that should mutate on each request
- DI containers - global state too.
Shared Memory
Any wrong example?
Shared Memory
https://github.com/chrisguitarguy/RequestIdBundle/blob/main/src/EventListener/RequestIdListener.php
public function onRequest(RequestEvent $event) : void
{
if (!$this->isMainRequest ($event)) {
return;
}
$req = $event->getRequest();
if ($this->trustRequest && ($id = $req->headers->get($this->requestHeader )))
{
$this->idStorage->setRequestId ($id);
return;
}
if ($id = $this->idStorage->getRequestId ()) {
$req->headers->set($this->requestHeader , $id);
return;
}
$id = $this->idGenerator ->generate();
$req->headers->set($this->requestHeader , $id);
$this->idStorage->setRequestId ($id);
}
Empty storage - no return
Generate NEW
Saving to storage
1
HTTP REQUEST:
Any wrong example?
Shared Memory
public function onRequest(RequestEvent $event) : void
{
if (!$this->isMainRequest ($event)) {
return;
}
$req = $event->getRequest();
if ($this->trustRequest && ($id = $req->headers->get($this->requestHeader )))
{
$this->idStorage->setRequestId ($id);
return;
}
if ($id = $this->idStorage->getRequestId ()) {
$req->headers->set($this->requestHeader , $id);
return;
}
$id = $this->idGenerator ->generate();
$req->headers->set($this->requestHeader , $id);
$this->idStorage->setRequestId ($id);
}
2
HTTP REQUEST:
Now has ID in storage
THE END
Dead code
https://github.com/chrisguitarguy/RequestIdBundle/blob/main/src/EventListener/RequestIdListener.php
Shared Memory
HTTP Server options:
Max Request: 100
What if...
Then…
In logs request ID is changed.
Best practices:
- Middlewares, Factories, Proxies, Delegators
Shared Memory
use PsrHttpMessageResponseInterface
;
use PsrHttpMessageServerRequestInterface
;
use PsrHttpServerRequestHandlerInterface
;
use PsrLogLoggerInterface
;
class SomeHandler implements RequestHandlerInterface
{
public function __construct(
private Closure $appServiceFactory
,
private LoggerInterface $logger
) {}
// Idempotent method!!!
public function handle(ServerRequestInterface $request) : ResponseInterface
{
$logger = clone $this->logger;
$logger->getProcessor(
'RequestID')->setRequestId(
$request->getAttribute(
'RequestID'));
$appService = ($this->appServiceFactory)(
$logger);
return new JsonResponse($appService->createBook())
;
}
}
Memory leaks
… in Doctrine ORM :(
Shared Memory
https://alejandrocelaya.blog/2019/11/04/how-to-properly-handle-a-doctrine-entity-manager-on-an-expressive-appli
cation-served-with-swoole/
use DoctrineORMDecoratorEntityManagerDecorator as
EMD;
class ReopeningEntityManager extends EMD
{
private $createEm;
public function __construct (callable $createEm)
{
parent::__construct($createEm());
$this->createEm = $createEm;
}
public function open(): void
{
if (! $this->wrapped->isOpen()) {
$this->wrapped = ($this->createEm)();
}
}
class CloseDbConnectionMiddleware implements
MiddlewareInterface
{
public function __construct(
private ReopeningEntityManager $em)
{}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
) : ResponseInterface
{
$this->em->open() ;
try {
return $handler->handle($request);
} finally {
$this->em->getConnection()->close() ;
$this->em->clear() ;
}
}
}
Saved in DI
Shared Memory
Memory leaks
BUT WHERE???
abstract class EntityManagerDecorator extends
ObjectManagerDecorator
{
/** @var EntityManagerInterface */
protected $wrapped;
public function __construct (EntityManagerInterface $wrapped)
{
$this->wrapped = $wrapped;
}
public function getRepository ($className)
{
return $this->wrapped->getRepository ($className);
}
public function getRepository ($entityName )
{
return $this->repositoryFactory ->getRepository ($this,
$entityName );
}
Somewhere far far away
in EntityManager
PROBLEMS DUE TO: timeouts / lifecycles
Avoid: Stateful convert to stateless
Connections
$redis = $di->get(Redis::class); // FactoryRedis: $redis->connect(...);
$redis->get('KEY1');
$redisFactory = $di->get(RedisFactory::
class);
$redisFactory()->get(‘KEY1’); // + close connect in desctructor
Request Wrapper/Delegator:
new/refresh state on each request
OR
Milestone 2: ASYNC
● Async theory
● Using coroutines
● Non-blocked IO solutions
● Concurrency problems review
● Again memory leaks
● GET /some/action1/ SELECT sleep(1);
○ 1 worker = 1 req/sec
○ 2 worker = 2 req/sec
● GET /some/action2/ fibonacci(30);
○ 1 worker = 1 req/sec
○ 2 worker = depends on CPU cores
Why/What async?
Try benchmark this:
● GET /some/action1/ SELECT sleep(1);
○ 1 worker = 1 req/sec
○ 2 worker = 2 req/sec
● GET /some/action2/ fibonacci(30);
○ 1 worker = 1 req/sec
○ 2 worker = depends on CPU cores
MIX 50/50 = 1 req/sec
50% CPU
Why/What async?
Try benchmark this:
Now enable coroutines: http server options: .
'enable_coroutine' => true,
Why/What async?
● GET /some/action1/
○ 1 worker = 10000 req/sec
○ 2 worker = 10000 req/sec
● GET /some/action2/
○ 1 worker = 1 req/sec
○ 2 worker = depends on CPU cores
Now enable coroutines: http server options: .
'enable_coroutine' => true,
Why/What async?
● GET /some/action1/
○ 1 worker = 10000 req/sec
○ 2 worker = 10000 req/sec
● GET /some/action2/
○ 1 worker = 1 req/sec
○ 2 worker = depends on CPU cores
MIX 50/50 = 2 req/sec
100% CPU
go(function () { // FIRST CO
echo '1';
go(function () { // SECOND CO
echo '2';
co::sleep(3); // IO (in 2 CO), will return in 3 sec
echo '6';
go(function () { // THIRD CO
echo '7';
co::sleep(2); // IO
echo "9n";
}); // END THIRD
echo '8';
}); // END SECOND
echo '3';
co::sleep(1); // Again IO but in 1 CO
echo '5';
}); // END FIRST CO
echo '4';
Coroutines
1
2
3
4
5
Coroutines
https://www.swoole.co.uk/docs/modules/swoole-runtime-flags
Swoole Hooks
Coroutine Clients:
● TCP/UDP
● HTTP
● HTTP2
● Socket
● FastCGI
● Redis
● MySQL
● Postgresql
Runtime HOOKS:
● Coroutine support (any IO libraries based on php_stream):
○ Redis (phpredis) or (predis)
○ MySQL (mysqlnd) PDO and MySQLi
○ PHP SOAP
○ file_get_contents, fopen and many more file I/O operations
○ stream_socket_client functions
○ fsockopen
○ CURL with libcurl
● Libraries without coroutine support:
○ MySQL with libmysqlclient
○ MongoDB with mongo-c-client
○ pdo_pgsql, pdo_ori, pdo_odbc, pdo_firebird, php-amqp
VS
Non-blocked IO
$swooleServer->onRequest: go($this->requestHandler);
--------------------------------------------------------------------------
non-blocked: multiple requests in same time/code
Concurrency
function onRequest() {
$class = $di->get(stdClass:class);
if ($class->some === null) {
$class->some = 123;
}
sleep(2);
// && some logic
$class->some = null;
}
First request awaiting here
We expected clean in request end
http server options: .
'enable_coroutine' => true,
$swooleServer->onRequest: go($this->requestHandler);
--------------------------------------------------------------------------
non-blocked: multiple requests in same time/code
Concurrency
function onRequest() {
$class = $di->get(stdClass:class);
if ($class->some === null) {
$class->some = 123;
}
sleep(2);
// && some logic
$class->some = null;
}
First request awaiting here
We expected clean in request end
EPIC FAIL! in second request
http server options: .
'enable_coroutine' => true,
Request Wrapper/Delegator: new/refresh state on each request
Uncaught SwooleError: Socket# has already been
bound to another coroutine
Connections. Again?
$http = new SwooleHttpServer( '0.0.0.0', '80', SWOOLE_PROCESS) ;
$http->on('request', function (SwooleHttpRequest $request, SwooleHttpResponse $response)
{
if (empty($redis)) {
$redis = new Redis();
$redis->connect('127.0.0.1' , 6379);
$redis->select(1);
$redis->setOption(Redis::OPT_SERIALIZER , Redis::SERIALIZER_PHP) ;
}
try {
$redisJson = $redis->get('key');
} catch (Exception $e) {
// SWOOLE ERROR HERE !
}
$response->end('some response' );
});
https://www.swoole.co.uk/docs/modules/redis-connection-pool
Connections. Again?
POOL
Connections. Again?
use
DoctrineCommonCacheCacheProvider ;
use Redis;
use SwooleDatabaseRedisPool ;
class SwooleRedisCacheProvider extends
CacheProvider
{
public function __construct (
private RedisPool $pool,
) {
}
protected function doFetch($id, ?Redis $redis = null)
{
if (! $redis) {
return $this->run(fn (Redis $redis) : mixed =>
. $this->doFetch($id, $redis));
}
return $redis->get($id);
}
private function run(callable $procedure)
{
$redis = $this->pool->get();
$redis->setOption(Redis::SERIALIZER, $this->getSerializer ());
try {
$result = $procedure($redis);
} catch (Throwable $e) {
throw $e;
} finally {
$this->pool->put($redis);
}
return $result;
}
Memory Leaks. Again?
final class ReopeningEntityManager implements EntityManagerInterface
{
private Closure $entityManagerFactory ;
protected array $wrapped = [];
public function clear($object = null) : void
{
$this->getWrapped()->clear($object);
unset ($this->wrapped[Co::getCid()]); // does not help!
}
private function getWrapped() : EntityManagerInterface
{
if (! isset($this->wrapped[Co::getCid()])) {
$this->wrapped[Co::getCid()] =
($this->entityManagerFactory )();
}
return $this->wrapped[Co::getCid()];
}
public function getConnection ()
{
return $this->getWrapped()->getConnection ();
}
Memory Leaks. Again?
Using WeakMap &
defer in coroutines!
Restart workers?
You are coward!
Using PostgreSQL - no PDO hooks in Swoole
● Use Coroutine Postgresql Client:
extension=swoole_postgresql.so
● Write new Driver for Doctrine ORM
● Be ready to problems
Problem Again
Other miscellaneous:
Manage crons (by timers)
Manage message workers (process manager)
Manage Signals (soft termination)
Milestone 3: others
Cron jobs
- persist Deployment: run “crontab” process + list php CLI command
- CronJob (k8s) periodical run POD with “php bin/app cron:process”
- CronJob as Message (run as “bin/app messenger:consume transport.amqp”)
- + Swoole Timer async callback in PHP master process (instead linux crontab)
Prӕfectus
https://github.com/opsway/praefectus
Supervisor & Metrics server for Symfony Messenger
Prӕfectus
PraefectusListener
use SpiralGoridgeRelay
;
use SpiralGoridgeRPCRPC
;
use SpiralGoridgeRPCRPCInterface
;
/**
* @see SymfonyComponentMessengerWorker
*/
class PraefectusListener implements EventSubscriberInterface
{
private const IPC_SOCKET_PATH_TPL = '/tmp/praefectus_%d.sock'
;
// …
public function onMessageReceived (EventWorkerMessageReceivedEvent $event) : void
{
$this->getRpc()->call('PraefectusRPC.WorkerState' ,['pid'=>getmypid() ,'state'=>self::WORKER_BUSY]);
$this->getRpc()->call('PraefectusRPC.MessageState' , [
'id' => $messageIdStamp ->id(),
'name' => get_class( $event->getEnvelope ()->getMessage()),
'transport' => $event->getReceiverName (),
'bus' => $busName,
'state' => self::MESSAGE_STATE_PROCESSING,
]);
}
}
*not yet released on GitHub
● no compatibility in code:
○ run same code in FPM & build-in http server
○ work without swoole extension (PoC - write stubs?)
○ XDebug - goodbye (use PCOV for coverage)
○ Profiling - https://github.com/upscalesoftware
Results
● Doctrine + async = EVIL*
Results
* But is possible, if enough extra memory:
Each concurrency EntityManager = +100-150Mb
● Swoole Table for cache - must have!
Results
Doctrine (any) cache shared between workers.
Solved case: Deployment & migration process without
50x errors
● Benchmarks - good results!
Results
- empty handler (bootstrap) - TTFB before: 140 ms, after: 4 ms
SWOOLE
FPM
● Benchmarks - good results!
Results
- empty handler (bootstrap) - TTFB before: 140 ms, after: 4 ms
- AVG (all traffic) TTFB reduced on 40% - before: 250 ms, after: 150 ms
- 95 percentile - before: 1500 ms, after: 600 ms
P.S. Load by same API RPS: ~200
- Now: 20 (PHP) / 20 (Typesense) / 20 (other) / 16 (DB) vCPUs
- 6k$ --> 3.5k$ (per month)
Results
● Resources usage - (ETA) reduced!
Useful links:
- https://github.com/deminy/swoole-by-examples
- https://docs.mezzio.dev/mezzio/v3/why-mezzio/
- https://docs.mezzio.dev/mezzio-swoole/v3/considerations/
- https://www.swoole.co.uk/docs/modules/swoole-server/configuration
- https://github.com/swooletw/awesome-swoole
- https://github.com/k911/swoole-bundle
We looking: DevOps / Backend Developer / Frontend Developer / QA
manual+automation.
Any Questions?
BTW:

Weitere ähnliche Inhalte

Was ist angesagt?

Ipv6 소켓프로그래밍
Ipv6 소켓프로그래밍Ipv6 소켓프로그래밍
Ipv6 소켓프로그래밍
Heo Seungwook
 
JavaOne 2012 - JVM JIT for Dummies
JavaOne 2012 - JVM JIT for DummiesJavaOne 2012 - JVM JIT for Dummies
JavaOne 2012 - JVM JIT for Dummies
Charles Nutter
 

Was ist angesagt? (20)

[오픈소스컨설팅] 아파치톰캣 운영가이드 v1.3
[오픈소스컨설팅] 아파치톰캣 운영가이드 v1.3[오픈소스컨설팅] 아파치톰캣 운영가이드 v1.3
[오픈소스컨설팅] 아파치톰캣 운영가이드 v1.3
 
淺談探索 Linux 系統設計之道
淺談探索 Linux 系統設計之道 淺談探索 Linux 系統設計之道
淺談探索 Linux 系統設計之道
 
Distributed fun with etcd
Distributed fun with etcdDistributed fun with etcd
Distributed fun with etcd
 
Project Reactor Now and Tomorrow
Project Reactor Now and TomorrowProject Reactor Now and Tomorrow
Project Reactor Now and Tomorrow
 
From cache to in-memory data grid. Introduction to Hazelcast.
From cache to in-memory data grid. Introduction to Hazelcast.From cache to in-memory data grid. Introduction to Hazelcast.
From cache to in-memory data grid. Introduction to Hazelcast.
 
Introduction to Node JS.pdf
Introduction to Node JS.pdfIntroduction to Node JS.pdf
Introduction to Node JS.pdf
 
Ipv6 소켓프로그래밍
Ipv6 소켓프로그래밍Ipv6 소켓프로그래밍
Ipv6 소켓프로그래밍
 
The Evolution of Distributed Systems on Kubernetes
The Evolution of Distributed Systems on KubernetesThe Evolution of Distributed Systems on Kubernetes
The Evolution of Distributed Systems on Kubernetes
 
Reactive programming intro
Reactive programming introReactive programming intro
Reactive programming intro
 
From Generator to Fiber the Road to Coroutine in PHP
From Generator to Fiber the Road to Coroutine in PHPFrom Generator to Fiber the Road to Coroutine in PHP
From Generator to Fiber the Road to Coroutine in PHP
 
2021.laravelconf.tw.slides1
2021.laravelconf.tw.slides12021.laravelconf.tw.slides1
2021.laravelconf.tw.slides1
 
Understanding Reactive Programming
Understanding Reactive ProgrammingUnderstanding Reactive Programming
Understanding Reactive Programming
 
JavaOne 2012 - JVM JIT for Dummies
JavaOne 2012 - JVM JIT for DummiesJavaOne 2012 - JVM JIT for Dummies
JavaOne 2012 - JVM JIT for Dummies
 
Introduction to Java 11
Introduction to Java 11 Introduction to Java 11
Introduction to Java 11
 
Kubernetes Introduction
Kubernetes IntroductionKubernetes Introduction
Kubernetes Introduction
 
Java 9 Features
Java 9 FeaturesJava 9 Features
Java 9 Features
 
DevOps with Kubernetes
DevOps with KubernetesDevOps with Kubernetes
DevOps with Kubernetes
 
Docker Commands With Examples | Docker Tutorial | DevOps Tutorial | Docker Tr...
Docker Commands With Examples | Docker Tutorial | DevOps Tutorial | Docker Tr...Docker Commands With Examples | Docker Tutorial | DevOps Tutorial | Docker Tr...
Docker Commands With Examples | Docker Tutorial | DevOps Tutorial | Docker Tr...
 
Spring Core
Spring CoreSpring Core
Spring Core
 
Scalability, Availability & Stability Patterns
Scalability, Availability & Stability PatternsScalability, Availability & Stability Patterns
Scalability, Availability & Stability Patterns
 

Ähnlich wie "Swoole: double troubles in c", Alexandr Vronskiy

Scalable Socket Server by Aryo
Scalable Socket Server by AryoScalable Socket Server by Aryo
Scalable Socket Server by Aryo
Agate Studio
 

Ähnlich wie "Swoole: double troubles in c", Alexandr Vronskiy (20)

DSLing your System For Scalability Testing Using Gatling - Dublin Scala User ...
DSLing your System For Scalability Testing Using Gatling - Dublin Scala User ...DSLing your System For Scalability Testing Using Gatling - Dublin Scala User ...
DSLing your System For Scalability Testing Using Gatling - Dublin Scala User ...
 
Original slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talkOriginal slides from Ryan Dahl's NodeJs intro talk
Original slides from Ryan Dahl's NodeJs intro talk
 
ApacheConNA 2015: What's new in Apache httpd 2.4
ApacheConNA 2015: What's new in Apache httpd 2.4ApacheConNA 2015: What's new in Apache httpd 2.4
ApacheConNA 2015: What's new in Apache httpd 2.4
 
php & performance
 php & performance php & performance
php & performance
 
Building and Scaling Node.js Applications
Building and Scaling Node.js ApplicationsBuilding and Scaling Node.js Applications
Building and Scaling Node.js Applications
 
PHP & Performance
PHP & PerformancePHP & Performance
PHP & Performance
 
Haproxy - zastosowania
Haproxy - zastosowaniaHaproxy - zastosowania
Haproxy - zastosowania
 
Scalable Socket Server by Aryo
Scalable Socket Server by AryoScalable Socket Server by Aryo
Scalable Socket Server by Aryo
 
Symfony Performance
Symfony PerformanceSymfony Performance
Symfony Performance
 
PHP Sessions and Non-Sessions
PHP Sessions and Non-SessionsPHP Sessions and Non-Sessions
PHP Sessions and Non-Sessions
 
Socket programming, and openresty
Socket programming, and openrestySocket programming, and openresty
Socket programming, and openresty
 
Redis
RedisRedis
Redis
 
202107 - Orion introduction - COSCUP
202107 - Orion introduction - COSCUP202107 - Orion introduction - COSCUP
202107 - Orion introduction - COSCUP
 
MySQL HA Orchestrator Proxysql Consul.pdf
MySQL HA Orchestrator Proxysql Consul.pdfMySQL HA Orchestrator Proxysql Consul.pdf
MySQL HA Orchestrator Proxysql Consul.pdf
 
NodeJS
NodeJSNodeJS
NodeJS
 
Debugging: Rules & Tools
Debugging: Rules & ToolsDebugging: Rules & Tools
Debugging: Rules & Tools
 
Php
PhpPhp
Php
 
Presentation of OrientDB v2.2 - Webinar
Presentation of OrientDB v2.2 - WebinarPresentation of OrientDB v2.2 - Webinar
Presentation of OrientDB v2.2 - Webinar
 
Introduction to ZooKeeper - TriHUG May 22, 2012
Introduction to ZooKeeper - TriHUG May 22, 2012Introduction to ZooKeeper - TriHUG May 22, 2012
Introduction to ZooKeeper - TriHUG May 22, 2012
 
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rhebok, High Performance Rack Handler / Rubykaigi 2015
 

Mehr von Fwdays

Mehr von Fwdays (20)

"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y..."How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
 
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
 
"Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl..."Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl...
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
 

Kürzlich hochgeladen

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Kürzlich hochgeladen (20)

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 

"Swoole: double troubles in c", Alexandr Vronskiy

  • 1.
  • 3. ● What existing problems need to solve? ● Why choose Swoole? ● How solve it with Swoole? ● What unexpected troubles could happens? ● Show methods to avoid then (or not?) ● Talk about theory ● Examples in a context of a real project Intro
  • 4. Side Effects of - PHP no dying (long-lived) - Concurrency Async - Shared Memory access Intro
  • 5. ● ZF2 / Laminas / Mezzio / PSR-15 / .. ● PHP 5.6 -> … -> 7.4 --> 8.0 + swoole ● PostgreSQL / ElasticSearch Typesense / Redis / RabbitMQ / PubSub ● Docker / k8s / autoscale / Google Cloud ● Files: 2.5k, LOC: 200k, LLOC: 30k Intro. About project
  • 6. Intro. About project Current AVG metrics: - GA online users: ~3500 - API RPS: ~200 - DB transactions: ~3000/sec - Products count: ~25kk Consuming resources: - PHP: 60 vCPU - ElasticSearch: 40 vCPU - PostgreSQL: 16 vCPU - Others: 20 vCPU
  • 7. ● Not optimized resources usage. ● Hard tuning horizontal scale. ● Over-complicated infrastructure ● Not well performance (TTFB) Why? Problems Database PHP PHP PHP PHP Elastic Elastic Elastic Other 4x times
  • 8. ● Not optimized resources usage. ● Hard tuning horizontal scale. ● Over-complicated infrastructure ● Not well performance (TTFB) Why? Problems PHP 4 vCPU PHP 16 vCPU VS
  • 9. ● Not optimized resources usage. ● Hard tuning horizontal scale. ● Over-complicated infrastructure ● Not well performance (TTFB) Why? Problems Docker, k8s, Terraform, Helm, GitHub Actions, Envs: prod+stage+devs+local, Go, Typescript
  • 10. ● Not optimized resources usage. ● Hard tuning horizontal scale. ● Over-complicated infrastructure ● Not well performance (TTFB) Why? Problems
  • 11. ● ASYNC entire ecosystem ● Performance ● Easy to start - Coroutine based concurrent asynchronous IO - Event Loop - Process management - In-memory storage and management - Async TCP/UDP/HTTP/WebSocket/HTTP2/FastCGI client and servers - Async Task API - Channels, Locks, Timer, Scheduler NO PECL: ext-pcntl, ext-pthreads, ext-event Why Swoole?
  • 12. Milestone 1: PHP no die ● Run HTTP Server ○ replace NGINX+FPM ○ simplify infrastructure (less DevOps, easy building & k8s configs) ○ change (unified) operations: CI / CD / local env ● Prepare bootstrap ● Implement best practices in shared memory usage to avoid side-effects Plan
  • 13. ● Server Mode: SWOOLE_PROCESS / SWOOLE_BASE ● Dispatch Mode: 1-9 (Round-Robin, Fixed, Preemptive, etc) ● Worker Num: 1-1000 (CPU*2) ● Max Request: 0-XXX (0) Other Options: Limits, Timeouts, Memory buffers... php bin/http-server.php Swoole HTTP Server https://www.swoole.co.uk/docs/modules/swoole-server/configuration MUST SEE: <?php $server = new SwooleHTTPServer("127.0.0.1", 9501); $server->on('Request', function(Swoole/Server/Request $request, Swoole/Server/Response $response) { $response->end('<h1>Hello World!</h1>'); }); $server->start();
  • 14. ● Scan config files, env, run reflection, attributes, build DI, generate proxy, warm caches: ● NO NEED cache layer anymore ● NOW it before real start http server (if no traffic: readiness probe=negative) PSR-7 HTTP Messages PSR-15 Middleware PSR-11 Container bootstrap in master http request in worker process Bootstrap app once fork state
  • 15. What are the problems? - NO SUPER GLOBALS ($_SERVER, ...) - No PHP Session (it is CLI SAPI) - Stateful services that should mutate on each request - DI containers - global state too. Shared Memory
  • 16. Any wrong example? Shared Memory https://github.com/chrisguitarguy/RequestIdBundle/blob/main/src/EventListener/RequestIdListener.php public function onRequest(RequestEvent $event) : void { if (!$this->isMainRequest ($event)) { return; } $req = $event->getRequest(); if ($this->trustRequest && ($id = $req->headers->get($this->requestHeader ))) { $this->idStorage->setRequestId ($id); return; } if ($id = $this->idStorage->getRequestId ()) { $req->headers->set($this->requestHeader , $id); return; } $id = $this->idGenerator ->generate(); $req->headers->set($this->requestHeader , $id); $this->idStorage->setRequestId ($id); } Empty storage - no return Generate NEW Saving to storage 1 HTTP REQUEST:
  • 17. Any wrong example? Shared Memory public function onRequest(RequestEvent $event) : void { if (!$this->isMainRequest ($event)) { return; } $req = $event->getRequest(); if ($this->trustRequest && ($id = $req->headers->get($this->requestHeader ))) { $this->idStorage->setRequestId ($id); return; } if ($id = $this->idStorage->getRequestId ()) { $req->headers->set($this->requestHeader , $id); return; } $id = $this->idGenerator ->generate(); $req->headers->set($this->requestHeader , $id); $this->idStorage->setRequestId ($id); } 2 HTTP REQUEST: Now has ID in storage THE END Dead code https://github.com/chrisguitarguy/RequestIdBundle/blob/main/src/EventListener/RequestIdListener.php
  • 18. Shared Memory HTTP Server options: Max Request: 100 What if... Then… In logs request ID is changed.
  • 19. Best practices: - Middlewares, Factories, Proxies, Delegators Shared Memory use PsrHttpMessageResponseInterface ; use PsrHttpMessageServerRequestInterface ; use PsrHttpServerRequestHandlerInterface ; use PsrLogLoggerInterface ; class SomeHandler implements RequestHandlerInterface { public function __construct( private Closure $appServiceFactory , private LoggerInterface $logger ) {} // Idempotent method!!! public function handle(ServerRequestInterface $request) : ResponseInterface { $logger = clone $this->logger; $logger->getProcessor( 'RequestID')->setRequestId( $request->getAttribute( 'RequestID')); $appService = ($this->appServiceFactory)( $logger); return new JsonResponse($appService->createBook()) ; } }
  • 20. Memory leaks … in Doctrine ORM :( Shared Memory https://alejandrocelaya.blog/2019/11/04/how-to-properly-handle-a-doctrine-entity-manager-on-an-expressive-appli cation-served-with-swoole/ use DoctrineORMDecoratorEntityManagerDecorator as EMD; class ReopeningEntityManager extends EMD { private $createEm; public function __construct (callable $createEm) { parent::__construct($createEm()); $this->createEm = $createEm; } public function open(): void { if (! $this->wrapped->isOpen()) { $this->wrapped = ($this->createEm)(); } } class CloseDbConnectionMiddleware implements MiddlewareInterface { public function __construct( private ReopeningEntityManager $em) {} public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface { $this->em->open() ; try { return $handler->handle($request); } finally { $this->em->getConnection()->close() ; $this->em->clear() ; } } } Saved in DI
  • 21. Shared Memory Memory leaks BUT WHERE??? abstract class EntityManagerDecorator extends ObjectManagerDecorator { /** @var EntityManagerInterface */ protected $wrapped; public function __construct (EntityManagerInterface $wrapped) { $this->wrapped = $wrapped; } public function getRepository ($className) { return $this->wrapped->getRepository ($className); } public function getRepository ($entityName ) { return $this->repositoryFactory ->getRepository ($this, $entityName ); } Somewhere far far away in EntityManager
  • 22. PROBLEMS DUE TO: timeouts / lifecycles Avoid: Stateful convert to stateless Connections $redis = $di->get(Redis::class); // FactoryRedis: $redis->connect(...); $redis->get('KEY1'); $redisFactory = $di->get(RedisFactory:: class); $redisFactory()->get(‘KEY1’); // + close connect in desctructor Request Wrapper/Delegator: new/refresh state on each request OR
  • 23. Milestone 2: ASYNC ● Async theory ● Using coroutines ● Non-blocked IO solutions ● Concurrency problems review ● Again memory leaks
  • 24. ● GET /some/action1/ SELECT sleep(1); ○ 1 worker = 1 req/sec ○ 2 worker = 2 req/sec ● GET /some/action2/ fibonacci(30); ○ 1 worker = 1 req/sec ○ 2 worker = depends on CPU cores Why/What async? Try benchmark this:
  • 25. ● GET /some/action1/ SELECT sleep(1); ○ 1 worker = 1 req/sec ○ 2 worker = 2 req/sec ● GET /some/action2/ fibonacci(30); ○ 1 worker = 1 req/sec ○ 2 worker = depends on CPU cores MIX 50/50 = 1 req/sec 50% CPU Why/What async? Try benchmark this:
  • 26. Now enable coroutines: http server options: . 'enable_coroutine' => true, Why/What async? ● GET /some/action1/ ○ 1 worker = 10000 req/sec ○ 2 worker = 10000 req/sec ● GET /some/action2/ ○ 1 worker = 1 req/sec ○ 2 worker = depends on CPU cores
  • 27. Now enable coroutines: http server options: . 'enable_coroutine' => true, Why/What async? ● GET /some/action1/ ○ 1 worker = 10000 req/sec ○ 2 worker = 10000 req/sec ● GET /some/action2/ ○ 1 worker = 1 req/sec ○ 2 worker = depends on CPU cores MIX 50/50 = 2 req/sec 100% CPU
  • 28. go(function () { // FIRST CO echo '1'; go(function () { // SECOND CO echo '2'; co::sleep(3); // IO (in 2 CO), will return in 3 sec echo '6'; go(function () { // THIRD CO echo '7'; co::sleep(2); // IO echo "9n"; }); // END THIRD echo '8'; }); // END SECOND echo '3'; co::sleep(1); // Again IO but in 1 CO echo '5'; }); // END FIRST CO echo '4'; Coroutines 1 2 3 4 5
  • 30. https://www.swoole.co.uk/docs/modules/swoole-runtime-flags Swoole Hooks Coroutine Clients: ● TCP/UDP ● HTTP ● HTTP2 ● Socket ● FastCGI ● Redis ● MySQL ● Postgresql Runtime HOOKS: ● Coroutine support (any IO libraries based on php_stream): ○ Redis (phpredis) or (predis) ○ MySQL (mysqlnd) PDO and MySQLi ○ PHP SOAP ○ file_get_contents, fopen and many more file I/O operations ○ stream_socket_client functions ○ fsockopen ○ CURL with libcurl ● Libraries without coroutine support: ○ MySQL with libmysqlclient ○ MongoDB with mongo-c-client ○ pdo_pgsql, pdo_ori, pdo_odbc, pdo_firebird, php-amqp VS Non-blocked IO
  • 31. $swooleServer->onRequest: go($this->requestHandler); -------------------------------------------------------------------------- non-blocked: multiple requests in same time/code Concurrency function onRequest() { $class = $di->get(stdClass:class); if ($class->some === null) { $class->some = 123; } sleep(2); // && some logic $class->some = null; } First request awaiting here We expected clean in request end http server options: . 'enable_coroutine' => true,
  • 32. $swooleServer->onRequest: go($this->requestHandler); -------------------------------------------------------------------------- non-blocked: multiple requests in same time/code Concurrency function onRequest() { $class = $di->get(stdClass:class); if ($class->some === null) { $class->some = 123; } sleep(2); // && some logic $class->some = null; } First request awaiting here We expected clean in request end EPIC FAIL! in second request http server options: . 'enable_coroutine' => true,
  • 33. Request Wrapper/Delegator: new/refresh state on each request Uncaught SwooleError: Socket# has already been bound to another coroutine Connections. Again? $http = new SwooleHttpServer( '0.0.0.0', '80', SWOOLE_PROCESS) ; $http->on('request', function (SwooleHttpRequest $request, SwooleHttpResponse $response) { if (empty($redis)) { $redis = new Redis(); $redis->connect('127.0.0.1' , 6379); $redis->select(1); $redis->setOption(Redis::OPT_SERIALIZER , Redis::SERIALIZER_PHP) ; } try { $redisJson = $redis->get('key'); } catch (Exception $e) { // SWOOLE ERROR HERE ! } $response->end('some response' ); });
  • 35. Connections. Again? use DoctrineCommonCacheCacheProvider ; use Redis; use SwooleDatabaseRedisPool ; class SwooleRedisCacheProvider extends CacheProvider { public function __construct ( private RedisPool $pool, ) { } protected function doFetch($id, ?Redis $redis = null) { if (! $redis) { return $this->run(fn (Redis $redis) : mixed => . $this->doFetch($id, $redis)); } return $redis->get($id); } private function run(callable $procedure) { $redis = $this->pool->get(); $redis->setOption(Redis::SERIALIZER, $this->getSerializer ()); try { $result = $procedure($redis); } catch (Throwable $e) { throw $e; } finally { $this->pool->put($redis); } return $result; }
  • 36. Memory Leaks. Again? final class ReopeningEntityManager implements EntityManagerInterface { private Closure $entityManagerFactory ; protected array $wrapped = []; public function clear($object = null) : void { $this->getWrapped()->clear($object); unset ($this->wrapped[Co::getCid()]); // does not help! } private function getWrapped() : EntityManagerInterface { if (! isset($this->wrapped[Co::getCid()])) { $this->wrapped[Co::getCid()] = ($this->entityManagerFactory )(); } return $this->wrapped[Co::getCid()]; } public function getConnection () { return $this->getWrapped()->getConnection (); }
  • 37. Memory Leaks. Again? Using WeakMap & defer in coroutines! Restart workers? You are coward!
  • 38. Using PostgreSQL - no PDO hooks in Swoole ● Use Coroutine Postgresql Client: extension=swoole_postgresql.so ● Write new Driver for Doctrine ORM ● Be ready to problems Problem Again
  • 39. Other miscellaneous: Manage crons (by timers) Manage message workers (process manager) Manage Signals (soft termination) Milestone 3: others
  • 40. Cron jobs - persist Deployment: run “crontab” process + list php CLI command - CronJob (k8s) periodical run POD with “php bin/app cron:process” - CronJob as Message (run as “bin/app messenger:consume transport.amqp”) - + Swoole Timer async callback in PHP master process (instead linux crontab)
  • 42. Prӕfectus PraefectusListener use SpiralGoridgeRelay ; use SpiralGoridgeRPCRPC ; use SpiralGoridgeRPCRPCInterface ; /** * @see SymfonyComponentMessengerWorker */ class PraefectusListener implements EventSubscriberInterface { private const IPC_SOCKET_PATH_TPL = '/tmp/praefectus_%d.sock' ; // … public function onMessageReceived (EventWorkerMessageReceivedEvent $event) : void { $this->getRpc()->call('PraefectusRPC.WorkerState' ,['pid'=>getmypid() ,'state'=>self::WORKER_BUSY]); $this->getRpc()->call('PraefectusRPC.MessageState' , [ 'id' => $messageIdStamp ->id(), 'name' => get_class( $event->getEnvelope ()->getMessage()), 'transport' => $event->getReceiverName (), 'bus' => $busName, 'state' => self::MESSAGE_STATE_PROCESSING, ]); } } *not yet released on GitHub
  • 43. ● no compatibility in code: ○ run same code in FPM & build-in http server ○ work without swoole extension (PoC - write stubs?) ○ XDebug - goodbye (use PCOV for coverage) ○ Profiling - https://github.com/upscalesoftware Results
  • 44. ● Doctrine + async = EVIL* Results * But is possible, if enough extra memory: Each concurrency EntityManager = +100-150Mb
  • 45. ● Swoole Table for cache - must have! Results Doctrine (any) cache shared between workers. Solved case: Deployment & migration process without 50x errors
  • 46. ● Benchmarks - good results! Results - empty handler (bootstrap) - TTFB before: 140 ms, after: 4 ms SWOOLE FPM
  • 47. ● Benchmarks - good results! Results - empty handler (bootstrap) - TTFB before: 140 ms, after: 4 ms - AVG (all traffic) TTFB reduced on 40% - before: 250 ms, after: 150 ms - 95 percentile - before: 1500 ms, after: 600 ms P.S. Load by same API RPS: ~200
  • 48. - Now: 20 (PHP) / 20 (Typesense) / 20 (other) / 16 (DB) vCPUs - 6k$ --> 3.5k$ (per month) Results ● Resources usage - (ETA) reduced!
  • 49. Useful links: - https://github.com/deminy/swoole-by-examples - https://docs.mezzio.dev/mezzio/v3/why-mezzio/ - https://docs.mezzio.dev/mezzio-swoole/v3/considerations/ - https://www.swoole.co.uk/docs/modules/swoole-server/configuration - https://github.com/swooletw/awesome-swoole - https://github.com/k911/swoole-bundle We looking: DevOps / Backend Developer / Frontend Developer / QA manual+automation. Any Questions? BTW: