danog / MadelineProto

Async PHP client API for the telegram MTProto protocol
https://docs.madelineproto.xyz
GNU Affero General Public License v3.0
2.76k stars 619 forks source link

Incompatibility between MadelineProto event handlers and blocking resources #1446

Closed a-kbv closed 7 months ago

a-kbv commented 7 months ago

When implementing a TelegramEventHandler that relies on blocking resources such as PDO or repositories, MadelineProto's event loop throws a severe issue during static analysis. The error message suggests that blocking resources are not allowed for performance reasons, and recommends using non-blocking alternatives like amphp/mysql.

Expected Behavior

Ideally, there should be a way to integrate traditional, synchronous PHP code and resources like those provided by Symfony's ORM, within the MadelineProto event loop, without having to completely refactor existing codebases to use asynchronous alternatives.

Actual Behavior

Currently, the use of blocking resources within the event handlers is prohibited, which presents a significant challenge in integrating MadelineProto with applications primarily built using synchronous PHP code.

Request for Guidance

Considering the complications in transitioning a substantial synchronous code base to asynchronous patterns overnight, is there a strategy, pattern, or workaround that can be employed to integrate blocking PHP code within the MadelineProto event handlers?

Can the event loop be paused to accommodate the execution of blocking code, or is it possible to run blocking code in a separate process that communicates with MadelineProto asynchronously?

Any guidance on achieving compatibility with blocking resources in a predominantly synchronous PHP application would be greatly appreciated by users who are integrating MadelineProto into existing Symfony-based applications.

Environment Details

danog commented 7 months ago

MadelineProto is an async library, and thus will never support legacy blocking database drivers like PDO, instead use https://github.com/amphp/mysql (in your case, since you were just using PDO, amphp/mysql is the perfect replacement).

Also, since amphp v3 is based on PHP's fibers, they can be very easily integrated with any existing framework, for example with a simple PSR adapter amphp/http-client can be used as backend for guzzle, and so on.

No special await or yield keywords are needed, just write blocking code as usual, replacing underlying primitives like PDO or curl with amphp's libraries.

Additionally, MadelineProto itself also offers an async ORM (https://github.com/danog/MadelineProtoDocs/blob/master/docs/docs/UPDATES.md#built-in-orm), which can come in quite useful in certain circumstances where simple properties (which are always persisted to the database if returned by __sleep) cannot be used, for example because too much data would have to be stored in a single property.

danog commented 7 months ago

Also, since you were mentioning symfony, it seems symfony already uses at least amphp/http-client under the hood if it's installed: https://symfony.com/blog/new-in-symfony-5-1-portable-http-2-implementation (some configuration might be needed to completely disable curl, but that ought to be quite simple)

a-kbv commented 7 months ago

MadelineProto is an async library, and thus will never support legacy blocking database drivers like PDO, instead use https://github.com/amphp/mysql (in your case, since you were just using PDO, amphp/mysql is the perfect replacement).

Also, since amphp v3 is based on PHP's fibers, they can be very easily integrated with any existing framework, for example with a simple PSR adapter amphp/http-client can be used as backend for guzzle, and so on.

No special await or yield keywords are needed, just write blocking code as usual, replacing underlying primitives like PDO or curl with amphp's libraries.

Additionally, MadelineProto itself also offers an async ORM (https://github.com/danog/MadelineProtoDocs/blob/master/docs/docs/UPDATES.md#built-in-orm), which can come in quite useful in certain circumstances where simple properties (which are always persisted to the database if returned by __sleep) cannot be used, for example because too much data would have to be stored in a single property.

Im using Doctrine Orm ...

danog commented 7 months ago

@a-kbv Implementing a doctrine driver based on amphp/mysql is also super simple, actually it seems like it was already implemented: https://github.com/amphp/mysql-dbal

a-kbv commented 7 months ago

Thank You for your patience. I've successfully implemented amp/mysql DBAL Doctrine driver and it looks like it worked. But the thing I needed was dependency injection in the event handler. So I bypassed the static analysis for blocking operations and injected the kernel in the onStart method of the event handler. That way I've got access to the dependency injection container.

    public function onStart()
    {
        $this->kernel = new Kernel($_ENV['APP_ENV'], (bool) $_ENV['APP_DEBUG']);
        $this->kernel->boot();
        $this->messageBus = $this->kernel->getContainer()->get('messenger.default_bus');
    }
danog commented 7 months ago

Dependency injection is not a blocking operation, so it shouldn't require bypassing static analysis, it should work fine OOTB as long as it's precompiled.