Skip to content

Commit

Permalink
[v2.x] Relay support (#1263)
Browse files Browse the repository at this point in the history
  • Loading branch information
tillkruss committed May 9, 2023
1 parent 538c659 commit 946c4b7
Show file tree
Hide file tree
Showing 130 changed files with 2,037 additions and 205 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/CONTRIBUTING.md export-ignore linguist-documentation
/FAQ.md export-ignore linguist-documentation
/VERSION export-ignore
/phpunit.relay.xml export-ignore
/phpunit.xml.dist export-ignore
/phpstan.dist.neon export-ignore
/phpstan-tests.dist.neon export-ignore
8 changes: 6 additions & 2 deletions .github/workflows/stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: relay

- name: Get Composer cache directory
id: composer-cache
Expand All @@ -58,5 +59,8 @@ jobs:
PHP_VERSION: ${{ matrix.php }}
run: composer install --ansi --no-progress --prefer-dist

- name: Run PHPUnit tests
run: vendor/bin/phpunit
- name: Run tests
run: vendor/bin/phpunit --group realm-stack

- name: Run tests using Relay
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml
15 changes: 10 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ concurrency:
jobs:

predis:
name: PHP ${{ matrix.php }} Redis ${{ matrix.redis }}
name: PHP ${{ matrix.php }} (Redis ${{ matrix.redis }})
runs-on: ubuntu-latest

strategy:
Expand Down Expand Up @@ -50,6 +50,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: relay
coverage: ${{ (matrix.php == '8.1' && matrix.redis == '7') && 'xdebug' || 'none' }}

- name: Install Composer dependencies
Expand All @@ -58,13 +59,17 @@ jobs:
dependency-versions: highest
composer-options: ${{ matrix.php == '8.0' && '--ignore-platform-reqs' || '' }}

- name: Run PHPUnit tests
- name: Run tests
if: ${{ matrix.php != '8.1' || matrix.redis != '7' }}
run: vendor/bin/phpunit --verbose --exclude-group realm-stack
run: vendor/bin/phpunit

- name: Run PHPUnit tests with coverage
- name: Run tests with coverage
if: ${{ matrix.php == '8.1' && matrix.redis == '7' }}
run: vendor/bin/phpunit --verbose --exclude-group realm-stack --coverage-clover build/logs/clover.xml --coverage-filter ./src
run: vendor/bin/phpunit --coverage-clover build/logs/clover.xml --coverage-filter ./src

- name: Run tests using Relay
if: ${{ matrix.redis >= '6' }}
run: vendor/bin/phpunit -c phpunit.relay.xml

- name: Send coverage to Coveralls
env:
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
## Unreleased

### Added
- Added support for `ACL SETUSER, GETUSER, DRYRUN` commands
- Added support for [Relay](https://github.com/predis/predis/wiki/Using-Relay) (#1263)
- Added support for `FCALL_RO` command
- Added support for `Redis JSON` module
- Added support for `Redis Bloom` module
- Added support for `Redis Search` module
- Added support for `Redis TimeSeries` module
- Added support for `ACL SETUSER, GETUSER, DRYRUN` commands

### Fixed
- Fixed prefixes for `XTRIM` and `XREVRANGE` commands
Expand Down
96 changes: 32 additions & 64 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Some frequently asked questions about Predis #
________________________________________________
# Frequently asked questions about Predis #

### What is the point of Predis? ###
## What is the point of Predis? ##

The main point of Predis is about offering a highly customizable and extensible client for Redis,
that can be easily extended by developers while still being reasonably fast. With Predis you can
Expand All @@ -13,29 +12,34 @@ a great asset since it allows developers to add new and still missing features o
the standard behaviour of the library without the need to break dependencies in production code (at
least to some degree).

### Does Predis support UNIX domain sockets and persistent connections? ###
## Does Predis support UNIX domain sockets and persistent connections? ##

Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).

### Does Predis support SSL-encrypted connections? ###
## Does Predis support SSL-encrypted connections? ##

Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various
cloud hosting providers without the need to configure an SSL proxy, but you should also take into
account the general performances degradation especially during the connect() operation when the TLS
handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help
in that respect, but they are supported only when running on PHP >= 7.0.0.

### Does Predis support transparent (de)serialization of values? ###
## Does Predis support transparent (de)serialization of values? ##

No and it will not ever do that by default. The reason behind this decision is that serialization is
usually something that developers prefer to customize depending on their needs and can not be easily
generalized when using Redis because of the many possible access patterns for your data. This does
not mean that it is impossible to have such a feature since you can leverage the extensibility of
this library to define your own serialization-aware commands. You can find more details about how to
do that [on this issue](http://github.com/predis/predis/issues/29#issuecomment-1202624).
When using [Relay](https://github.com/cachewerk/relay) as the underlying client, several
serialization and compression algorithms are supported. This slightly increases CPU usage,
but significantly reduces bytes sent over the network and Redis memory usage.

### How can I force Predis to connect to Redis before sending any command? ###
Without Relay, Predis will not serialize data and will never do that by default. The reason
behind this decision is that serialization is usually something that developers prefer to
customize depending on their needs and can not be easilygeneralized when using Redis because
of the many possible access patterns for your data. This does not mean that it is impossible
to have such a feature since you can leverage the extensibility of this library to define
your own serialization-aware commands. You can find more details about how to do that
[on this issue](http://github.com/predis/predis/issues/29#issuecomment-1202624).

## How can I force Predis to connect to Redis before sending any command? ##

Explicitly connecting to Redis is usually not needed since the client initializes connections lazily
only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when
Expand All @@ -55,7 +59,7 @@ try {
$client->info();
```

### How Predis abstracts Redis commands? ###
## How Predis abstracts Redis commands? ##

The approach used to implement Redis commands is quite simple: by default each command follows the
same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things
Expand All @@ -75,12 +79,20 @@ $client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single n
An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed
[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75).

## When should I use Relay? ##

If you care about performance, __always__. [Relay](https://github.com/cachewerk/relay) is free to use.

## When should I use PhpRedis? ###

# Speaking about performances... #
_________________________________________________
Predis is fast enough when Redis is located on the same machine as PHP, more on that later.

[PhpRedis](https://github.com/phpredis/phpredis) (and Relay) perform significantly better when
network I/O is involved, due to their ability to compress data by ~75%. Fewer bytes and received
sent over the network [means faster operations](https://akalongman.medium.com/phpredis-vs-predis-comparison-on-real-production-data-a819b48cbadb),
and potentially cost savings when network traffic isn't free (e.g. AWS Elasticache Inter-AZ transfer costs).

### Predis is a pure-PHP implementation: it can not be fast enough! ###
## Predis is a pure-PHP implementation: it can not be fast enough! ##

It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a
couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6
Expand All @@ -92,7 +104,7 @@ against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6
0.130 seconds to fetch 30000 keys using _KEYS *_.
```

How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension
How does it compare with [__PhpRedis__](http://github.com/phpredis/phpredis), a nice C extension
providing an efficient client for Redis?

```
Expand All @@ -101,7 +113,7 @@ providing an efficient client for Redis?
0.035 seconds to fetch 30000 keys using "KEYS *"".
```

Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
Wow __PhpRedis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are
going to use Redis in your application? Are you really going to send thousands of commands using a
for-loop on each page request using a single client instance? If so... well I guess you are probably
Expand All @@ -119,7 +131,7 @@ Using Predis:
3200 GET/sec while retrieving the very same values
0.132 seconds to fetch 30000 keys using "KEYS *".
Using phpredis:
Using PhpRedis:
3500 SET/sec using 12 bytes for both key and value
3500 GET/sec while retrieving the very same values
0.045 seconds to fetch 30000 keys using "KEYS *".
Expand All @@ -131,47 +143,3 @@ that we are measuring the overhead of client libraries implementations and the e
round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands
of concurrent clients doing requests! Also, actual performances should be measured according to how
your application will use Redis.

### I am convinced, but performances for multi-bulk responses are still worse ###

Fair enough, but there is an option available if you need even more speed and consists on installing
__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the
client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client
library for Redis) with a thin layer exposing its features to PHP. You can then choose between two
different connection classes:

- `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams).
- `Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`).

You will now get the benefits of a faster protocol serializer and parser just by adding a couple of
lines of code:

```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisStreamConnection',
'unix' => 'Predis\Connection\PhpiredisSocketConnection',
),
));
```

Dead simple. Nothing changes in the way you use the library in your application. So how fast is it
our basic benchmark script now? There are not much improvements for inline or short bulk responses
like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on
par with phpredis:

```
Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis::
0.035 seconds from a local Redis instance
0.047 seconds from a remote Redis instance
```

### If I need an extension to get better performances, why not using phpredis? ###

Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and
you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
do not need any kind of extensibility or guaranteed backwards compatibility with different versions
of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then
using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding
__phpiredis__ you can get a nice speed bump almost for free.
27 changes: 10 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,16 @@ $response = $client->lpushrand('random_values', $seed = mt_rand());

### Customizable connection backends ###

Predis can use different connection backends to connect to Redis. The builtin Relay integration
leverages the [Relay](https://github.com/cachewerk/relay) extension for PHP for major performance
gains, by caching a partial replica of the Redis dataset in PHP shared runtime memory.

```php
$client = new Predis\Client('tcp://127.0.0.1', [
'connections' => 'relay',
]);
```

Developers can create their own connection classes to support whole new network backends, extend
existing classes or provide completely different implementations. Connection classes must implement
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
Expand Down Expand Up @@ -439,23 +449,6 @@ be disabled. See [the tests README](tests/README.md) for more details about test
Predis uses GitHub Actions for continuous integration and the history for past and current builds can be
found [on its actions page](https://github.com/predis/predis/actions).


## Other ##


### Project related links ###

- [Source code](https://github.com/predis/predis)
- [Wiki](https://github.com/predis/predis/wiki)
- [Issue tracker](https://github.com/predis/predis/issues)


### Author ###

- [Till Krüss](https://till.im) ([Twitter](http://twitter.com/tillkruss))
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))


### License ###

The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
Expand Down
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^8.0 || ~9.4.4"
},
"suggest": {
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
},
"scripts": {
"phpstan": "phpstan analyse",
"style": "php-cs-fixer fix --diff --dry-run",
Expand Down
6 changes: 3 additions & 3 deletions examples/pubsub_consumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
// Subscribe to your channels
$pubsub->subscribe('control_channel', 'notifications');

// Start processing the pubsup messages. Open a terminal and use redis-cli
// Start processing the pubsub messages. Open a terminal and use redis-cli
// to push messages to the channels. Examples:
// ./redis-cli PUBLISH notifications "this is a test"
// ./redis-cli PUBLISH control_channel quit_loop
// redis-cli PUBLISH notifications "this is a test"
// redis-cli PUBLISH control_channel quit_loop
foreach ($pubsub as $message) {
switch ($message->kind) {
case 'subscribe':
Expand Down
51 changes: 51 additions & 0 deletions examples/relay_compression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

require __DIR__ . '/shared.php';

// Enable igbinary serializer as well as LZ4 compression
$options = [
'serializer' => 'igbinary',
'compression' => 'lz4',
];

$client = new Predis\Client($single_server + $options, [
'connections' => 'relay',
]);

$quote = (object) [
'author' => 'Jean-Luc Picard',
'text' => 'I look forward to your report Mr. Broccoli.',
];

// Serialize object and apply LZ4 compression, then write key to Redis
$client->set('quote', $client->pack($quote));

// NOTE: In Predis v3.x serialization and compression will happen
// automatically without the need to call `pack()` and `unpack()`

// Retrieve raw binary value from Redis
$raw = $client->get('quote');

// Decompress and unserialize binary value
$data = $client->unpack($raw);

var_dump($quote == $data); // true

var_dump($data);

/*
object(stdClass)#11 (2) {
["author"]=>string(15) "Jean-Luc Picard"
["text"]=>string(43) "I look forward to your report Mr. Broccoli."
}
*/
Loading

0 comments on commit 946c4b7

Please sign in to comment.