Permalink
Browse files

Make Predis\Network\MasterSlaveReplication serializable.

Since PHP cannot serialize closures we switched to a private method to check
if a SORT command is a read-only operation, but closures are still supported
even if they will make the connection unserializable.
  • Loading branch information...
1 parent 93163bd commit 317fb6c3984a32216fe8ffb0395a7a3de5b46b34 @nrk committed Jan 14, 2012
Showing with 62 additions and 6 deletions.
  1. +21 −6 lib/Predis/Network/MasterSlaveReplication.php
  2. +41 −0 tests/Predis/Network/MasterSlaveReplicationTest.php
@@ -262,7 +262,7 @@ protected function isReadOperation(ICommand $command)
return true;
}
- return $readonly($command);
+ return call_user_func($readonly, $command);
}
if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
@@ -273,14 +273,24 @@ protected function isReadOperation(ICommand $command)
return true;
}
- return $readonly($command);
+ return call_user_func($readonly, $command);
}
}
return false;
}
/**
+ * Checks if a SORT command is a readable operation by parsing the arguments
+ * array of the specified commad instance.
+ */
+ private function isSortReadOnly(ICommand $command)
+ {
+ $arguments = $command->getArguments();
+ return ($c = count($arguments)) === 1 ? true : $arguments[$c - 2] !== 'STORE';
+ }
+
+ /**
* Marks a command as a read-only operation. When the behaviour of a
* command can be decided only at runtime depending on its arguments,
* a callable object can be provided to dinamically check if the passed
@@ -396,10 +406,15 @@ protected function getReadOnlyOperations()
'ECHO' => true,
'QUIT' => true,
'OBJECT' => true,
- 'SORT' => function(ICommand $command) {
- $arguments = $command->getArguments();
- return ($c = count($arguments)) === 1 ? true : $arguments[$c - 2] !== 'STORE';
- },
+ 'SORT' => array($this, 'isSortReadOnly'),
);
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __sleep()
+ {
+ return array('master', 'slaves', 'disallowed', 'readonly', 'readonlySHA1');
+ }
}
@@ -412,6 +412,29 @@ public function testEvalTriggersSwitchToMasterConnection()
/**
* @group disconnected
+ */
+ public function testSortTriggersSwitchToMasterConnectionOnStoreModifier()
+ {
+ $profile = ServerProfile::get('dev');
+ $cmdSortNormal = $profile->createCommand('sort', array('key'));
+ $cmdSortStore = $profile->createCommand('sort', array('key', array('store' => 'key:store')));
+
+ $master = $this->getMockConnection('tcp://host1?alias=master');
+ $master->expects($this->once())->method('executeCommand')->with($cmdSortStore);
+
+ $slave1 = $this->getMockConnection('tcp://host2?alias=slave1');
+ $slave1->expects($this->once())->method('executeCommand')->with($cmdSortNormal);
+
+ $replication = new MasterSlaveReplication();
+ $replication->add($master);
+ $replication->add($slave1);
+
+ $replication->executeCommand($cmdSortNormal);
+ $replication->executeCommand($cmdSortStore);
+ }
+
+ /**
+ * @group disconnected
* @expectedException Predis\NotSupportedException
* @expectedExceptionMessage The command INFO is not allowed in replication mode
*/
@@ -508,6 +531,24 @@ public function testCanSetReadOnlyFlagForEvalScripts()
$replication->executeCommand($cmdEvalSha);
}
+ /**
+ * @group disconnected
+ */
+ public function testCanBeSerialized()
+ {
+ $master = $this->getMockConnection('tcp://host1?alias=master');
+ $slave1 = $this->getMockConnection('tcp://host2?alias=slave1');
+
+ $replication = new MasterSlaveReplication();
+ $replication->add($master);
+ $replication->add($slave1);
+
+ $unserialized = unserialize(serialize($replication));
+
+ $this->assertEquals($master, $unserialized->getConnectionById('master'));
+ $this->assertEquals($slave1, $unserialized->getConnectionById('slave1'));
+ }
+
// ******************************************************************** //
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //

0 comments on commit 317fb6c

Please sign in to comment.