mirror of
https://github.com/php-win-ext/phpredis.git
synced 2026-03-24 00:52:16 +01:00
Add cluster support for strict sessions and lazy write
* Add ini setting redis.session.early_refresh to allow for session TTL updates on session start ( requires redis server version 6.2 or greater ) * Enable cluster session support for strict mode sessions ( via PS_VALIDATE_SID_FUNC ) * Cluster sessions used to write on every session, now we only write if the session has been modified. * Send EXPIRE instead of SETEX if sessioh has not been changed * If early refresh is enabled use GETEX for initial session read * When strict sessions are enabled, check whether the session exists first, validate sid and regenerate if necessary
This commit is contained in:
committed by
Michael Grunder
parent
79c9d2241f
commit
b6cf6361dd
@@ -193,3 +193,12 @@ The save path for cluster based session storage takes the form of a PHP GET requ
|
||||
* _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing).
|
||||
* _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands.
|
||||
* _stream (array)_: ssl/tls stream context options.
|
||||
|
||||
### redis.session.early_refresh
|
||||
Under normal operation, the client will refresh the session's expiry ttl whenever the session is closed. However, we can save this additional round-trip by updating the ttl when the session is opened instead ( This means that sessions that have not been modified will not send further commands to the server ).
|
||||
|
||||
To enable, set the following INI variable:
|
||||
```ini
|
||||
redis.session.early_refresh = 1
|
||||
```
|
||||
Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required.
|
||||
1
redis.c
1
redis.c
@@ -110,6 +110,7 @@ PHP_INI_BEGIN()
|
||||
PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL)
|
||||
PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL)
|
||||
PHP_INI_END()
|
||||
|
||||
static const zend_module_dep redis_deps[] = {
|
||||
|
||||
176
redis_session.c
176
redis_session.c
@@ -60,7 +60,7 @@ ps_module ps_mod_redis = {
|
||||
};
|
||||
|
||||
ps_module ps_mod_redis_cluster = {
|
||||
PS_MOD(rediscluster)
|
||||
PS_MOD_UPDATE_TIMESTAMP(rediscluster)
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -982,6 +982,166 @@ failure:
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* {{{ PS_CREATE_SID_FUNC
|
||||
*/
|
||||
PS_CREATE_SID_FUNC(rediscluster)
|
||||
{
|
||||
redisCluster *c = PS_GET_MOD_DATA();
|
||||
clusterReply *reply;
|
||||
char *cmd, *skey;
|
||||
zend_string *sid;
|
||||
int cmdlen, skeylen;
|
||||
int retries = 3;
|
||||
short slot;
|
||||
|
||||
if (!c) {
|
||||
return php_session_create_id(NULL);
|
||||
}
|
||||
|
||||
if (INI_INT("session.use_strict_mode") == 0) {
|
||||
return php_session_create_id((void **) &c);
|
||||
}
|
||||
|
||||
while (retries-- > 0) {
|
||||
sid = php_session_create_id((void **) &c);
|
||||
|
||||
/* Create session key if it doesn't already exist */
|
||||
skey = cluster_session_key(c, ZSTR_VAL(sid), ZSTR_LEN(sid), &skeylen, &slot);
|
||||
cmdlen = redis_spprintf(NULL, NULL, &cmd, "SET", "ssssd", skey,
|
||||
skeylen, "", 0, "NX", 2, "EX", 2, session_gc_maxlifetime());
|
||||
|
||||
efree(skey);
|
||||
|
||||
/* Attempt to kick off our command */
|
||||
c->readonly = 0;
|
||||
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
|
||||
php_error_docref(NULL, E_NOTICE, "Redis connection not available");
|
||||
efree(cmd);
|
||||
zend_string_release(sid);
|
||||
return php_session_create_id(NULL);;
|
||||
}
|
||||
|
||||
efree(cmd);
|
||||
|
||||
/* Attempt to read reply */
|
||||
reply = cluster_read_resp(c, 1);
|
||||
|
||||
if (!reply || c->err) {
|
||||
php_error_docref(NULL, E_NOTICE, "Unable to read redis response");
|
||||
} else if (reply->len > 0) {
|
||||
cluster_free_reply(reply, 1);
|
||||
break;
|
||||
} else {
|
||||
php_error_docref(NULL, E_NOTICE, "Redis sid collision on %s, retrying %d time(s)", sid->val, retries);
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
cluster_free_reply(reply, 1);
|
||||
}
|
||||
|
||||
zend_string_release(sid);
|
||||
sid = NULL;
|
||||
}
|
||||
|
||||
return sid;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ PS_VALIDATE_SID_FUNC
|
||||
*/
|
||||
PS_VALIDATE_SID_FUNC(rediscluster)
|
||||
{
|
||||
redisCluster *c = PS_GET_MOD_DATA();
|
||||
clusterReply *reply;
|
||||
char *cmd, *skey;
|
||||
int cmdlen, skeylen;
|
||||
int res = FAILURE;
|
||||
short slot;
|
||||
|
||||
/* Check key is valid and whether it already exists */
|
||||
if (php_session_valid_key(ZSTR_VAL(key)) == FAILURE) {
|
||||
php_error_docref(NULL, E_NOTICE, "Invalid session key: %s", ZSTR_VAL(key));
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
|
||||
cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXISTS", "s", skey, skeylen);
|
||||
efree(skey);
|
||||
|
||||
/* We send to master, to ensure consistency */
|
||||
c->readonly = 0;
|
||||
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
|
||||
php_error_docref(NULL, E_NOTICE, "Redis connection not available");
|
||||
efree(cmd);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
efree(cmd);
|
||||
|
||||
/* Attempt to read reply */
|
||||
reply = cluster_read_resp(c, 0);
|
||||
|
||||
if (!reply || c->err) {
|
||||
php_error_docref(NULL, E_NOTICE, "Unable to read redis response");
|
||||
res = FAILURE;
|
||||
} else if (reply->integer == 1) {
|
||||
res = SUCCESS;
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
if (reply) {
|
||||
cluster_free_reply(reply, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ PS_UPDATE_TIMESTAMP_FUNC
|
||||
*/
|
||||
PS_UPDATE_TIMESTAMP_FUNC(rediscluster) {
|
||||
redisCluster *c = PS_GET_MOD_DATA();
|
||||
clusterReply *reply;
|
||||
char *cmd, *skey;
|
||||
int cmdlen, skeylen;
|
||||
short slot;
|
||||
|
||||
/* No need to update the session timestamp if we've already done so */
|
||||
if (INI_INT("redis.session.early_refresh")) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/* Set up command and slot info */
|
||||
skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
|
||||
cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXPIRE", "sd", skey,
|
||||
skeylen, session_gc_maxlifetime());
|
||||
efree(skey);
|
||||
|
||||
/* Attempt to send EXPIRE command */
|
||||
c->readonly = 0;
|
||||
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
|
||||
php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry");
|
||||
efree(cmd);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* Clean up our command */
|
||||
efree(cmd);
|
||||
|
||||
/* Attempt to read reply */
|
||||
reply = cluster_read_resp(c, 0);
|
||||
if (!reply || c->err) {
|
||||
if (reply) cluster_free_reply(reply, 1);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
cluster_free_reply(reply, 1);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ PS_READ_FUNC
|
||||
*/
|
||||
PS_READ_FUNC(rediscluster) {
|
||||
@@ -994,11 +1154,19 @@ PS_READ_FUNC(rediscluster) {
|
||||
/* Set up our command and slot information */
|
||||
skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
|
||||
|
||||
cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen);
|
||||
/* Update the session ttl if early refresh is enabled */
|
||||
if (INI_INT("redis.session.early_refresh")) {
|
||||
cmdlen = redis_spprintf(NULL, NULL, &cmd, "GETEX", "ssd", skey,
|
||||
skeylen, "EX", 2, session_gc_maxlifetime());
|
||||
c->readonly = 0;
|
||||
} else {
|
||||
cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen);
|
||||
c->readonly = 1;
|
||||
}
|
||||
|
||||
efree(skey);
|
||||
|
||||
/* Attempt to kick off our command */
|
||||
c->readonly = 1;
|
||||
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
|
||||
efree(cmd);
|
||||
return FAILURE;
|
||||
@@ -1126,4 +1294,4 @@ PS_GC_FUNC(rediscluster) {
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set tabstop=4 expandtab: */
|
||||
/* vim: set tabstop=4 expandtab: */
|
||||
@@ -20,6 +20,9 @@ PS_READ_FUNC(rediscluster);
|
||||
PS_WRITE_FUNC(rediscluster);
|
||||
PS_DESTROY_FUNC(rediscluster);
|
||||
PS_GC_FUNC(rediscluster);
|
||||
PS_CREATE_SID_FUNC(rediscluster);
|
||||
PS_VALIDATE_SID_FUNC(rediscluster);
|
||||
PS_UPDATE_TIMESTAMP_FUNC(rediscluster);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user