mirror of
https://github.com/phpredis/phpredis.git
synced 2026-06-19 07:35:31 +00:00
Merge branch 'session-locking' into develop
Conflicts: redis_session.c tests/RedisTest.php
This commit is contained in:
@@ -14,3 +14,4 @@ missing
|
||||
autom4te.cache
|
||||
mkinstalldirs
|
||||
run-tests.php
|
||||
idea/*
|
||||
@@ -262,11 +262,6 @@ PHP_REDIS_API int redis_sock_read_multibulk_multi_reply_loop(
|
||||
INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab,
|
||||
int numElems);
|
||||
|
||||
#ifndef _MSC_VER
|
||||
ZEND_BEGIN_MODULE_GLOBALS(redis)
|
||||
ZEND_END_MODULE_GLOBALS(redis)
|
||||
#endif
|
||||
|
||||
extern zend_module_entry redis_module_entry;
|
||||
|
||||
#define redis_module_ptr &redis_module_entry
|
||||
|
||||
@@ -73,6 +73,12 @@ PHP_INI_BEGIN()
|
||||
PHP_INI_ENTRY("redis.clusters.read_timeout", "", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.clusters.timeout", "", PHP_INI_ALL, NULL)
|
||||
|
||||
/* redis session */
|
||||
PHP_INI_ENTRY("redis.session.locking_enabled", "", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.session.lock_expire", "", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.session.lock_retries", "", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.session.lock_wait_time", "", PHP_INI_ALL, NULL)
|
||||
PHP_INI_END()
|
||||
|
||||
/** {{{ Argument info for commands in redis 1.0 */
|
||||
@@ -226,10 +232,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
|
||||
ZEND_ARG_INFO(0, i_count)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#ifdef ZTS
|
||||
ZEND_DECLARE_MODULE_GLOBALS(redis)
|
||||
#endif
|
||||
|
||||
static zend_function_entry redis_functions[] = {
|
||||
PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
|
||||
PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC)
|
||||
|
||||
+339
-10
@@ -41,13 +41,40 @@
|
||||
#include "SAPI.h"
|
||||
#include "ext/standard/url.h"
|
||||
|
||||
/* HOST_NAME_MAX doesn't exist everywhere */
|
||||
#ifndef HOST_NAME_MAX
|
||||
#if defined(_POSIX_HOST_NAME_MAX)
|
||||
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
||||
#elif defined(MAXHOSTNAMELEN)
|
||||
#define HOST_NAME_MAX MAXHOSTNAMELEN
|
||||
#else
|
||||
#define HOST_NAME_MAX 255
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Session lock LUA as well as its SHA1 hash */
|
||||
#define LOCK_RELEASE_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"
|
||||
#define LOCK_RELEASE_LUA_LEN (sizeof(LOCK_RELEASE_LUA_STR) - 1)
|
||||
#define LOCK_RELEASE_SHA_STR "b70c2384248f88e6b75b9f89241a180f856ad852"
|
||||
#define LOCK_RELEASE_SHA_LEN (sizeof(LOCK_RELEASE_SHA_STR) - 1)
|
||||
|
||||
/* Check if a response is the Redis +OK status response */
|
||||
#define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3))
|
||||
|
||||
ps_module ps_mod_redis = {
|
||||
PS_MOD(redis)
|
||||
PS_MOD_SID(redis)
|
||||
};
|
||||
ps_module ps_mod_redis_cluster = {
|
||||
PS_MOD(rediscluster)
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
zend_bool is_locked;
|
||||
char *session_key;
|
||||
char *lock_key;
|
||||
char *lock_secret;
|
||||
} redis_session_lock_status;
|
||||
|
||||
typedef struct redis_pool_member_ {
|
||||
|
||||
RedisSock *redis_sock;
|
||||
@@ -65,6 +92,7 @@ typedef struct {
|
||||
int count;
|
||||
|
||||
redis_pool_member *head;
|
||||
redis_session_lock_status lock_status;
|
||||
|
||||
} redis_pool;
|
||||
|
||||
@@ -78,7 +106,6 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight,
|
||||
rpm->database = database;
|
||||
|
||||
rpm->prefix = prefix;
|
||||
|
||||
rpm->auth = auth;
|
||||
|
||||
rpm->next = pool->head;
|
||||
@@ -101,9 +128,35 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) {
|
||||
efree(rpm);
|
||||
rpm = next;
|
||||
}
|
||||
|
||||
/* Cleanup after our lock */
|
||||
if (pool->lock_status.session_key)
|
||||
efree(pool->lock_status.session_key);
|
||||
if (pool->lock_status.lock_secret)
|
||||
efree(pool->lock_status.lock_secret);
|
||||
if (pool->lock_status.lock_key)
|
||||
efree(pool->lock_status.lock_key);
|
||||
|
||||
/* Cleanup pool itself */
|
||||
efree(pool);
|
||||
}
|
||||
|
||||
/* Send a command to Redis. Returns reply on success and NULL on failure */
|
||||
static char *redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen,
|
||||
int *replylen TSRMLS_DC)
|
||||
{
|
||||
char *reply;
|
||||
|
||||
if (redis_sock_write(redis_sock, cmd, cmdlen TSRMLS_CC) >= 0) {
|
||||
if ((reply = redis_sock_read(redis_sock, replylen TSRMLS_CC)) != NULL) {
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to send or receive command */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) {
|
||||
RedisSock *redis_sock = rpm->redis_sock;
|
||||
@@ -171,6 +224,182 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Helper to set our session lock key */
|
||||
static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len
|
||||
TSRMLS_DC)
|
||||
{
|
||||
char *reply;
|
||||
int reply_len;
|
||||
|
||||
reply = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply_len TSRMLS_CC);
|
||||
if (reply) {
|
||||
if (IS_REDIS_OK(reply, reply_len)) {
|
||||
efree(reply);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
efree(reply);
|
||||
}
|
||||
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status
|
||||
TSRMLS_DC)
|
||||
{
|
||||
char *cmd, hostname[HOST_NAME_MAX] = {0};
|
||||
int cmd_len, lock_wait_time, retries, i, expiry;
|
||||
|
||||
/* Short circuit if we are already locked or not using session locks */
|
||||
if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled"))
|
||||
return SUCCESS;
|
||||
|
||||
/* How long to wait between attempts to acquire lock */
|
||||
lock_wait_time = INI_INT("redis.session.lock_wait_time");
|
||||
if (lock_wait_time == 0) {
|
||||
lock_wait_time = 2000;
|
||||
}
|
||||
|
||||
/* Maximum number of times to retry (-1 means infinite) */
|
||||
retries = INI_INT("redis.session.lock_retries");
|
||||
if (retries == 0) {
|
||||
retries = 10;
|
||||
}
|
||||
|
||||
/* How long should the lock live (in seconds) */
|
||||
expiry = INI_INT("redis.session.lock_expire");
|
||||
if (expiry == 0) {
|
||||
expiry = INI_INT("max_execution_time");
|
||||
}
|
||||
|
||||
/* Generate our qualified lock key */
|
||||
spprintf(&lock_status->lock_key, 0, "%s%s", lock_status->session_key, "_LOCK");
|
||||
|
||||
/* Calculate lock secret */
|
||||
gethostname(hostname, HOST_NAME_MAX);
|
||||
spprintf(&lock_status->lock_secret, 0, "%s|%ld", hostname, (long)getpid());
|
||||
|
||||
if (expiry > 0) {
|
||||
cmd_len = REDIS_SPPRINTF(&cmd, "SET", "ssssd", lock_status->lock_key,
|
||||
strlen(lock_status->lock_key), lock_status->lock_secret,
|
||||
strlen(lock_status->lock_secret), "NX", 2,
|
||||
"PX", 2, expiry * 1000);
|
||||
} else {
|
||||
cmd_len = REDIS_SPPRINTF(&cmd, "SET", "sss", lock_status->lock_key,
|
||||
strlen(lock_status->lock_key), lock_status->lock_secret,
|
||||
strlen(lock_status->lock_secret), "NX", 2);
|
||||
}
|
||||
|
||||
/* Attempt to get our lock */
|
||||
for (i = 0; retries == -1 || i <= retries; i++) {
|
||||
if (set_session_lock_key(redis_sock, cmd, cmd_len TSRMLS_CC) == SUCCESS) {
|
||||
lock_status->is_locked = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Sleep unless we're done making attempts */
|
||||
if (retries == -1 || i < retries) {
|
||||
usleep(lock_wait_time);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup SET command */
|
||||
efree(cmd);
|
||||
|
||||
/* Success if we're locked */
|
||||
return lock_status->is_locked ? SUCCESS : FAILURE;
|
||||
}
|
||||
|
||||
#define IS_LOCK_SECRET(reply, len, secret) (len == strlen(secret) && !strncmp(reply, secret, len))
|
||||
static void refresh_lock_status(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC)
|
||||
{
|
||||
char *cmd, *reply = NULL;
|
||||
int replylen, cmdlen;
|
||||
|
||||
/* Return early if we're not locked */
|
||||
if (!lock_status->is_locked)
|
||||
return;
|
||||
|
||||
/* If redis.session.lock_expire is not set => TTL=max_execution_time
|
||||
Therefore it is guaranteed that the current process is still holding
|
||||
the lock */
|
||||
if (lock_status->is_locked && INI_INT("redis.session.lock_expire") == 0)
|
||||
return;
|
||||
|
||||
/* Command to get our lock key value and compare secrets */
|
||||
cmdlen = REDIS_SPPRINTF(&cmd, "GET", "s", lock_status->lock_key,
|
||||
strlen(lock_status->lock_key));
|
||||
|
||||
/* Attempt to refresh the lock */
|
||||
reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC);
|
||||
if (reply != NULL) {
|
||||
lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret);
|
||||
efree(reply);
|
||||
} else {
|
||||
lock_status->is_locked = 0;
|
||||
}
|
||||
|
||||
/* Issue a warning if we're not locked. We don't attempt to refresh the lock
|
||||
* if we aren't flagged as locked, so if we're not flagged here something
|
||||
* failed */
|
||||
if (!lock_status->is_locked) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to refresh session lock");
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
efree(cmd);
|
||||
}
|
||||
|
||||
static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC)
|
||||
{
|
||||
if (!INI_INT("redis.session.locking_enabled"))
|
||||
return 1;
|
||||
|
||||
refresh_lock_status(redis_sock, lock_status TSRMLS_CC);
|
||||
|
||||
return lock_status->is_locked;
|
||||
}
|
||||
|
||||
/* Release any session lock we hold and cleanup allocated lock data. This function
|
||||
* first attempts to use EVALSHA and then falls back to EVAL if EVALSHA fails. This
|
||||
* will cause Redis to cache the script, so subsequent calls should then succeed
|
||||
* using EVALSHA. */
|
||||
static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status TSRMLS_DC)
|
||||
{
|
||||
char *cmd, *reply;
|
||||
int i, cmdlen, replylen;
|
||||
|
||||
/* Keywords, command, and length fallbacks */
|
||||
const char *kwd[] = {"EVALSHA", "EVAL"};
|
||||
const char *lua[] = {LOCK_RELEASE_SHA_STR, LOCK_RELEASE_LUA_STR};
|
||||
int len[] = {LOCK_RELEASE_SHA_LEN, LOCK_RELEASE_LUA_LEN};
|
||||
|
||||
/* We first want to try EVALSHA and then fall back to EVAL */
|
||||
for (i = 0; lock_status->is_locked && i < sizeof(kwd)/sizeof(*kwd); i++) {
|
||||
/* Construct our command */
|
||||
cmdlen = REDIS_SPPRINTF(&cmd, (char*)kwd[i], "sdss", lua[i], len[i], 1,
|
||||
lock_status->lock_key, strlen(lock_status->lock_key),
|
||||
lock_status->lock_secret, strlen(lock_status->lock_secret));
|
||||
|
||||
/* Send it off */
|
||||
reply = redis_simple_cmd(redis_sock, cmd, cmdlen, &replylen TSRMLS_CC);
|
||||
|
||||
/* Release lock and cleanup reply if we got one */
|
||||
if (reply != NULL) {
|
||||
lock_status->is_locked = 0;
|
||||
efree(reply);
|
||||
}
|
||||
|
||||
/* Cleanup command */
|
||||
efree(cmd);
|
||||
}
|
||||
|
||||
/* Something has failed if we are still locked */
|
||||
if (lock_status->is_locked) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to release session lock");
|
||||
}
|
||||
}
|
||||
|
||||
/* {{{ PS_OPEN_FUNC
|
||||
*/
|
||||
PS_OPEN_FUNC(redis)
|
||||
@@ -189,7 +418,7 @@ PS_OPEN_FUNC(redis)
|
||||
/* find end of url */
|
||||
j = i;
|
||||
while (j<path_len && !isspace(save_path[j]) && save_path[j] != ',')
|
||||
j++;
|
||||
j++;
|
||||
|
||||
if (i < j) {
|
||||
int weight = 1;
|
||||
@@ -197,8 +426,8 @@ PS_OPEN_FUNC(redis)
|
||||
int persistent = 0;
|
||||
int database = -1;
|
||||
char *persistent_id = NULL;
|
||||
zend_string *prefix = NULL, *auth = NULL;
|
||||
long retry_interval = 0;
|
||||
zend_string *prefix = NULL, *auth = NULL;
|
||||
|
||||
/* translate unix: into file: */
|
||||
if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) {
|
||||
@@ -308,10 +537,18 @@ PS_CLOSE_FUNC(redis)
|
||||
{
|
||||
redis_pool *pool = PS_GET_MOD_DATA();
|
||||
|
||||
if (pool){
|
||||
if (pool) {
|
||||
redis_pool_member *rpm = redis_pool_get_sock(pool, pool->lock_status.session_key TSRMLS_CC);
|
||||
|
||||
RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
|
||||
if (redis_sock) {
|
||||
lock_release(redis_sock, &pool->lock_status TSRMLS_CC);
|
||||
}
|
||||
|
||||
redis_pool_free(pool TSRMLS_CC);
|
||||
PS_SET_MOD_DATA(NULL);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -338,6 +575,73 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses
|
||||
return session;
|
||||
}
|
||||
|
||||
/* {{{ PS_CREATE_SID_FUNC
|
||||
*/
|
||||
PS_CREATE_SID_FUNC(redis)
|
||||
{
|
||||
int retries = 3;
|
||||
redis_pool *pool = PS_GET_MOD_DATA();
|
||||
|
||||
if (!pool) {
|
||||
#if (PHP_MAJOR_VERSION < 7)
|
||||
return php_session_create_id(NULL, newlen TSRMLS_CC);
|
||||
#else
|
||||
return php_session_create_id(NULL TSRMLS_CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
while (retries-- > 0) {
|
||||
#if (PHP_MAJOR_VERSION < 7)
|
||||
char* sid = php_session_create_id((void **) &pool, newlen TSRMLS_CC);
|
||||
redis_pool_member *rpm = redis_pool_get_sock(pool, sid TSRMLS_CC);
|
||||
#else
|
||||
zend_string* sid = php_session_create_id((void **) &pool TSRMLS_CC);
|
||||
redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid) TSRMLS_CC);
|
||||
#endif
|
||||
RedisSock *redis_sock = rpm?rpm->redis_sock:NULL;
|
||||
|
||||
if (!rpm || !redis_sock) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_NOTICE,
|
||||
"Redis not available while creating session_id");
|
||||
|
||||
#if (PHP_MAJOR_VERSION < 7)
|
||||
efree(sid);
|
||||
return php_session_create_id(NULL, newlen TSRMLS_CC);
|
||||
#else
|
||||
zend_string_release(sid);
|
||||
return php_session_create_id(NULL TSRMLS_CC);
|
||||
#endif
|
||||
}
|
||||
|
||||
int resp_len;
|
||||
#if (PHP_MAJOR_VERSION < 7)
|
||||
char *full_session_key = redis_session_key(rpm, sid, strlen(sid), &resp_len);
|
||||
#else
|
||||
char *full_session_key = redis_session_key(rpm, ZSTR_VAL(sid), ZSTR_LEN(sid), &resp_len);
|
||||
#endif
|
||||
char *full_session_key_nt = estrndup(full_session_key, resp_len);
|
||||
efree(full_session_key);
|
||||
pool->lock_status.session_key = full_session_key_nt;
|
||||
|
||||
if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) == SUCCESS) {
|
||||
return sid;
|
||||
}
|
||||
|
||||
#if (PHP_MAJOR_VERSION < 7)
|
||||
efree(sid);
|
||||
#else
|
||||
zend_string_release(sid);
|
||||
#endif
|
||||
sid = NULL;
|
||||
}
|
||||
|
||||
php_error_docref(NULL TSRMLS_CC, E_NOTICE,
|
||||
"Acquiring session lock failed while creating session_id");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ PS_READ_FUNC
|
||||
*/
|
||||
PS_READ_FUNC(redis)
|
||||
@@ -363,9 +667,15 @@ PS_READ_FUNC(redis)
|
||||
|
||||
/* send GET command */
|
||||
resp = redis_session_key(rpm, skey, skeylen, &resp_len);
|
||||
pool->lock_status.session_key = estrndup(resp, resp_len);
|
||||
cmd_len = REDIS_SPPRINTF(&cmd, "GET", "s", resp, resp_len);
|
||||
|
||||
efree(resp);
|
||||
|
||||
if (lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_NOTICE,
|
||||
"Acquire of session lock was not successful");
|
||||
}
|
||||
|
||||
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
|
||||
efree(cmd);
|
||||
return FAILURE;
|
||||
@@ -417,17 +727,31 @@ PS_WRITE_FUNC(redis)
|
||||
|
||||
redis_pool *pool = PS_GET_MOD_DATA();
|
||||
redis_pool_member *rpm = redis_pool_get_sock(pool, skey TSRMLS_CC);
|
||||
RedisSock *redis_sock = rpm?rpm->redis_sock:NULL;
|
||||
if (!rpm || !redis_sock){
|
||||
RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL;
|
||||
if (!redis_sock) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* send SET command */
|
||||
session = redis_session_key(rpm, skey, skeylen, &session_len);
|
||||
#if (PHP_MAJOR_VERSION < 7)
|
||||
/* We need to check for PHP5 if the session key changes (a bug with session_regenerate_id() is causing a missing PS_CREATE_SID call)*/
|
||||
int session_key_changed = strlen(pool->lock_status.session_key) != session_len || strncmp(pool->lock_status.session_key, session, session_len) != 0;
|
||||
if (session_key_changed) {
|
||||
efree(pool->lock_status.session_key);
|
||||
pool->lock_status.session_key = estrndup(session, session_len);
|
||||
}
|
||||
|
||||
if (session_key_changed && lock_acquire(redis_sock, &pool->lock_status TSRMLS_CC) != SUCCESS) {
|
||||
efree(session);
|
||||
return FAILURE;
|
||||
}
|
||||
#endif
|
||||
cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "sds", session, session_len,
|
||||
INI_INT("session.gc_maxlifetime"), sval, svallen);
|
||||
efree(session);
|
||||
if (redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
|
||||
|
||||
if (!write_allowed(redis_sock, &pool->lock_status TSRMLS_CC) || redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
|
||||
efree(cmd);
|
||||
return FAILURE;
|
||||
}
|
||||
@@ -438,7 +762,7 @@ PS_WRITE_FUNC(redis)
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (response_len == 3 && strncmp(response, "+OK", 3) == 0) {
|
||||
if (IS_REDIS_OK(response, response_len)) {
|
||||
efree(response);
|
||||
return SUCCESS;
|
||||
} else {
|
||||
@@ -469,6 +793,11 @@ PS_DESTROY_FUNC(redis)
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* Release lock */
|
||||
if (redis_sock) {
|
||||
lock_release(redis_sock, &pool->lock_status TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* send DEL command */
|
||||
session = redis_session_key(rpm, skey, skeylen, &session_len);
|
||||
cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "s", session, session_len);
|
||||
|
||||
+1
-1
@@ -9,6 +9,7 @@ PS_READ_FUNC(redis);
|
||||
PS_WRITE_FUNC(redis);
|
||||
PS_DESTROY_FUNC(redis);
|
||||
PS_GC_FUNC(redis);
|
||||
PS_CREATE_SID_FUNC(redis);
|
||||
|
||||
PS_OPEN_FUNC(rediscluster);
|
||||
PS_CLOSE_FUNC(rediscluster);
|
||||
@@ -19,4 +20,3 @@ PS_GC_FUNC(rediscluster);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ class Redis_Cluster_Test extends Redis_Test {
|
||||
RedisCluster::FAILOVER_DISTRIBUTE
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:';
|
||||
|
||||
/* Tests we'll skip all together in the context of RedisCluster. The
|
||||
* RedisCluster class doesn't implement specialized (non-redis) commands
|
||||
* such as sortAsc, or sortDesc and other commands such as SELECT are
|
||||
@@ -37,6 +42,21 @@ class Redis_Cluster_Test extends Redis_Test {
|
||||
public function testSwapDB() { return $this->markTestSkipped(); }
|
||||
public function testConnectException() { return $this->markTestSkipped(); }
|
||||
|
||||
/* Session locking feature is currently not supported in in context of Redis Cluster.
|
||||
The biggest issue for this is the distribution nature of Redis cluster */
|
||||
public function testSession_savedToRedis() { return $this->markTestSkipped(); }
|
||||
public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); }
|
||||
public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); }
|
||||
public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); }
|
||||
public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); }
|
||||
public function testSession_ttlLockExpire() { return $this->markTestSkipped(); }
|
||||
public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); }
|
||||
public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); }
|
||||
public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); }
|
||||
public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); }
|
||||
public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); }
|
||||
public function testSession_lockWaitTime() { return $this->markTestSkipped(); }
|
||||
|
||||
/* Load our seeds on construction */
|
||||
public function __construct() {
|
||||
$str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
|
||||
|
||||
+368
-8
@@ -21,6 +21,11 @@ class Redis_Test extends TestSuite
|
||||
*/
|
||||
public $redis;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sessionPrefix = 'PHPREDIS_SESSION:';
|
||||
|
||||
public function setUp() {
|
||||
$this->redis = $this->newInstance();
|
||||
$info = $this->redis->info();
|
||||
@@ -5142,15 +5147,176 @@ class Redis_Test extends TestSuite
|
||||
$this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D'));
|
||||
}
|
||||
|
||||
public function testSession()
|
||||
public function testSession_savedToRedis()
|
||||
{
|
||||
ini_set('session.save_handler', 'redis');
|
||||
ini_set('session.save_path', 'tcp://localhost:6379');
|
||||
if (!@session_start()) {
|
||||
return $this->markTestSkipped();
|
||||
}
|
||||
session_write_close();
|
||||
$this->assertTrue($this->redis->exists('PHPREDIS_SESSION:' . session_id()));
|
||||
$this->setSessionHandler();
|
||||
|
||||
$sessionId = $this->generateSessionId();
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
|
||||
|
||||
$this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId));
|
||||
$this->assertTrue($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_lockKeyCorrect()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 5, true);
|
||||
usleep(100000);
|
||||
|
||||
$this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
|
||||
}
|
||||
|
||||
public function testSession_lockingDisabledByDefault()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 5, true, 300, false);
|
||||
usleep(100000);
|
||||
|
||||
$start = microtime(true);
|
||||
$sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
|
||||
$this->assertTrue($elapsedTime < 1);
|
||||
$this->assertTrue($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_lockReleasedOnClose()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 1, true);
|
||||
usleep(1100000);
|
||||
|
||||
$this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
|
||||
}
|
||||
|
||||
public function testSession_ttlMaxExecutionTime()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 10, true, 2);
|
||||
usleep(100000);
|
||||
|
||||
$start = microtime(true);
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertTrue($elapsedTime < 3);
|
||||
$this->assertTrue($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_ttlLockExpire()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2);
|
||||
usleep(100000);
|
||||
|
||||
$start = microtime(true);
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertTrue($elapsedTime < 3);
|
||||
$this->assertTrue($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess');
|
||||
usleep(1500000); // 1.5 sec
|
||||
$writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, 'secondProcess');
|
||||
sleep(1);
|
||||
|
||||
$this->assertTrue($writeSuccessful);
|
||||
$this->assertEquals('secondProcess', $this->getSessionData($sessionId));
|
||||
}
|
||||
|
||||
public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess');
|
||||
|
||||
$this->assertFalse($writeSuccessful);
|
||||
$this->assertTrue('firstProcess' !== $this->getSessionData($sessionId));
|
||||
}
|
||||
|
||||
public function testSession_correctLockRetryCount()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 10, true);
|
||||
usleep(100000);
|
||||
|
||||
$start = microtime(true);
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 1000000, 3);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertTrue($elapsedTime > 3 && $elapsedTime < 4);
|
||||
$this->assertFalse($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_defaultLockRetryCount()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 10, true);
|
||||
usleep(100000);
|
||||
|
||||
$start = microtime(true);
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 200000, 0);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertTrue($elapsedTime > 2 && $elapsedTime < 3);
|
||||
$this->assertFalse($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_noUnlockOfOtherProcess()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 3, true, 1); // Process 1
|
||||
usleep(100000);
|
||||
$this->startSessionProcess($sessionId, 5, true); // Process 2
|
||||
|
||||
$start = microtime(true);
|
||||
// Waiting until TTL of process 1 ended and process 2 locked the session,
|
||||
// because is not guaranteed which waiting process gets the next lock
|
||||
sleep(1);
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertTrue($elapsedTime > 5);
|
||||
$this->assertTrue($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testSession_lockWaitTime()
|
||||
{
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 1, true, 300);
|
||||
usleep(100000);
|
||||
|
||||
$start = microtime(true);
|
||||
$sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000);
|
||||
$end = microtime(true);
|
||||
$elapsedTime = $end - $start;
|
||||
|
||||
$this->assertTrue($elapsedTime > 2.5);
|
||||
$this->assertTrue($elapsedTime < 3.5);
|
||||
$this->assertTrue($sessionSuccessful);
|
||||
}
|
||||
|
||||
public function testMultipleConnect() {
|
||||
@@ -5175,5 +5341,199 @@ class Redis_Test extends TestSuite
|
||||
$this->assertTrue(strpos($e, "timed out") !== false);
|
||||
}
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_noLock_noDestroy() {
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_noLock_withDestroy() {
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, false, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_withLock_noDestroy() {
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_withLock_withDestroy() {
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, true, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_noLock_noDestroy_withProxy() {
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
|
||||
}
|
||||
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, false, false, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_noLock_withDestroy_withProxy() {
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
|
||||
}
|
||||
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, false, true, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_withLock_noDestroy_withProxy() {
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
|
||||
}
|
||||
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, true, false, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
public function testSession_regenerateSessionId_withLock_withDestroy_withProxy() {
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
|
||||
}
|
||||
|
||||
$this->setSessionHandler();
|
||||
$sessionId = $this->generateSessionId();
|
||||
$this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
|
||||
|
||||
$newSessionId = $this->regenerateSessionId($sessionId, true, true, true);
|
||||
|
||||
$this->assertTrue($newSessionId !== $sessionId);
|
||||
$this->assertEquals('bar', $this->getSessionData($newSessionId));
|
||||
}
|
||||
|
||||
private function setSessionHandler()
|
||||
{
|
||||
$host = $this->getHost() ?: 'localhost';
|
||||
|
||||
ini_set('session.save_handler', 'redis');
|
||||
ini_set('session.save_path', 'tcp://' . $host . ':6379');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function generateSessionId()
|
||||
{
|
||||
if (function_exists('session_create_id')) {
|
||||
return session_create_id();
|
||||
} else {
|
||||
$encoded = bin2hex(openssl_random_pseudo_bytes(8));
|
||||
return $encoded;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
* @param int $sleepTime
|
||||
* @param bool $background
|
||||
* @param int $maxExecutionTime
|
||||
* @param bool $locking_enabled
|
||||
* @param int $lock_wait_time
|
||||
* @param int $lock_retries
|
||||
* @param int $lock_expires
|
||||
* @param string $sessionData
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function startSessionProcess($sessionId, $sleepTime, $background, $maxExecutionTime = 300, $locking_enabled = true, $lock_wait_time = null, $lock_retries = -1, $lock_expires = 0, $sessionData = '')
|
||||
{
|
||||
if (substr(php_uname(), 0, 7) == "Windows"){
|
||||
$this->markTestSkipped();
|
||||
return true;
|
||||
} else {
|
||||
$commandParameters = array($this->getHost(), $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData);
|
||||
if ($locking_enabled) {
|
||||
$commandParameters[] = '1';
|
||||
|
||||
if ($lock_wait_time != null) {
|
||||
$commandParameters[] = $lock_wait_time;
|
||||
}
|
||||
}
|
||||
$commandParameters = array_map('escapeshellarg', $commandParameters);
|
||||
|
||||
$command = 'php ' . __DIR__ . '/startSession.php ' . implode(' ', $commandParameters);
|
||||
$command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1';
|
||||
|
||||
exec($command, $output);
|
||||
return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSessionData($sessionId)
|
||||
{
|
||||
$command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getHost()) . ' ' . escapeshellarg($sessionId);
|
||||
exec($command, $output);
|
||||
|
||||
return $output[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
* @param bool $locking
|
||||
* @param bool $destroyPrevious
|
||||
* @param bool $sessionProxy
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false)
|
||||
{
|
||||
$args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious, $sessionProxy));
|
||||
|
||||
$command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getHost()) . ' ' . implode(' ', $args);
|
||||
|
||||
exec($command, $output);
|
||||
|
||||
return $output[0];
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
error_reporting(E_ERROR | E_WARNING);
|
||||
|
||||
$redisHost = $argv[1];
|
||||
$sessionId = $argv[2];
|
||||
|
||||
if (empty($redisHost)) {
|
||||
$redisHost = 'localhost';
|
||||
}
|
||||
|
||||
ini_set('session.save_handler', 'redis');
|
||||
ini_set('session.save_path', 'tcp://' . $redisHost . ':6379');
|
||||
|
||||
session_id($sessionId);
|
||||
if (!session_start()) {
|
||||
echo "session_start() was nut successful";
|
||||
} else {
|
||||
echo isset($_SESSION['redis_test']) ? $_SESSION['redis_test'] : 'Key redis_test not found';
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
error_reporting(E_ERROR | E_WARNING);
|
||||
|
||||
$redisHost = $argv[1];
|
||||
$sessionId = $argv[2];
|
||||
$locking = !!$argv[3];
|
||||
$destroyPrevious = !!$argv[4];
|
||||
$sessionProxy = !!$argv[5];
|
||||
|
||||
if (empty($redisHost)) {
|
||||
$redisHost = 'localhost';
|
||||
}
|
||||
|
||||
ini_set('session.save_handler', 'redis');
|
||||
ini_set('session.save_path', 'tcp://' . $redisHost . ':6379');
|
||||
|
||||
if ($locking) {
|
||||
ini_set('redis.session.locking_enabled', true);
|
||||
}
|
||||
|
||||
if (interface_exists('SessionHandlerInterface')) {
|
||||
class TestHandler implements SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $handler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->handler = new SessionHandler();
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
return $this->handler->close();
|
||||
}
|
||||
|
||||
public function destroy($session_id)
|
||||
{
|
||||
return $this->handler->destroy($session_id);
|
||||
}
|
||||
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->handler->gc($maxlifetime);
|
||||
}
|
||||
|
||||
public function open($save_path, $name)
|
||||
{
|
||||
return $this->handler->open($save_path, $name);
|
||||
}
|
||||
|
||||
public function read($session_id)
|
||||
{
|
||||
return $this->handler->read($session_id);
|
||||
}
|
||||
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
return $this->handler->write($session_id, $session_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($sessionProxy) {
|
||||
$handler = new TestHandler();
|
||||
session_set_save_handler($handler);
|
||||
}
|
||||
|
||||
session_id($sessionId);
|
||||
if (!session_start()) {
|
||||
$result = "FAILED: session_start()";
|
||||
}
|
||||
elseif (!session_regenerate_id($destroyPrevious)) {
|
||||
$result = "FAILED: session_regenerate_id()";
|
||||
}
|
||||
else {
|
||||
$result = session_id();
|
||||
}
|
||||
session_write_close();
|
||||
echo $result;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
error_reporting(E_ERROR | E_WARNING);
|
||||
|
||||
$redisHost = $argv[1];
|
||||
$sessionId = $argv[2];
|
||||
$sleepTime = $argv[3];
|
||||
$maxExecutionTime = $argv[4];
|
||||
$lock_retries = $argv[5];
|
||||
$lock_expire = $argv[6];
|
||||
$sessionData = $argv[7];
|
||||
|
||||
if (empty($redisHost)) {
|
||||
$redisHost = 'localhost';
|
||||
}
|
||||
|
||||
ini_set('session.save_handler', 'redis');
|
||||
ini_set('session.save_path', 'tcp://' . $redisHost . ':6379');
|
||||
ini_set('max_execution_time', $maxExecutionTime);
|
||||
ini_set('redis.session.lock_retries', $lock_retries);
|
||||
ini_set('redis.session.lock_expire', $lock_expire);
|
||||
|
||||
if (isset($argv[8])) {
|
||||
ini_set('redis.session.locking_enabled', $argv[8]);
|
||||
}
|
||||
|
||||
if (isset($argv[9])) {
|
||||
ini_set('redis.session.lock_wait_time', $argv[9]);
|
||||
}
|
||||
|
||||
session_id($sessionId);
|
||||
$sessionStartSuccessful = session_start();
|
||||
sleep($sleepTime);
|
||||
if (!empty($sessionData)) {
|
||||
$_SESSION['redis_test'] = $sessionData;
|
||||
}
|
||||
session_write_close();
|
||||
|
||||
echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE';
|
||||
Reference in New Issue
Block a user