mirror of
https://github.com/predis/predis.git
synced 2026-08-30 04:02:22 +00:00
Added support for transactions in OSS Cluster (#1497)
* Added support for transactions in OSS Cluster * Updated test cases * Updated version restriction * Updated server version restriction * Added test skip for Relay * Added test case for Relay * Set transaction slot to null in case of failed transaction * Restrict usage of transaction commands outside of transaction context * Added handling for Relay responses * Reverted changes * Removed unsupported command * Added dummy arguments * Codestyle fixes * Added additional test coverage * Added CHANGELOG and README entries * Added missing PR reference * Missing word * Fixed README
This commit is contained in:
committed by
GitHub
parent
86e6804c80
commit
73e6a03fd8
@@ -20,6 +20,7 @@ use Predis\Command\CommandInterface;
|
||||
use Predis\Connection\NodeConnectionInterface;
|
||||
use Predis\Connection\Parameters;
|
||||
use Predis\Response;
|
||||
use Predis\Transaction\Exception\TransactionException;
|
||||
use PredisTestCase;
|
||||
use RuntimeException;
|
||||
|
||||
@@ -781,6 +782,7 @@ class MultiExecTest extends PredisTestCase
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 2.2.0
|
||||
*/
|
||||
public function testIntegrationWritesOnWatchedKeysAbortTransaction(): void
|
||||
@@ -803,6 +805,31 @@ class MultiExecTest extends PredisTestCase
|
||||
$this->assertSame('client2', $client1->get('sentinel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group ext-relay
|
||||
* @requiresRedisVersion >= 2.2.0
|
||||
*/
|
||||
public function testRelayIntegrationWritesOnWatchedKeysAbortTransaction(): void
|
||||
{
|
||||
$exception = null;
|
||||
$client1 = $this->getClient();
|
||||
$client2 = $this->getClient();
|
||||
|
||||
try {
|
||||
$client1->transaction(['watch' => 'sentinel'], function ($tx) use ($client2) {
|
||||
$tx->set('sentinel', 'client1');
|
||||
$tx->get('sentinel');
|
||||
$client2->set('sentinel', 'client2');
|
||||
});
|
||||
} catch (Response\ServerException $ex) {
|
||||
$exception = $ex;
|
||||
}
|
||||
|
||||
$this->assertInstanceOf(Response\ServerException::class, $exception);
|
||||
$this->assertSame('client2', $client1->get('sentinel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.2.0
|
||||
@@ -851,6 +878,136 @@ class MultiExecTest extends PredisTestCase
|
||||
$this->assertSame([['hijacked!', null]], $responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 3.0.0
|
||||
*/
|
||||
public function testExecutesTransactionAgainstCluster(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$response = $redis->transaction(function (MultiExec $tx) {
|
||||
$tx->set('{foo}foo', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('{foo}baz', 'value');
|
||||
});
|
||||
|
||||
$this->assertEquals(['OK', 'OK', 'OK'], $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 3.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnDifferentHashSlots(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(AbortedMultiExecException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'To be able to execute a transaction against cluster, all commands should operate on the same hash slot'
|
||||
);
|
||||
|
||||
$redis->transaction(function (MultiExec $tx) {
|
||||
$tx->set('foo_bar_baz', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('{foo}baz', 'value');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 3.0.0
|
||||
*/
|
||||
public function testExecutesCASTransactionAgainstCluster(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$options = ['cas' => true, 'watch' => ['{foo}foo', '{foo}bar', '{foo}baz']];
|
||||
|
||||
$response = $redis->transaction($options, function (MultiExec $tx) {
|
||||
$tx->multi();
|
||||
$tx->set('{foo}foo', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('{foo}baz', 'value');
|
||||
});
|
||||
|
||||
$this->assertEquals(['OK', 'OK', 'OK'], $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 3.0.0
|
||||
*/
|
||||
public function testUNWATCHCurrentlyWATCHedKeys(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$options = ['cas' => true, 'watch' => ['{foo}foo', '{foo}bar', '{foo}baz']];
|
||||
|
||||
$response = $redis->transaction($options, function (MultiExec $tx) {
|
||||
$tx->multi();
|
||||
$tx->set('{foo}foo', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('{foo}baz', 'value');
|
||||
$tx->unwatch();
|
||||
});
|
||||
|
||||
$this->assertEquals(['OK', 'OK', 'OK', 'OK'], $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 3.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWATCHedKeysPointsToDifferentSlots(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$options = ['cas' => true, 'watch' => ['foo_bar_baz', '{foo}bar', '{foo}baz']];
|
||||
|
||||
$this->expectException(TransactionException::class);
|
||||
$this->expectExceptionMessage('WATCHed keys should point to the same hash slot');
|
||||
|
||||
$redis->transaction($options, function (MultiExec $tx) {
|
||||
$tx->multi();
|
||||
$tx->set('{foo}foo', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('{foo}baz', 'value');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 3.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnTransactionContextPointsToDifferentSlots(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$options = ['cas' => true, 'watch' => ['{foo}foo', '{foo}bar', '{foo}baz']];
|
||||
|
||||
$this->expectException(AbortedMultiExecException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'To be able to execute a transaction against cluster, all commands should operate on the same hash slot'
|
||||
);
|
||||
|
||||
$redis->transaction($options, function (MultiExec $tx) {
|
||||
$tx->multi();
|
||||
$tx->set('{foo}foo', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('foo_bar_baz', 'value');
|
||||
});
|
||||
}
|
||||
|
||||
// ******************************************************************** //
|
||||
// ---- HELPER METHODS ------------------------------------------------ //
|
||||
// ******************************************************************** //
|
||||
|
||||
Reference in New Issue
Block a user