Skip to content

Commit 56e33a5

Browse files
committed
Merge new param with connection_persistent
1 parent fc6d65c commit 56e33a5

File tree

7 files changed

+212
-42
lines changed

7 files changed

+212
-42
lines changed

docs/README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,12 @@ It also works for the Phpredis Cluster mode.
173173

174174
### Persistent Connections ###
175175

176-
When using persistent connections, you may need to provide a unique identifier to ensure that different clients don't share the same socket connection. This is especially important when operating multiple clients within the same application that communicate with the same Redis server.
176+
When using persistent connections, you can provide a unique identifier to ensure that different clients don't share the same socket connection. This is especially important when operating multiple clients within the same application that communicate with the same Redis server.
177+
178+
The `connection_persistent` option accepts either a boolean or string value:
179+
- `true`: Enable persistent connections using the client alias as the connection ID
180+
- `false`: Disable persistent connections (default)
181+
- `string`: Enable persistent connections using the provided string as the connection ID
177182

178183
``` yaml
179184
snc_redis:
@@ -183,22 +188,26 @@ snc_redis:
183188
alias: app_cache
184189
dsn: redis://localhost/0
185190
options:
186-
connection_persistent: true
187-
connection_persistent_id: "app_cache_connection"
191+
connection_persistent: "app_cache_connection" # Custom persistent ID
188192
session_store:
189193
type: predis # or phpredis
190194
alias: session_store
191195
dsn: redis://localhost/1
192196
options:
193-
connection_persistent: true
194-
connection_persistent_id: "session_store_connection"
197+
connection_persistent: true # Uses alias as persistent ID
198+
simple_cache:
199+
type: phpredis
200+
alias: simple_cache
201+
dsn: redis://localhost/2
202+
options:
203+
connection_persistent: false # No persistent connection
195204
```
196205

197-
**Why this matters:** Without `connection_persistent_id`, two clients connecting to different databases on the same Redis server would share the same persistent socket, leading to unexpected behavior where commands from one client could affect the database context of another.
206+
**Why this matters:** Without a unique persistent connection ID, two clients connecting to different databases on the same Redis server would share the same persistent socket, leading to unexpected behavior where commands from one client could affect the database context of another.
198207

199-
**For phpredis clients:** The `connection_persistent_id` is passed directly to the `pconnect()` function as the persistent ID parameter.
208+
**For phpredis clients:** The persistent ID is passed directly to the `pconnect()` function as the persistent ID parameter.
200209

201-
**For predis clients:** The `connection_persistent_id` is mapped to the `conn_uid` connection option (requires predis/predis version 2.4.0 or higher). This ensures each client creates its own socket so the connection context won't be shared across clients.
210+
**For predis clients:** The persistent ID is mapped to the `conn_uid` connection option (requires predis/predis version 2.4.0 or higher). This ensures each client creates its own socket so the connection context won't be shared across clients.
202211

203212
### Sessions ###
204213

@@ -313,7 +322,6 @@ snc_redis:
313322
prefix: foo
314323
connection_timeout: 10
315324
connection_persistent: true
316-
connection_persistent_id: "cluster_connection"
317325
read_write_timeout: 30
318326
iterable_multibulk: false
319327
throw_errors: true

src/DependencyInjection/Configuration/Configuration.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
use Symfony\Component\Config\Definition\ConfigurationInterface;
3030

3131
use function class_exists;
32+
use function is_bool;
3233
use function is_iterable;
34+
use function is_string;
3335
use function trigger_deprecation;
3436

3537
class Configuration implements ConfigurationInterface
@@ -124,8 +126,13 @@ private function addClientsSection(ArrayNodeDefinition $rootNode): void
124126
->scalarPrototype()->end()
125127
->end()
126128
->booleanNode('connection_async')->defaultFalse()->end()
127-
->booleanNode('connection_persistent')->defaultFalse()->end()
128-
->scalarNode('connection_persistent_id')->defaultNull()->end()
129+
->variableNode('connection_persistent')
130+
->defaultFalse()
131+
->validate()
132+
->ifTrue(static fn ($v) => !(is_bool($v) || (is_string($v) && $v !== '')))
133+
->thenInvalid('connection_persistent must be a boolean or string')
134+
->end()
135+
->end()
129136
->scalarNode('connection_timeout')->cannotBeEmpty()->defaultValue(5)->end()
130137
->scalarNode('read_write_timeout')->defaultNull()->end()
131138
->booleanNode('iterable_multibulk')->defaultFalse()->end()

src/DependencyInjection/SncRedisExtension.php

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use function assert;
3737
use function class_exists;
3838
use function count;
39+
use function is_string;
3940
use function sprintf;
4041
use function version_compare;
4142

@@ -137,29 +138,31 @@ private function loadPredisClient(array $client, ContainerBuilder $container): v
137138
// predis connection parameters have been renamed in v0.8
138139
$client['options']['async_connect'] = $client['options']['connection_async'];
139140
$client['options']['timeout'] = $client['options']['connection_timeout'];
140-
$client['options']['persistent'] = $client['options']['connection_persistent'];
141-
$client['options']['exceptions'] = $client['options']['throw_errors'];
142-
// fix ssl configuration key name
143-
$client['options']['ssl'] = $client['options']['parameters']['ssl_context'] ?? [];
144-
145-
// Handle connection_persistent_id for predis conn_uid (requires predis >= 2.4.0)
146-
if (isset($client['options']['connection_persistent_id'])) {
141+
// Handle connection_persistent as bool|string
142+
if (is_string($client['options']['connection_persistent'])) {
143+
$client['options']['persistent'] = true;
144+
// For predis, use the string value as conn_uid (requires predis >= 2.4.0)
147145
if (class_exists('Predis\Client')) {
148146
if (!version_compare(Client::VERSION, '2.4.0', '>=')) {
149147
throw new InvalidConfigurationException(
150-
'The connection_persistent_id parameter for Predis requires predis/predis version 2.4.0 or higher. ' .
148+
'Using connection_persistent as string for Predis requires predis/predis version 2.4.0 or higher. ' .
151149
sprintf('Current version: %s', Client::VERSION),
152150
);
153151
}
154152

155-
$client['options']['conn_uid'] = $client['options']['connection_persistent_id'];
153+
$client['options']['conn_uid'] = $client['options']['connection_persistent'];
156154
}
155+
} else {
156+
$client['options']['persistent'] = $client['options']['connection_persistent'];
157157
}
158158

159+
$client['options']['exceptions'] = $client['options']['throw_errors'];
160+
// fix ssl configuration key name
161+
$client['options']['ssl'] = $client['options']['parameters']['ssl_context'] ?? [];
162+
159163
unset($client['options']['connection_async']);
160164
unset($client['options']['connection_timeout']);
161165
unset($client['options']['connection_persistent']);
162-
unset($client['options']['connection_persistent_id']);
163166
unset($client['options']['throw_errors']);
164167
unset($client['options']['parameters']['ssl_context']);
165168

src/Factory/PhpredisClientFactory.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use function in_array;
3333
use function is_a;
3434
use function is_array;
35+
use function is_string;
3536
use function phpversion;
3637
use function spl_autoload_register;
3738
use function sprintf;
@@ -111,9 +112,9 @@ public function create(string $class, array $dsns, array $options, string $alias
111112
}
112113

113114
/**
114-
* @param class-string $class
115-
* @param list<RedisDsn> $dsns
116-
* @param array{service: ?string, connection_persistent: ?bool, connection_persistent_id: ?string, connection_timeout: ?string, read_write_timeout: ?string, parameters: array{sentinel_username: string, sentinel_password: string}} $options
115+
* @param class-string $class
116+
* @param list<RedisDsn> $dsns
117+
* @param array{service: ?string, connection_persistent: bool|non-empty-string|null, connection_timeout: ?string, read_write_timeout: ?string, parameters: array{sentinel_username: string, sentinel_password: string}} $options
117118
*
118119
* @return Redis|Relay
119120
*/
@@ -125,7 +126,9 @@ private function createClientFromSentinel(string $class, array $dsns, string $al
125126
$connectionTimeout = $options['connection_timeout'] ?? 0;
126127
$connectionPersistent = null;
127128
if ($options['connection_persistent']) {
128-
$connectionPersistent = $options['connection_persistent_id'] ?? $masterName;
129+
$connectionPersistent = is_string($options['connection_persistent'])
130+
? $options['connection_persistent']
131+
: $masterName;
129132
}
130133

131134
$readTimeout = $options['read_write_timeout'] ?? 0;
@@ -260,7 +263,9 @@ private function createClient(RedisDsn $dsn, string $class, string $alias, array
260263

261264
$persistentId = null;
262265
if (!empty($options['connection_persistent'])) {
263-
$persistentId = $options['connection_persistent_id'] ?? $dsn->getPersistentId();
266+
$persistentId = is_string($options['connection_persistent'])
267+
? $options['connection_persistent']
268+
: $alias;
264269
}
265270

266271
$connectParameters = [

tests/DependencyInjection/SncRedisExtensionEnvTest.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ public function testPhpredisDefaultParameterConfig(string $config, string $class
9696
[
9797
'connection_async' => false,
9898
'connection_persistent' => false,
99-
'connection_persistent_id' => null,
10099
'connection_timeout' => 5,
101100
'read_write_timeout' => null,
102101
'iterable_multibulk' => false,
@@ -140,7 +139,6 @@ public function testPhpredisFullConfig(): void
140139
'logging' => false,
141140
],
142141
'connection_async' => false,
143-
'connection_persistent_id' => null,
144142
'read_write_timeout' => null,
145143
'iterable_multibulk' => false,
146144
'throw_errors' => true,
@@ -168,7 +166,6 @@ public function testPhpredisWithAclConfig(): void
168166
'cluster' => null,
169167
'connection_async' => false,
170168
'connection_persistent' => true,
171-
'connection_persistent_id' => null,
172169
'connection_timeout' => 10,
173170
'iterable_multibulk' => false,
174171
'parameters' => [
@@ -222,7 +219,6 @@ public function testPhpRedisClusterOption(): void
222219
'cluster' => true,
223220
'connection_async' => false,
224221
'connection_persistent' => false,
225-
'connection_persistent_id' => null,
226222
'connection_timeout' => 5,
227223
'read_write_timeout' => null,
228224
'iterable_multibulk' => false,
@@ -252,7 +248,6 @@ public function testPhpRedisSentinelOption(): void
252248
'service' => 'mymaster',
253249
'connection_async' => false,
254250
'connection_persistent' => false,
255-
'connection_persistent_id' => null,
256251
'connection_timeout' => 5,
257252
'read_write_timeout' => null,
258253
'iterable_multibulk' => false,
@@ -284,7 +279,6 @@ public function testPhpRedisClusterOptionMultipleDsn(): void
284279
'connection_timeout' => 1.5,
285280
'connection_persistent' => true,
286281
'connection_async' => false,
287-
'connection_persistent_id' => null,
288282
'iterable_multibulk' => false,
289283
'throw_errors' => true,
290284
'serialization' => 'default',

tests/DependencyInjection/SncRedisExtensionTest.php

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -761,26 +761,47 @@ private function getPhpRedisWithInvalidACLYamlMinimalConfig(): string
761761
YAML;
762762
}
763763

764-
public function testPredisWithConnectionPersistentId(): void
764+
public function testPredisWithConnectionPersistentBool(): void
765765
{
766766
if (!class_exists('Predis\Client')) {
767767
$this->markTestSkipped('Predis not available');
768768
}
769769

770770
if (version_compare(Client::VERSION, '2.4.0', '<')) {
771-
$this->markTestSkipped('Predis version 2.4.0 or higher required for connection_persistent_id');
771+
$this->markTestSkipped('Predis version 2.4.0 or higher required for connection_persistent');
772772
}
773773

774774
$extension = new SncRedisExtension();
775-
$config = $this->parseYaml($this->getPredisWithConnectionPersistentIdYamlConfig());
775+
$config = $this->parseYaml($this->getPredisWithConnectionPersistentBoolYamlConfig());
776776
$extension->load([$config], $container = $this->getContainer());
777777

778778
$this->assertTrue($container->hasDefinition('snc_redis.default'));
779779
$definition = $container->getDefinition('snc_redis.connection.default_parameters.default');
780+
$this->assertTrue($definition->getArgument(0)['persistent']);
781+
$this->assertNull($definition->getArgument(0)['conn_uid']);
782+
}
783+
784+
public function testPredisWithConnectionPersistentString(): void
785+
{
786+
if (!class_exists('Predis\Client')) {
787+
$this->markTestSkipped('Predis not available');
788+
}
789+
790+
if (version_compare(Client::VERSION, '2.4.0', '<')) {
791+
$this->markTestSkipped('Predis version 2.4.0 or higher required for connection_persistent');
792+
}
793+
794+
$extension = new SncRedisExtension();
795+
$config = $this->parseYaml($this->getPredisWithConnectionPersistentStringYamlConfig());
796+
$extension->load([$config], $container = $this->getContainer());
797+
798+
$this->assertTrue($container->hasDefinition('snc_redis.default'));
799+
$definition = $container->getDefinition('snc_redis.connection.default_parameters.default');
800+
$this->assertTrue($definition->getArgument(0)['persistent']);
780801
$this->assertSame('my_custom_conn_uid', $definition->getArgument(0)['conn_uid']);
781802
}
782803

783-
public function testPredisWithConnectionPersistentIdVersionTooOld(): void
804+
public function testPredisWithConnectionPersistentVersionTooOld(): void
784805
{
785806
if (!class_exists('Predis\Client')) {
786807
$this->markTestSkipped('Predis not available');
@@ -791,14 +812,14 @@ public function testPredisWithConnectionPersistentIdVersionTooOld(): void
791812
}
792813

793814
$this->expectException(InvalidConfigurationException::class);
794-
$this->expectExceptionMessage('The connection_persistent_id parameter for Predis requires predis/predis version 2.4.0 or higher');
815+
$this->expectExceptionMessage('Using connection_persistent as string for Predis requires predis/predis version 2.4.0 or higher');
795816

796817
$extension = new SncRedisExtension();
797-
$config = $this->parseYaml($this->getPredisWithConnectionPersistentIdYamlConfig());
818+
$config = $this->parseYaml($this->getPredisWithConnectionPersistentStringYamlConfig());
798819
$extension->load([$config], $container = $this->getContainer());
799820
}
800821

801-
private function getPredisWithConnectionPersistentIdYamlConfig(): string
822+
private function getPredisWithConnectionPersistentBoolYamlConfig(): string
802823
{
803824
return <<<'YAML'
804825
clients:
@@ -808,7 +829,102 @@ private function getPredisWithConnectionPersistentIdYamlConfig(): string
808829
dsn: redis://localhost:6379/0
809830
options:
810831
connection_persistent: true
811-
connection_persistent_id: my_custom_conn_uid
832+
833+
YAML;
834+
}
835+
836+
private function getPredisWithConnectionPersistentStringYamlConfig(): string
837+
{
838+
return <<<'YAML'
839+
clients:
840+
default:
841+
type: predis
842+
alias: default
843+
dsn: redis://localhost:6379/0
844+
options:
845+
connection_persistent: my_custom_conn_uid
846+
847+
YAML;
848+
}
849+
850+
public function testPredisWithConnectionPersistentFalse(): void
851+
{
852+
if (!class_exists('Predis\Client')) {
853+
$this->markTestSkipped('Predis not available');
854+
}
855+
856+
if (version_compare(Client::VERSION, '2.4.0', '<')) {
857+
$this->markTestSkipped('Predis version 2.4.0 or higher required for connection_persistent');
858+
}
859+
860+
$extension = new SncRedisExtension();
861+
$config = $this->parseYaml($this->getPredisWithConnectionPersistentFalseYamlConfig());
862+
$extension->load([$config], $container = $this->getContainer());
863+
864+
$this->assertTrue($container->hasDefinition('snc_redis.default'));
865+
$definition = $container->getDefinition('snc_redis.connection.default_parameters.default');
866+
$this->assertFalse($definition->getArgument(0)['persistent']);
867+
$this->assertNull($definition->getArgument(0)['conn_uid']);
868+
}
869+
870+
private function getPredisWithConnectionPersistentFalseYamlConfig(): string
871+
{
872+
return <<<'YAML'
873+
clients:
874+
default:
875+
type: predis
876+
alias: default
877+
dsn: redis://localhost:6379/0
878+
options:
879+
connection_persistent: false
880+
881+
YAML;
882+
}
883+
884+
public function testInvalidConnectionPersistentValue(): void
885+
{
886+
$this->expectException(InvalidConfigurationException::class);
887+
$this->expectExceptionMessage('connection_persistent must be a boolean or string');
888+
889+
$extension = new SncRedisExtension();
890+
$config = $this->parseYaml($this->getInvalidConnectionPersistentYamlConfig());
891+
$extension->load([$config], $this->getContainer());
892+
}
893+
894+
private function getInvalidConnectionPersistentYamlConfig(): string
895+
{
896+
return <<<'YAML'
897+
clients:
898+
default:
899+
type: predis
900+
alias: default
901+
dsn: redis://localhost:6379/0
902+
options:
903+
connection_persistent: []
904+
905+
YAML;
906+
}
907+
908+
public function testEmptyStringConnectionPersistentValue(): void
909+
{
910+
$this->expectException(InvalidConfigurationException::class);
911+
$this->expectExceptionMessage('connection_persistent must be a boolean or string');
912+
913+
$extension = new SncRedisExtension();
914+
$config = $this->parseYaml($this->getEmptyStringConnectionPersistentYamlConfig());
915+
$extension->load([$config], $this->getContainer());
916+
}
917+
918+
private function getEmptyStringConnectionPersistentYamlConfig(): string
919+
{
920+
return <<<'YAML'
921+
clients:
922+
default:
923+
type: predis
924+
alias: default
925+
dsn: redis://localhost:6379/0
926+
options:
927+
connection_persistent: ""
812928

813929
YAML;
814930
}

0 commit comments

Comments
 (0)