Browse Source

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.
Daniele Alessandri 13 years ago
parent
commit
317fb6c398

+ 21 - 6
lib/Predis/Network/MasterSlaveReplication.php

@@ -262,7 +262,7 @@ class MasterSlaveReplication implements IConnectionReplication
                 return true;
             }
 
-            return $readonly($command);
+            return call_user_func($readonly, $command);
         }
 
         if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
@@ -273,13 +273,23 @@ class MasterSlaveReplication implements IConnectionReplication
                     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,
@@ -396,10 +406,15 @@ class MasterSlaveReplication implements IConnectionReplication
             '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');
+    }
 }

+ 41 - 0
tests/Predis/Network/MasterSlaveReplicationTest.php

@@ -410,6 +410,29 @@ class MasterSlaveReplicationTest extends StandardTestCase
         $replication->executeCommand($cmdEval);
     }
 
+    /**
+     * @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
@@ -508,6 +531,24 @@ class MasterSlaveReplicationTest extends StandardTestCase
         $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 ------------------------------------------------ //
     // ******************************************************************** //