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

[8.x] Add phpredis serialization and compression config support #40282

Merged
merged 4 commits into from
Jan 15, 2022

Conversation

TheLevti
Copy link
Contributor

@TheLevti TheLevti commented Jan 6, 2022

This is a follow up from #36791

This pull request allows the framework user to configure phpredis (https://github.com/phpredis/phpredis) serialization and compression options right in the config instead of the need to overwrite the service provider or a custom driver.

Supported serializers:

  • NONE
  • PHP
  • JSON
  • IGBINARY
  • MSGPACK

Supported compressors:

  • NONE
  • LZF
  • ZSTD
  • LZ4

Also added tests for different compression algorithms.

Notes

My first attempt was to always serialize + compress the passed arguments to eval, but then later decided to let the user take care of this as we can not know what kind of lua script he wants to execute and whether all arguments are expected to be serialized/compressed. Instead I provided a helper method on the PhpredisConnection class that the user may use. In addition I refactored the previous phpredis lock support (#36337) for these functionalities to use this new helper method.

@TheLevti TheLevti force-pushed the feature/phpredis-options branch 2 times, most recently from 313f167 to f7428d8 Compare January 6, 2022 22:58
@GrahamCampbell GrahamCampbell changed the title Add phpredis serialization and compression config support [8.x] Add phpredis serialization and compression config support Jan 6, 2022
@it-can
Copy link
Contributor

it-can commented Jan 6, 2022

Does this play well with Horizon?

@TheLevti
Copy link
Contributor Author

TheLevti commented Jan 7, 2022

Does this play well with Horizon?

I have no idea as I never used horizon. This change just makes sure that if phpredis locking is used, we properly pack the passed arguments to eval and in addition adds config support for serialization and compression.

@TheLevti TheLevti force-pushed the feature/phpredis-options branch 2 times, most recently from 5e0b8d6 to 1bf86b6 Compare January 7, 2022 12:37
@taylorotwell
Copy link
Member

Does this have any breaking changes for existing applications, even minor or obscure breaking changes?

@TheLevti
Copy link
Contributor Author

TheLevti commented Jan 9, 2022

Does this have any breaking changes for existing applications, even minor or obscure breaking changes?

The methods that were moved to the connection class are tagged as deprecated, so you can remove them later.

The only breaking change I could think off is when someone had been using the Laravel's redis lock algorithm with a connection that has serialization and/or compression enabled, which just was not working before this pull request anyway (lock would never be released/acquired), so If we had such users of the framework, for them they would receive an exception revealing to them that they are using a serialization/compression algorithm that is not supported or that they are missing a required php extension to properly support redis lock with the given redis connection (in case of an old phpredis version). Before that they would silently not notice that locking is not working.

@taylorotwell taylorotwell merged commit 0dc2d8a into laravel:8.x Jan 15, 2022
@taylorotwell
Copy link
Member

Can you submit a PR to laravel/docs repository that discusses these new configuration options? Thanks!

@TheLevti TheLevti deleted the feature/phpredis-options branch January 15, 2022 18:39
@TheLevti
Copy link
Contributor Author

Can you submit a PR to laravel/docs repository that discusses these new configuration options? Thanks!

laravel/docs#7588

@it-can
Copy link
Contributor

it-can commented Jan 19, 2022

@TheLevti I tested this quickly with Laravel Horizon, but when adding jobs I get this error. When not using compression/serializer it works perfect...
So it seems Horizon is not completely compatible with Redis serializer/compression (I see the values in Redis being serialized though).

database.php

'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'serializer'  => Redis::SERIALIZER_MSGPACK,
            'compression' => Redis::COMPRESSION_LZ4,
        ],

horizon/src/JobPayload.php:48

ErrorException · Trying to access array offset on value of type null
ErrorException Trying to access array offset on value of type null 
    vendor/laravel/horizon/src/JobPayload.php:48 Illuminate\Foundation\Bootstrap\HandleExceptions::handleError
    vendor/laravel/horizon/src/JobPayload.php:48 Laravel\Horizon\JobPayload::id
    vendor/laravel/horizon/src/Repositories/RedisJobRepository.php:428 Laravel\Horizon\Repositories\RedisJobRepository::Laravel\Horizon\Repositories\{closure}
    vendor/laravel/framework/src/Illuminate/Support/helpers.php:263 tap
    vendor/laravel/framework/src/Illuminate/Redis/Connections/PhpRedisConnection.php:408 Illuminate\Redis\Connections\PhpRedisConnection::pipeline
    vendor/laravel/horizon/src/Repositories/RedisJobRepository.php:435 Laravel\Horizon\Repositories\RedisJobRepository::migrated
    vendor/laravel/horizon/src/Listeners/MarkJobsAsMigrated.php:36 Laravel\Horizon\Listeners\MarkJobsAsMigrated::handle
    vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:424 Illuminate\Events\Dispatcher::Illuminate\Events\{closure}
    vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:249 Illuminate\Events\Dispatcher::dispatch
    vendor/laravel/horizon/src/RedisQueue.php:196 Laravel\Horizon\RedisQueue::event
    vendor/laravel/horizon/src/RedisQueue.php:150 Laravel\Horizon\RedisQueue::Laravel\Horizon\{closure}
    vendor/laravel/framework/src/Illuminate/Support/helpers.php:263 tap
    vendor/laravel/horizon/src/RedisQueue.php:151 Laravel\Horizon\RedisQueue::migrateExpiredJobs
    vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php:230 Illuminate\Queue\RedisQueue::migrate
    vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php:210 Illuminate\Queue\RedisQueue::pop
    vendor/laravel/horizon/src/RedisQueue.php:133 Laravel\Horizon\RedisQueue::pop
    vendor/laravel/framework/src/Illuminate/Queue/Worker.php:345 Illuminate\Queue\Worker::Illuminate\Queue\{closure}
    vendor/laravel/framework/src/Illuminate/Queue/Worker.php:354 Illuminate\Queue\Worker::getNextJob
    vendor/laravel/framework/src/Illuminate/Queue/Worker.php:159 Illuminate\Queue\Worker::daemon
    vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:117 Illuminate\Queue\Console\WorkCommand::runWorker
    vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:101 Illuminate\Queue\Console\WorkCommand::handle
    vendor/laravel/horizon/src/Console/WorkCommand.php:51 Laravel\Horizon\Console\WorkCommand::handle
    vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36 Illuminate\Container\BoundMethod::Illuminate\Container\{closure}
    vendor/laravel/framework/src/Illuminate/Container/Util.php:40 Illuminate\Container\Util::unwrapIfClosure
    vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93 Illuminate\Container\BoundMethod::callBoundMethod
    vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37 Illuminate\Container\BoundMethod::call
    vendor/laravel/framework/src/Illuminate/Container/Container.php:653 Illuminate\Container\Container::call
    vendor/laravel/framework/src/Illuminate/Console/Command.php:136 Illuminate\Console\Command::execute
    vendor/symfony/console/Command/Command.php:298 Symfony\Component\Console\Command\Command::run
    vendor/laravel/framework/src/Illuminate/Console/Command.php:121 Illuminate\Console\Command::run
    vendor/symfony/console/Application.php:1005 Symfony\Component\Console\Application::doRunCommand
    vendor/symfony/console/Application.php:299 Symfony\Component\Console\Application::doRun
    vendor/symfony/console/Application.php:171 Symfony\Component\Console\Application::run
    vendor/laravel/framework/src/Illuminate/Console/Application.php:94 Illuminate\Console\Application::run
    vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:129 Illuminate\Foundation\Console\Kernel::handle
    artisan:37 [main]

@TheLevti
Copy link
Contributor Author

TheLevti commented Jan 19, 2022

@TheLevti I tested this quickly with Laravel Horizon, but when adding jobs I get this error. When not using compression/serializer it works perfect... So it seems Horizon is not completely compatible with Redis serializer/compression (I see the values in Redis being serialized though).

database.php

'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'serializer'  => Redis::SERIALIZER_MSGPACK,
            'compression' => Redis::COMPRESSION_LZ4,
        ],

horizon/src/JobPayload.php:48

ErrorException · Trying to access array offset on value of type null
ErrorException Trying to access array offset on value of type null 
    vendor/laravel/horizon/src/JobPayload.php:48 Illuminate\Foundation\Bootstrap\HandleExceptions::handleError
    vendor/laravel/horizon/src/JobPayload.php:48 Laravel\Horizon\JobPayload::id
    vendor/laravel/horizon/src/Repositories/RedisJobRepository.php:428 Laravel\Horizon\Repositories\RedisJobRepository::Laravel\Horizon\Repositories\{closure}
    vendor/laravel/framework/src/Illuminate/Support/helpers.php:263 tap
    vendor/laravel/framework/src/Illuminate/Redis/Connections/PhpRedisConnection.php:408 Illuminate\Redis\Connections\PhpRedisConnection::pipeline
    vendor/laravel/horizon/src/Repositories/RedisJobRepository.php:435 Laravel\Horizon\Repositories\RedisJobRepository::migrated
    vendor/laravel/horizon/src/Listeners/MarkJobsAsMigrated.php:36 Laravel\Horizon\Listeners\MarkJobsAsMigrated::handle
    vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:424 Illuminate\Events\Dispatcher::Illuminate\Events\{closure}
    vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:249 Illuminate\Events\Dispatcher::dispatch
    vendor/laravel/horizon/src/RedisQueue.php:196 Laravel\Horizon\RedisQueue::event
    vendor/laravel/horizon/src/RedisQueue.php:150 Laravel\Horizon\RedisQueue::Laravel\Horizon\{closure}
    vendor/laravel/framework/src/Illuminate/Support/helpers.php:263 tap
    vendor/laravel/horizon/src/RedisQueue.php:151 Laravel\Horizon\RedisQueue::migrateExpiredJobs
    vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php:230 Illuminate\Queue\RedisQueue::migrate
    vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php:210 Illuminate\Queue\RedisQueue::pop
    vendor/laravel/horizon/src/RedisQueue.php:133 Laravel\Horizon\RedisQueue::pop
    vendor/laravel/framework/src/Illuminate/Queue/Worker.php:345 Illuminate\Queue\Worker::Illuminate\Queue\{closure}
    vendor/laravel/framework/src/Illuminate/Queue/Worker.php:354 Illuminate\Queue\Worker::getNextJob
    vendor/laravel/framework/src/Illuminate/Queue/Worker.php:159 Illuminate\Queue\Worker::daemon
    vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:117 Illuminate\Queue\Console\WorkCommand::runWorker
    vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:101 Illuminate\Queue\Console\WorkCommand::handle
    vendor/laravel/horizon/src/Console/WorkCommand.php:51 Laravel\Horizon\Console\WorkCommand::handle
    vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36 Illuminate\Container\BoundMethod::Illuminate\Container\{closure}
    vendor/laravel/framework/src/Illuminate/Container/Util.php:40 Illuminate\Container\Util::unwrapIfClosure
    vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93 Illuminate\Container\BoundMethod::callBoundMethod
    vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37 Illuminate\Container\BoundMethod::call
    vendor/laravel/framework/src/Illuminate/Container/Container.php:653 Illuminate\Container\Container::call
    vendor/laravel/framework/src/Illuminate/Console/Command.php:136 Illuminate\Console\Command::execute
    vendor/symfony/console/Command/Command.php:298 Symfony\Component\Console\Command\Command::run
    vendor/laravel/framework/src/Illuminate/Console/Command.php:121 Illuminate\Console\Command::run
    vendor/symfony/console/Application.php:1005 Symfony\Component\Console\Application::doRunCommand
    vendor/symfony/console/Application.php:299 Symfony\Component\Console\Application::doRun
    vendor/symfony/console/Application.php:171 Symfony\Component\Console\Application::run
    vendor/laravel/framework/src/Illuminate/Console/Application.php:94 Illuminate\Console\Application::run
    vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:129 Illuminate\Foundation\Console\Kernel::handle
    artisan:37 [main]

can you show me more where you put this options? A bigger view of database.php. These options must be set per connection and not outside. Also it depends on how Horizon is using phpredis, what it does.

@it-can
Copy link
Contributor

it-can commented Jan 19, 2022

``
'redis' => [

    'client' => env('REDIS_CLIENT', 'phpredis'),

    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        //'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_db_'),
        'serializer'  => Redis::SERIALIZER_MSGPACK,
        'compression' => Redis::COMPRESSION_LZ4,
    ],

    'default' => [
        'scheme'       => env('REDIS_SCHEME', 'tcp'),
        'url'          => env('REDIS_URL'),
        'host'         => env('REDIS_HOST', '127.0.0.1'),
        'password'     => env('REDIS_PASSWORD', null),
        'port'         => env('REDIS_PORT', '6379'),
        'database'     => env('REDIS_DB', '0'),
        'read_timeout' => 60,
        'prefix'       => 'd:',
    ],
    ];

``

@TheLevti
Copy link
Contributor Author

TheLevti commented Jan 19, 2022

Can you try this?

'redis' => [
    'client' => env('REDIS_CLIENT', 'phpredis'),

    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        //'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_db_'),
    ],

    'default' => [
        'scheme'       => env('REDIS_SCHEME', 'tcp'),
        'url'          => env('REDIS_URL'),
        'host'         => env('REDIS_HOST', '127.0.0.1'),
        'password'     => env('REDIS_PASSWORD', null),
        'port'         => env('REDIS_PORT', '6379'),
        'database'     => env('REDIS_DB', '0'),
        'read_timeout' => 60,
        'prefix'       => 'd:',
        'options' => [
            'serializer'  => Redis::SERIALIZER_MSGPACK,
            'compression' => Redis::COMPRESSION_LZ4,
        ],
    ],
];

@it-can
Copy link
Contributor

it-can commented Jan 19, 2022

Same problem...

@TheLevti
Copy link
Contributor Author

TheLevti commented Jan 19, 2022

Same problem...

Well I don't know what Horizon is doing, maybe same as the redlock algorithm it is using some eval calls, which need to be packed. Can you point me to some classes where it fails?

It looks like Horizon is creating it's own connection by picking some params from the config: https://github.com/laravel/horizon/blob/5.x/src/Connectors/RedisConnector.php

@it-can
Copy link
Contributor

it-can commented Jan 19, 2022

my error

Same problem...

Well I don't know what Horizon is doing, maybe same as the redlock algorithm it is using some eval calls, which need to be packed. Can you point me to some classes where it fails?

It looks like Horizon is creating it's own connection by picking some params from the config: https://github.com/laravel/horizon/blob/5.x/src/Connectors/RedisConnector.php

aah yes that seems to be the "problem"

@TheLevti
Copy link
Contributor Author

TheLevti commented Jan 19, 2022

my error

Same problem...

Well I don't know what Horizon is doing, maybe same as the redlock algorithm it is using some eval calls, which need to be packed. Can you point me to some classes where it fails?
It looks like Horizon is creating it's own connection by picking some params from the config: https://github.com/laravel/horizon/blob/5.x/src/Connectors/RedisConnector.php

aah yes that seems to be the "problem"

Also https://github.com/laravel/horizon/blob/5.x/src/LuaScripts.php those script calls need to pack their arguments for commands where phpredis packs them.

Some connection adjustments and additional tests with different serializer/compression setups should solve the issue.

@ReDev1L
Copy link

ReDev1L commented Jan 20, 2022

#30004

Thank you, finally

@reziamini
Copy link
Contributor

@TheLevti @taylorotwell

This PR has been merged a long time ago.
But there is a problem with creating the Redis cluster.
These two constants RedisCluster::OPT_COMPRESSION and RedisCluster::OPT_COMPRESSION_LEVEL which have been used for creating the cluster, have not been defined at all!

@TheLevti
Copy link
Contributor Author

@TheLevti @taylorotwell

This PR has been merged a long time ago. But there is a problem with creating the Redis cluster. These two constants RedisCluster::OPT_COMPRESSION and RedisCluster::OPT_COMPRESSION_LEVEL which have been used for creating the cluster, have not been defined at all!

Are you maybe using an outdated phpredis extension?

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.

5 participants