laravel-swoole icon indicating copy to clipboard operation
laravel-swoole copied to clipboard

Any way to share same singletone instance between requests?

Open ddzobov opened this issue 4 years ago • 15 comments

Hello!

Anyone knows how to share same singletone instance between requests in same thread with laravel AppServiceProvider?

Now i’m saving singletone instance into static class property but for me it is very dirty way.

ddzobov avatar Apr 12 '20 08:04 ddzobov

Well, I imagine that you're manually cleaning your singleton instance at every request isn't it? As each request runs as Coroutine I think you could use Channels.

Table could be a way as well imo.

Arkanius avatar Oct 01 '20 21:10 Arkanius

I have instance of AMQP connector, i want to use single connection from all requests in current thread.

public function register()
    {
        $this->app->singleton(AMQP::class, function ($app) {
            return new AMQP($host, $port, $username, $password, $exchange, $queue, $queueType, $prefetchCount);
        });

        $this->app->alias(AMQP::class, 'amqp');
    }

I made simple test - in controller i'm returning spl_object_hash of some instances:

return response()->json([
            'response' => 'ok',
            'instances' => collect(['log', 'amqp', 'view'])->mapWithKeys(function($name){
                return [
                    $name => spl_object_hash(app($name))
                ];
            })
        ], 201);

And made 2 requests:

{
    "response": "ok",
    "instances": {
        "log": "000000001e6bf27d0000000054e3eb80",
        "amqp": "000000001e6bf23f0000000054e3eb80",
        "view": "000000001e6bf2010000000054e3eb80"
    }
}
{
    "response": "ok",
    "instances": {
        "log": "000000001e6bf27d0000000054e3eb80",
        "amqp": "000000001e6bf1ff0000000054e3eb80",
        "view": "000000001e6bf1f30000000054e3eb80"
    }
}

As you can see, log instance unchanged between requests, but view and amqp have different signatures.

Now i have only one solution - save instance into static AMQP property and return instance from this property in provider, but this is very dirty way.

ddzobov avatar Oct 06 '20 09:10 ddzobov

Well, this looks weird to me. This is exactly the behavior that swoole would provide to you. I did some tests here as well:

Request 1:

"Log: 0000000023b4852d000000001920f5f7"
"View: 0000000023b4839a000000001920f5f7"
"Auth: 0000000023b483d6000000001920f5f7"
"Custom: 0000000023b483d4000000001920f5f7"

Request 2:

"Log: 0000000023b4852d000000001920f5f7"
"View: 0000000023b4839a000000001920f5f7"
"Auth: 0000000023b483d6000000001920f5f7"
"Custom: 0000000023b483d4000000001920f5f7"

Have you setted the SWOOLE_HTTP_MAX_REQUESTS env?

Arkanius avatar Oct 06 '20 15:10 Arkanius

I dont see SWOOLE_HTTP_MAX_REQUESTS env

i'm using SWOOLE_MAX_REQUEST=100

When i'm setting instance to static class property, it have same signature at every request. Probably, something wrong with sandbox...

I'm using Lumen in this project, maybe problems only with it? "laravel/lumen-framework": "^7.0",

ddzobov avatar Oct 06 '20 18:10 ddzobov

Hum, maybe...

I'll run some tests with lumen to check it

Arkanius avatar Oct 06 '20 20:10 Arkanius

any updates here?

ddzobov avatar Oct 25 '20 21:10 ddzobov

Not yet, sorry

Arkanius avatar Oct 26 '20 01:10 Arkanius

ping

ddzobov avatar Nov 12 '20 20:11 ddzobov

Sorry for the late reply. I'm able to reproduce this behavior with Lumen but not with Laravel. I think it's any particularity with Lumen. I'm looking toward it at this moment

Arkanius avatar Nov 13 '20 17:11 Arkanius

Hey @albertcht sorry to bother you. Could you try to give us some light here? I reproduced this and it's happening only with lumen. I tried to debug Sandbox but couldn't find anything. I think that it's happening because lumen misses some of laravel kernel's components.

I'm not sure about which place I can find this

Arkanius avatar Nov 21 '20 04:11 Arkanius

Hi @ddzobov and @Arkanius ,

Well, basically you can share any singleton instances in the same worker. Swoole HTTP server generated several workers for incoming requests according to your settings. Requests dispatched to the same worker will share any global variables including static properties.

In your case, set your worker number to 1 first to make sure all the requests are handled by the same worker process. And make sure your ampq instance is set in pre_resolved in swoole_http.php config. Otherwise all the resolved singleton instances will be cleared every time the request initialized.

You can refer to: https://github.com/swooletw/laravel-swoole/wiki/2.-Swoole-Structure#sandbox-container

albertcht avatar Nov 22 '20 05:11 albertcht

Hi @ddzobov and @Arkanius ,

Well, basically you can share any singleton instances in the same worker. Swoole HTTP server generated several workers for incoming requests according to your settings. Requests dispatched to the same worker will share any global variables including static properties.

In your case, set your worker number to 1 first to make sure all the requests are handled by the same worker process. And make sure your ampq instance is set in pre_resolved in swoole_http.php config. Otherwise all the resolved singleton instances will be cleared every time the request initialized.

You can refer to: https://github.com/swooletw/laravel-swoole/wiki/2.-Swoole-Structure#sandbox-container

Just repeated this test with SWOOLE_HTTP_WORKER_NUM=1 and SWOOLE_HTTP_REACTOR_NUM=1

'pre_resolved' => [
        'view', 'files', 'session', 'session.store', 'routes',
        'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie',
        'encrypter', 'hash', 'router', 'translator', 'url', 'log', 'amqp',
    ],
$this->app->singleton(AMQP::class, function ($app) {
            return new AMQP(
                config('amqp.host'),
                config('amqp.port'),
                config('amqp.username'),
                config('amqp.password'),
                config('amqp.exchange'),
                config('amqp.queue'),
                config('amqp.queueType'),
                config('amqp.prefetchCount'),
                config('amqp.publishRetries'),
                config('amqp.connectionTimeout'),
                config('amqp.readWriteTimeout'),
                config('amqp.heartbeat')
            );
        });
return response()->json([
            'response' => 'ok',
            'instances' => collect(['log', 'amqp', 'view'])->mapWithKeys(function($name){
                return [
                    $name => spl_object_hash(app($name))
                ];
            })
        ], 201);
{
    "response": "ok",
    "instances": {
        "log": "00000000622e85b6000000004399d6c2",
        "amqp": "00000000622e80dd000000004399d6c2",
        "view": "00000000622e80c2000000004399d6c2"
    }
}
{
    "response": "ok",
    "instances": {
        "log": "00000000622e85b6000000004399d6c2",
        "amqp": "00000000622e8104000000004399d6c2",
        "view": "00000000622e8109000000004399d6c2"
    }
}
{
    "response": "ok",
    "instances": {
        "log": "00000000622e85b6000000004399d6c2",
        "amqp": "00000000622e814b000000004399d6c2",
        "view": "00000000622e8170000000004399d6c2"
    }
}

As you can see, only log instance have same signature. amqp and view changes every request.

ddzobov avatar Nov 22 '20 09:11 ddzobov

Yes, I reproduced your test and it's happening with Lumen. I tested with Laravel and is everything ok. I haven't found any problem with Sadbox, my guess is some problem with Container app binding

Arkanius avatar Nov 23 '20 02:11 Arkanius

Any updates here?

ddzobov avatar Mar 05 '21 19:03 ddzobov

Sorry, could reproduce but didn't find the bug at lumen yet =/

Arkanius avatar Mar 05 '21 23:03 Arkanius