Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b6cf636

Browse files
zorro-fr24michael-grunder
authored andcommitted
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
1 parent 79c9d22 commit b6cf636

4 files changed

Lines changed: 185 additions & 4 deletions

File tree

cluster.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,12 @@ The save path for cluster based session storage takes the form of a PHP GET requ
193193
* _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing).
194194
* _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands.
195195
* _stream (array)_: ssl/tls stream context options.
196+
197+
### redis.session.early_refresh
198+
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 ).
199+
200+
To enable, set the following INI variable:
201+
```ini
202+
redis.session.early_refresh = 1
203+
```
204+
Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required.

redis.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ PHP_INI_BEGIN()
110110
PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
111111
PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL)
112112
PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL)
113+
PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL)
113114
PHP_INI_END()
114115

115116
static const zend_module_dep redis_deps[] = {

redis_session.c

Lines changed: 172 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ ps_module ps_mod_redis = {
6060
};
6161

6262
ps_module ps_mod_redis_cluster = {
63-
PS_MOD(rediscluster)
63+
PS_MOD_UPDATE_TIMESTAMP(rediscluster)
6464
};
6565

6666
typedef struct {
@@ -982,6 +982,166 @@ PS_OPEN_FUNC(rediscluster) {
982982
return FAILURE;
983983
}
984984

985+
/* {{{ PS_CREATE_SID_FUNC
986+
*/
987+
PS_CREATE_SID_FUNC(rediscluster)
988+
{
989+
redisCluster *c = PS_GET_MOD_DATA();
990+
clusterReply *reply;
991+
char *cmd, *skey;
992+
zend_string *sid;
993+
int cmdlen, skeylen;
994+
int retries = 3;
995+
short slot;
996+
997+
if (!c) {
998+
return php_session_create_id(NULL);
999+
}
1000+
1001+
if (INI_INT("session.use_strict_mode") == 0) {
1002+
return php_session_create_id((void **) &c);
1003+
}
1004+
1005+
while (retries-- > 0) {
1006+
sid = php_session_create_id((void **) &c);
1007+
1008+
/* Create session key if it doesn't already exist */
1009+
skey = cluster_session_key(c, ZSTR_VAL(sid), ZSTR_LEN(sid), &skeylen, &slot);
1010+
cmdlen = redis_spprintf(NULL, NULL, &cmd, "SET", "ssssd", skey,
1011+
skeylen, "", 0, "NX", 2, "EX", 2, session_gc_maxlifetime());
1012+
1013+
efree(skey);
1014+
1015+
/* Attempt to kick off our command */
1016+
c->readonly = 0;
1017+
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
1018+
php_error_docref(NULL, E_NOTICE, "Redis connection not available");
1019+
efree(cmd);
1020+
zend_string_release(sid);
1021+
return php_session_create_id(NULL);;
1022+
}
1023+
1024+
efree(cmd);
1025+
1026+
/* Attempt to read reply */
1027+
reply = cluster_read_resp(c, 1);
1028+
1029+
if (!reply || c->err) {
1030+
php_error_docref(NULL, E_NOTICE, "Unable to read redis response");
1031+
} else if (reply->len > 0) {
1032+
cluster_free_reply(reply, 1);
1033+
break;
1034+
} else {
1035+
php_error_docref(NULL, E_NOTICE, "Redis sid collision on %s, retrying %d time(s)", sid->val, retries);
1036+
}
1037+
1038+
if (reply) {
1039+
cluster_free_reply(reply, 1);
1040+
}
1041+
1042+
zend_string_release(sid);
1043+
sid = NULL;
1044+
}
1045+
1046+
return sid;
1047+
}
1048+
/* }}} */
1049+
1050+
/* {{{ PS_VALIDATE_SID_FUNC
1051+
*/
1052+
PS_VALIDATE_SID_FUNC(rediscluster)
1053+
{
1054+
redisCluster *c = PS_GET_MOD_DATA();
1055+
clusterReply *reply;
1056+
char *cmd, *skey;
1057+
int cmdlen, skeylen;
1058+
int res = FAILURE;
1059+
short slot;
1060+
1061+
/* Check key is valid and whether it already exists */
1062+
if (php_session_valid_key(ZSTR_VAL(key)) == FAILURE) {
1063+
php_error_docref(NULL, E_NOTICE, "Invalid session key: %s", ZSTR_VAL(key));
1064+
return FAILURE;
1065+
}
1066+
1067+
skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
1068+
cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXISTS", "s", skey, skeylen);
1069+
efree(skey);
1070+
1071+
/* We send to master, to ensure consistency */
1072+
c->readonly = 0;
1073+
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
1074+
php_error_docref(NULL, E_NOTICE, "Redis connection not available");
1075+
efree(cmd);
1076+
return FAILURE;
1077+
}
1078+
1079+
efree(cmd);
1080+
1081+
/* Attempt to read reply */
1082+
reply = cluster_read_resp(c, 0);
1083+
1084+
if (!reply || c->err) {
1085+
php_error_docref(NULL, E_NOTICE, "Unable to read redis response");
1086+
res = FAILURE;
1087+
} else if (reply->integer == 1) {
1088+
res = SUCCESS;
1089+
}
1090+
1091+
/* Clean up */
1092+
if (reply) {
1093+
cluster_free_reply(reply, 1);
1094+
}
1095+
1096+
return res;
1097+
}
1098+
/* }}} */
1099+
1100+
/* {{{ PS_UPDATE_TIMESTAMP_FUNC
1101+
*/
1102+
PS_UPDATE_TIMESTAMP_FUNC(rediscluster) {
1103+
redisCluster *c = PS_GET_MOD_DATA();
1104+
clusterReply *reply;
1105+
char *cmd, *skey;
1106+
int cmdlen, skeylen;
1107+
short slot;
1108+
1109+
/* No need to update the session timestamp if we've already done so */
1110+
if (INI_INT("redis.session.early_refresh")) {
1111+
return SUCCESS;
1112+
}
1113+
1114+
/* Set up command and slot info */
1115+
skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
1116+
cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXPIRE", "sd", skey,
1117+
skeylen, session_gc_maxlifetime());
1118+
efree(skey);
1119+
1120+
/* Attempt to send EXPIRE command */
1121+
c->readonly = 0;
1122+
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
1123+
php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry");
1124+
efree(cmd);
1125+
return FAILURE;
1126+
}
1127+
1128+
/* Clean up our command */
1129+
efree(cmd);
1130+
1131+
/* Attempt to read reply */
1132+
reply = cluster_read_resp(c, 0);
1133+
if (!reply || c->err) {
1134+
if (reply) cluster_free_reply(reply, 1);
1135+
return FAILURE;
1136+
}
1137+
1138+
/* Clean up */
1139+
cluster_free_reply(reply, 1);
1140+
1141+
return SUCCESS;
1142+
}
1143+
/* }}} */
1144+
9851145
/* {{{ PS_READ_FUNC
9861146
*/
9871147
PS_READ_FUNC(rediscluster) {
@@ -994,11 +1154,19 @@ PS_READ_FUNC(rediscluster) {
9941154
/* Set up our command and slot information */
9951155
skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot);
9961156

997-
cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen);
1157+
/* Update the session ttl if early refresh is enabled */
1158+
if (INI_INT("redis.session.early_refresh")) {
1159+
cmdlen = redis_spprintf(NULL, NULL, &cmd, "GETEX", "ssd", skey,
1160+
skeylen, "EX", 2, session_gc_maxlifetime());
1161+
c->readonly = 0;
1162+
} else {
1163+
cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen);
1164+
c->readonly = 1;
1165+
}
1166+
9981167
efree(skey);
9991168

10001169
/* Attempt to kick off our command */
1001-
c->readonly = 1;
10021170
if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) {
10031171
efree(cmd);
10041172
return FAILURE;
@@ -1126,4 +1294,4 @@ PS_GC_FUNC(rediscluster) {
11261294

11271295
#endif
11281296

1129-
/* vim: set tabstop=4 expandtab: */
1297+
/* vim: set tabstop=4 expandtab: */

redis_session.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ PS_READ_FUNC(rediscluster);
2020
PS_WRITE_FUNC(rediscluster);
2121
PS_DESTROY_FUNC(rediscluster);
2222
PS_GC_FUNC(rediscluster);
23+
PS_CREATE_SID_FUNC(rediscluster);
24+
PS_VALIDATE_SID_FUNC(rediscluster);
25+
PS_UPDATE_TIMESTAMP_FUNC(rediscluster);
2326

2427
#endif
2528
#endif

0 commit comments

Comments
 (0)