Add RedisSentinel class and tests

This commit is contained in:
Pavlo Yatsukhnenko
2019-12-18 14:00:45 +02:00
committed by Michael Grunder
parent b1724b8482
commit c94e28f1eb
12 changed files with 355 additions and 8 deletions
+3
View File
@@ -44,12 +44,15 @@ before_script:
- redis-server --port 0 --daemonize yes --unixsocket /tmp/redis.sock
- for PORT in $(seq 6379 6382) $(seq 32767 32769); do redis-server --port $PORT --daemonize yes; done
- for PORT in $(seq 7000 7011); do redis-server --port $PORT --cluster-enabled yes --cluster-config-file $PORT.conf --daemonize yes; echo 127.0.0.1:$PORT >> tests/nodes/nodemap; done
- for PORT in $(seq 26379 26380); do wget download.redis.io/redis-stable/sentinel.conf -O $PORT.conf; redis-server $PORT.conf --port $PORT --daemonize yes --sentinel; done
- echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7011) --cluster-replicas 3
- echo 'extension = redis.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
script:
- php tests/TestRedis.php --class Redis
- php tests/TestRedis.php --class RedisArray
- php tests/TestRedis.php --class RedisCluster
- php tests/TestRedis.php --class RedisSentinel
- USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class Redis
- USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisArray
- USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisCluster
- USE_ZEND_ALLOC=0 valgrind --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel
+1 -1
View File
@@ -267,5 +267,5 @@ if test "$PHP_REDIS" != "no"; then
dnl
dnl PHP_SUBST(REDIS_SHARED_LIBADD)
PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c $lzf_sources, $ext_shared)
PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c $lzf_sources, $ext_shared)
fi
+2
View File
@@ -167,6 +167,8 @@ redis_error_throw(RedisSock *redis_sock)
* Disque) */
if (!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "ERR") &&
!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOSCRIPT") &&
!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOQUORUM") &&
!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGOODSLAVE") &&
!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "WRONGTYPE") &&
!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "BUSYGROUP") &&
!REDIS_SOCK_ERRCMP_STATIC(redis_sock, "NOGROUP"))
+12 -4
View File
@@ -22,16 +22,16 @@
#include "config.h"
#endif
#include "common.h"
#include "ext/standard/info.h"
#include "php_redis.h"
#include "redis_commands.h"
#include "redis_array.h"
#include "redis_cluster.h"
#include "redis_commands.h"
#include "redis_sentinel.h"
#include <zend_exceptions.h>
#include <ext/standard/info.h>
#ifdef PHP_SESSION
#include "ext/session/php_session.h"
#include <ext/session/php_session.h>
#endif
#include "library.h"
@@ -48,6 +48,7 @@ extern ps_module ps_mod_redis_cluster;
extern zend_class_entry *redis_array_ce;
extern zend_class_entry *redis_cluster_ce;
extern zend_class_entry *redis_cluster_exception_ce;
extern zend_class_entry *redis_sentinel_ce;
zend_class_entry *redis_ce;
zend_class_entry *redis_exception_ce;
@@ -56,6 +57,7 @@ extern int le_cluster_slot_cache;
extern zend_function_entry redis_array_functions[];
extern zend_function_entry redis_cluster_functions[];
extern zend_function_entry redis_sentinel_functions[];
int le_redis_pconnect;
@@ -754,6 +756,7 @@ PHP_MINIT_FUNCTION(redis)
zend_class_entry redis_class_entry;
zend_class_entry redis_array_class_entry;
zend_class_entry redis_cluster_class_entry;
zend_class_entry redis_sentinel_class_entry;
zend_class_entry redis_exception_class_entry;
zend_class_entry redis_cluster_exception_class_entry;
@@ -780,6 +783,11 @@ PHP_MINIT_FUNCTION(redis)
redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry);
redis_cluster_ce->create_object = create_cluster_context;
/* RedisSentinel class */
INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_functions);
redis_sentinel_ce = zend_register_internal_class(&redis_sentinel_class_entry);
redis_sentinel_ce->create_object = create_sentinel_object;
/* Register our cluster cache list item */
le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor,
"Redis cluster slot cache",
+24
View File
@@ -3863,6 +3863,30 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
return SUCCESS;
}
int
redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
{
if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
return FAILURE;
}
*cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "s", kw, strlen(kw));
return SUCCESS;
}
int
redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
{
zend_string *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
return FAILURE;
}
*cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "sS", kw, strlen(kw), name);
return SUCCESS;
}
/*
* Redis commands that don't deal with the server at all. The RedisSock*
* pointer is the only thing retrieved differently, so we just take that
+6
View File
@@ -289,6 +289,12 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx);
int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
/* Commands that don't communicate with Redis at all (such as getOption,
* setOption, _prefix, _serialize, etc). These can be handled in one place
* with the method of grabbing our RedisSock* object in different ways
+94
View File
@@ -0,0 +1,94 @@
#include "php_redis.h"
#include "redis_commands.h"
#include "redis_sentinel.h"
zend_class_entry *redis_sentinel_ce;
ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, port)
ZEND_END_ARG_INFO()
zend_function_entry redis_sentinel_functions[] = {
PHP_ME(RedisSentinel, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, ckquorum, arginfo_value, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, failover, arginfo_value, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, flushconfig, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, getMasterAddrByName, arginfo_value, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, master, arginfo_value, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, masters, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, ping, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, reset, arginfo_value, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, sentinels, arginfo_value, ZEND_ACC_PUBLIC)
PHP_ME(RedisSentinel, slaves, arginfo_value, ZEND_ACC_PUBLIC)
PHP_FE_END
};
PHP_METHOD(RedisSentinel, __construct)
{
redis_sentinel_object *obj;
zend_long port = -1;
zend_string *host;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &host, &port) == FAILURE) {
RETURN_FALSE;
}
/* If it's not a unix socket, set to default */
if (port < 0 && ZSTR_LEN(host) > 0 && *ZSTR_VAL(host) != '/') {
port = 26379;
}
obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, getThis());
obj->sock = redis_sock_create(ZSTR_VAL(host), ZSTR_LEN(host), port, 0, 0, 0, NULL, 0);
}
PHP_METHOD(RedisSentinel, ckquorum)
{
REDIS_PROCESS_KW_CMD("ckquorum", redis_sentinel_str_cmd, redis_boolean_response);
}
PHP_METHOD(RedisSentinel, failover)
{
REDIS_PROCESS_KW_CMD("failover", redis_sentinel_str_cmd, redis_boolean_response);
}
PHP_METHOD(RedisSentinel, flushconfig)
{
REDIS_PROCESS_KW_CMD("flushconfig", redis_sentinel_cmd, redis_boolean_response);
}
PHP_METHOD(RedisSentinel, getMasterAddrByName)
{
REDIS_PROCESS_KW_CMD("get-master-addr-by-name", redis_sentinel_str_cmd, redis_mbulk_reply_raw);
}
PHP_METHOD(RedisSentinel, master)
{
REDIS_PROCESS_KW_CMD("master", redis_sentinel_str_cmd, redis_mbulk_reply_zipped_raw);
}
PHP_METHOD(RedisSentinel, masters)
{
REDIS_PROCESS_KW_CMD("masters", redis_sentinel_cmd, sentinel_mbulk_reply_zipped_assoc);
}
PHP_METHOD(RedisSentinel, ping)
{
REDIS_PROCESS_KW_CMD("PING", redis_empty_cmd, redis_boolean_response);
}
PHP_METHOD(RedisSentinel, reset)
{
REDIS_PROCESS_KW_CMD("reset", redis_sentinel_str_cmd, redis_boolean_response);
}
PHP_METHOD(RedisSentinel, sentinels)
{
REDIS_PROCESS_KW_CMD("sentinels", redis_sentinel_str_cmd, sentinel_mbulk_reply_zipped_assoc);
}
PHP_METHOD(RedisSentinel, slaves)
{
REDIS_PROCESS_KW_CMD("slaves", redis_sentinel_str_cmd, sentinel_mbulk_reply_zipped_assoc);
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef REDIS_SENTINEL_H
#define REDIS_SENTINEL_H
#include "sentinel_library.h"
PHP_METHOD(RedisSentinel, __construct);
PHP_METHOD(RedisSentinel, ckquorum);
PHP_METHOD(RedisSentinel, failover);
PHP_METHOD(RedisSentinel, flushconfig);
PHP_METHOD(RedisSentinel, getMasterAddrByName);
PHP_METHOD(RedisSentinel, master);
PHP_METHOD(RedisSentinel, masters);
PHP_METHOD(RedisSentinel, ping);
PHP_METHOD(RedisSentinel, reset);
PHP_METHOD(RedisSentinel, sentinels);
PHP_METHOD(RedisSentinel, slaves);
#endif /* REDIS_SENTINEL_H */
+62
View File
@@ -0,0 +1,62 @@
#include "sentinel_library.h"
static zend_object_handlers redis_sentinel_object_handlers;
static void
free_redis_sentinel_object(zend_object *object)
{
redis_sentinel_object *obj = (redis_sentinel_object *)((char *)(object) - XtOffsetOf(redis_sentinel_object, std));
if (obj->sock) {
redis_sock_disconnect(obj->sock, 0);
redis_free_socket(obj->sock);
}
zend_object_std_dtor(&obj->std);
}
zend_object *
create_sentinel_object(zend_class_entry *ce)
{
redis_sentinel_object *obj = ecalloc(1, sizeof(*obj) + zend_object_properties_size(ce));
zend_object_std_init(&obj->std, ce);
object_properties_init(&obj->std, ce);
memcpy(&redis_sentinel_object_handlers, zend_get_std_object_handlers(), sizeof(redis_sentinel_object_handlers));
redis_sentinel_object_handlers.offset = XtOffsetOf(redis_sentinel_object, std);
redis_sentinel_object_handlers.free_obj = free_redis_sentinel_object;
obj->std.handlers = &redis_sentinel_object_handlers;
return &obj->std;
}
PHP_REDIS_API void
sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx)
{
char inbuf[4096];
int i, nelem;
size_t len;
zval z_ret;
/* Throws exception on failure */
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
RETURN_FALSE;
}
if (*inbuf != TYPE_MULTIBULK) {
if (*inbuf == TYPE_ERR) {
redis_sock_set_err(redis_sock, inbuf + 1, len - 1);
}
RETURN_FALSE;
}
array_init(&z_ret);
nelem = atoi(inbuf + 1);
for (i = 0; i < nelem; ++i) {
/* redis_mbulk_reply_zipped_raw calls redis_mbulk_reply_zipped
* which puts result into return_value via RETVAL_ZVAL */
array_init(return_value);
redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
add_next_index_zval(&z_ret, return_value);
}
RETURN_ZVAL(&z_ret, 0, 1);
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef REDIS_SENTINEL_LIBRARY_H
#define REDIS_SENTINEL_LIBRARY_H
#include "common.h"
#include "library.h"
typedef redis_object redis_sentinel_object;
zend_object *create_sentinel_object(zend_class_entry *ce);
PHP_REDIS_API void sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
#endif /* REDIS_SENTINEL_LIBRARY_H */
+113
View File
@@ -0,0 +1,113 @@
<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");
require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");
class Redis_Sentinel_Test extends TestSuite
{
const NAME = 'mymaster';
/**
* @var RedisSentinel
*/
public $sentinel;
/**
* Common fields
*/
protected $fields = [
'name',
'ip',
'port',
'runid',
'flags',
'link-pending-commands',
'link-refcount',
'last-ping-sent',
'last-ok-ping-reply',
'last-ping-reply',
'down-after-milliseconds',
];
protected function newInstance()
{
return new RedisSentinel($this->getHost());
}
public function setUp()
{
$this->sentinel = $this->newInstance();
}
public function testCkquorum()
{
$this->assertTrue($this->sentinel->ckquorum(self::NAME));
}
public function testFailover()
{
$this->assertFalse($this->sentinel->failover(self::NAME));
}
public function testFlushconfig()
{
$this->assertTrue($this->sentinel->flushconfig());
}
public function testGetMasterAddrByName()
{
$result = $this->sentinel->getMasterAddrByName(self::NAME);
$this->assertTrue(is_array($result));
$this->assertEquals(2, count($result));
}
protected function checkFields(array $fields)
{
foreach ($this->fields as $k) {
$this->assertTrue(array_key_exists($k, $fields));
}
}
public function testMaster()
{
$result = $this->sentinel->master(self::NAME);
$this->assertTrue(is_array($result));
$this->checkFields($result);
}
public function testMasters()
{
$result = $this->sentinel->masters();
$this->assertTrue(is_array($result));
foreach ($result as $master) {
$this->checkFields($master);
}
}
public function testPing()
{
$this->assertTrue($this->sentinel->ping());
}
public function testReset()
{
$this->assertFalse($this->sentinel->reset('*'));
}
public function testSentinels()
{
$result = $this->sentinel->sentinels(self::NAME);
$this->assertTrue(is_array($result));
foreach ($result as $sentinel) {
$this->checkFields($sentinel);
}
}
public function testSlaves()
{
$result = $this->sentinel->slaves(self::NAME);
$this->assertTrue(is_array($result));
foreach ($result as $slave) {
$this->checkFields($slave);
}
}
}
+7 -3
View File
@@ -4,6 +4,7 @@ require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisTest.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisArrayTest.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisClusterTest.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisSentinelTest.php");
/* Make sure errors go to stdout and are shown */
error_reporting(E_ALL);
@@ -13,7 +14,7 @@ ini_set( 'display_errors','1');
$arr_args = getopt('', ['host:', 'class:', 'test:', 'nocolors']);
/* Grab the test the user is trying to run */
$arr_valid_classes = ['redis', 'redisarray', 'rediscluster'];
$arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel'];
$str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis';
$boo_colorize = !isset($arr_args['nocolors']);
@@ -25,7 +26,7 @@ $str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1';
/* Validate the class is known */
if (!in_array($str_class, $arr_valid_classes)) {
echo "Error: Valid test classes are Redis, RedisArray, and RedisCluster!\n";
echo "Error: Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n";
exit(1);
}
@@ -56,8 +57,11 @@ if ($str_class == 'redis') {
}
}
}
} else {
} else if ($str_class == 'rediscluster') {
echo TestSuite::make_bold("RedisCluster") . "\n";
exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host));
} else {
echo TestSuite::make_bold("RedisSentinel") . "\n";
exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host));
}
?>