Skip to content

Commit e3a98ce

Browse files
committed
Merge remote-tracking branch 'origin/unstable' into race-empty-primary-with-test
2 parents 9522e07 + d16788e commit e3a98ce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+2959
-681
lines changed

.github/workflows/daily.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ jobs:
12531253
version: 13.2
12541254
shell: bash
12551255
run: |
1256-
sudo pkg install -y bash gmake lang/tcl86 lang/tclx
1256+
sudo pkg install -y bash gmake lang/tcl86 lang/tclX
12571257
gmake
12581258
./runtest --single unit/keyspace --single unit/auth --single unit/networking --single unit/protocol
12591259

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
5252

5353
% make USE_SYSTEMD=yes
5454

55+
Since Valkey version 8.1, `fast_float` has been introduced as an optional
56+
dependency, which can speed up sorted sets and other commands that use
57+
the double datatype. To build with `fast_float` support, you'll need a
58+
C++ compiler and run:
59+
60+
% make USE_FAST_FLOAT=yes
61+
5562
To append a suffix to Valkey program names, use:
5663

5764
% make PROG_SUFFIX="-alt"

sentinel.conf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,11 @@ sentinel monitor mymaster 127.0.0.1 6379 2
119119
# In the Valkey servers side, the ACL to provide just minimal access to
120120
# Sentinel instances, should be configured along the following lines:
121121
#
122-
# user sentinel-user >somepassword +client +subscribe +publish \
122+
# user sentinel-user >somepassword +subscribe +publish +failover +script|kill \
123123
# +ping +info +multi +slaveof +config +client +exec &__sentinel__:hello on
124+
#
125+
# Since Valkey Sentinel 9.0, the sentinel user requires the +failover permission
126+
# on all monitored Valkey instances for proper operation.
124127

125128
# sentinel down-after-milliseconds <master-name> <milliseconds>
126129
#

src/acl.c

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include <fcntl.h>
3434
#include <ctype.h>
3535

36+
#include "script.h"
37+
3638
/* =============================================================================
3739
* Global state for ACLs
3840
* ==========================================================================*/
@@ -1541,11 +1543,25 @@ user *ACLGetUserByName(const char *name, size_t namelen) {
15411543
* ACL permission checks
15421544
* ==========================================================================*/
15431545

1544-
/* Check if the key can be accessed by the selector.
1546+
/* Checks whether a given key is allowed for the specified ACL selector.
1547+
*
1548+
* The function evaluates the key against the selector's patterns, taking into account
1549+
* the requested key access flags (read/write) and whether the key should be
1550+
* treated as a prefix. If the selector has the SELECTOR_FLAG_ALLKEYS flag set,
1551+
* the check always succeeds.
15451552
*
1546-
* If the selector can access the key, ACL_OK is returned, otherwise
1547-
* ACL_DENIED_KEY is returned. */
1548-
static int ACLSelectorCheckKey(aclSelector *selector, const char *key, int keylen, int keyspec_flags) {
1553+
* - selector The ACL selector containing the allowed key patterns.
1554+
* - key Pointer to the key string to check.
1555+
* - keylen Length of the key in bytes.
1556+
* - keyspec_flags Bitmask of requested key operations (e.g., CMD_KEY_ACCESS,
1557+
* CMD_KEY_INSERT, CMD_KEY_DELETE, CMD_KEY_UPDATE).
1558+
* - is_prefix If true, the key is compared as a prefix against the selector's
1559+
* patterns using prefixmatchlen rather than a full string match.
1560+
*
1561+
* This function returns ACL_OK if the key matches at least one pattern that allows the requested
1562+
* operations; otherwise ACL_DENIED_KEY.
1563+
*/
1564+
static int ACLSelectorCheckKey(aclSelector *selector, const char *key, int keylen, int keyspec_flags, bool is_prefix) {
15491565
/* The selector can access any key */
15501566
if (selector->flags & SELECTOR_FLAG_ALLKEYS) return ACL_OK;
15511567

@@ -1564,6 +1580,12 @@ static int ACLSelectorCheckKey(aclSelector *selector, const char *key, int keyle
15641580
keyPattern *pattern = listNodeValue(ln);
15651581
if ((pattern->flags & key_flags) != key_flags) continue;
15661582
size_t plen = sdslen(pattern->pattern);
1583+
if (is_prefix) {
1584+
if (prefixmatchlen(pattern->pattern, plen, key, keylen, 0))
1585+
return ACL_OK;
1586+
else
1587+
continue;
1588+
}
15671589
if (stringmatchlen(pattern->pattern, plen, key, keylen, 0)) return ACL_OK;
15681590
}
15691591
return ACL_DENIED_KEY;
@@ -1688,7 +1710,7 @@ static int ACLSelectorCheckCmd(aclSelector *selector,
16881710
keyReference *resultidx = result->keys;
16891711
for (int j = 0; j < result->numkeys; j++) {
16901712
int idx = resultidx[j].pos;
1691-
ret = ACLSelectorCheckKey(selector, argv[idx]->ptr, sdslen(argv[idx]->ptr), resultidx[j].flags);
1713+
ret = ACLSelectorCheckKey(selector, argv[idx]->ptr, sdslen(argv[idx]->ptr), resultidx[j].flags, false);
16921714
if (ret != ACL_OK) {
16931715
if (keyidxptr) *keyidxptr = resultidx[j].pos;
16941716
return ret;
@@ -1721,13 +1743,30 @@ static int ACLSelectorCheckCmd(aclSelector *selector,
17211743
return ACL_OK;
17221744
}
17231745

1724-
/* Check if the key can be accessed by the client according to
1725-
* the ACLs associated with the specified user according to the
1726-
* keyspec access flags.
1746+
/* Checks whether the given user has permission to access a specified key.
17271747
*
1728-
* If the user can access the key, ACL_OK is returned, otherwise
1729-
* ACL_DENIED_KEY is returned. */
1730-
int ACLUserCheckKeyPerm(user *u, const char *key, int keylen, int flags) {
1748+
* This function verifies the access control list (ACL) permissions for a user on a key.
1749+
* If the user pointer is NULL, the connection is considered authorized to run any
1750+
* command and the function returns ACL_OK. Otherwise, the function iterates
1751+
* through the user's ACL selectors and returns ACL_OK as soon as any selector
1752+
* grants permission. If no selector grants permission, ACL_DENIED_KEY is returned.
1753+
*
1754+
* Input parameters:
1755+
*
1756+
* - u Pointer to a user structure. If NULL, the user is treated as
1757+
* having unrestricted access.
1758+
* - key Pointer to the key string to check.
1759+
* - keylen Length of the key string in bytes.
1760+
* - flags Flags that may influence selector checks (implementation-specific).
1761+
* - is_prefix true if key should be treated as a prefix in selector
1762+
* evaluation; false otherwise.
1763+
*
1764+
* Returns ACL_OK if access is granted, ACL_DENIED_KEY if access is denied.
1765+
*
1766+
* NOTE: The function performs no modification of the user structure or the key;
1767+
* it only evaluates read-only selectors.
1768+
*/
1769+
int ACLUserCheckKeyPerm(user *u, const char *key, int keylen, int flags, bool is_prefix) {
17311770
listIter li;
17321771
listNode *ln;
17331772

@@ -1738,7 +1777,7 @@ int ACLUserCheckKeyPerm(user *u, const char *key, int keylen, int flags) {
17381777
listRewind(u->selectors, &li);
17391778
while ((ln = listNext(&li))) {
17401779
aclSelector *s = (aclSelector *)listNodeValue(ln);
1741-
if (ACLSelectorCheckKey(s, key, keylen, flags) == ACL_OK) {
1780+
if (ACLSelectorCheckKey(s, key, keylen, flags, is_prefix) == ACL_OK) {
17421781
return ACL_OK;
17431782
}
17441783
}
@@ -2546,7 +2585,7 @@ void ACLLoadUsersAtStartup(void) {
25462585
typedef struct ACLLogEntry {
25472586
uint64_t count; /* Number of times this happened recently. */
25482587
int reason; /* Reason for denying the command. ACL_DENIED_*. */
2549-
int context; /* Toplevel, Lua or MULTI/EXEC? ACL_LOG_CTX_*. */
2588+
int context; /* Toplevel, Lua, Script or MULTI/EXEC? ACL_LOG_CTX_*. */
25502589
sds object; /* The key name or command name. */
25512590
sds username; /* User the client is authenticated with. */
25522591
mstime_t ctime; /* Milliseconds time of last update to this entry. */
@@ -2654,6 +2693,15 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
26542693
client *realclient = server.current_client ? server.current_client : c;
26552694

26562695
le->cinfo = catClientInfoString(sdsempty(), realclient, 0);
2696+
2697+
if (context == ACL_LOG_CTX_SCRIPT &&
2698+
strcmp(scriptGetRunningEngineName(), "LUA") == 0) {
2699+
/* For backward compatibility, we track that it's Lua using a special
2700+
* lua ACL log context. Any other scripting language is just "script" in
2701+
* the ACL log. */
2702+
context = ACL_LOG_CTX_LUA;
2703+
}
2704+
26572705
le->context = context;
26582706

26592707
/* Try to match this entry with past ones, to see if we can just
@@ -3044,6 +3092,7 @@ void aclCommand(client *c) {
30443092
case ACL_LOG_CTX_MULTI: ctxstr = "multi"; break;
30453093
case ACL_LOG_CTX_LUA: ctxstr = "lua"; break;
30463094
case ACL_LOG_CTX_MODULE: ctxstr = "module"; break;
3095+
case ACL_LOG_CTX_SCRIPT: ctxstr = "script"; break;
30473096
default: ctxstr = "unknown";
30483097
}
30493098
addReplyBulkCString(c, ctxstr);

src/aof.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,7 +1452,14 @@ int loadSingleAppendOnlyFile(char *filename) {
14521452

14531453
if (fseek(fp, 0, SEEK_SET) == -1) goto readerr;
14541454
rioInitWithFile(&rdb, fp);
1455-
if (rdbLoadRio(&rdb, RDBFLAGS_AOF_PREAMBLE, NULL) != C_OK) {
1455+
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
1456+
int rdb_flags = RDBFLAGS_AOF_PREAMBLE;
1457+
int rsi_is_valid = 0;
1458+
if (iAmPrimary()) {
1459+
if (server.repl_backlog == NULL) createReplicationBacklog();
1460+
rdb_flags |= RDBFLAGS_FEED_REPL;
1461+
}
1462+
if (rdbLoadRio(&rdb, rdb_flags, &rsi) != RDB_OK) {
14561463
if (old_style)
14571464
serverLog(LL_WARNING, "Error reading the RDB preamble of the AOF file %s, AOF loading aborted",
14581465
filename);
@@ -1462,10 +1469,15 @@ int loadSingleAppendOnlyFile(char *filename) {
14621469
ret = AOF_FAILED;
14631470
goto cleanup;
14641471
} else {
1472+
/* Restore the replication ID / offset from the RDB file. */
1473+
rsi_is_valid = rdbRestoreOffsetFromSaveInfo(&rsi, true);
14651474
loadingAbsProgress(ftello(fp));
14661475
last_progress_report_size = ftello(fp);
14671476
if (old_style) serverLog(LL_NOTICE, "Reading the remaining AOF tail...");
14681477
}
1478+
/* If the AOF didn't contain replication info, it's not possible to
1479+
* support partial resync, so we can free the backlog to save memory. */
1480+
if (!rsi_is_valid && server.repl_backlog && listLength(server.replicas) == 0) freeReplicationBacklog();
14691481
}
14701482

14711483
/* Read the actual AOF file, in REPL format, command by command. */
@@ -1910,7 +1922,7 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
19101922
return 0;
19111923
}
19121924
}
1913-
sds ele = node->ele;
1925+
sds ele = zslGetNodeElement(node);
19141926
if (!rioWriteBulkDouble(r, node->score) || !rioWriteBulkString(r, ele, sdslen(ele))) {
19151927
hashtableResetIterator(&iter);
19161928
return 0;
@@ -2400,8 +2412,10 @@ int rewriteAppendOnlyFile(char *filename) {
24002412
startSaving(RDBFLAGS_AOF_PREAMBLE);
24012413

24022414
if (server.aof_use_rdb_preamble) {
2415+
rdbSaveInfo rsi, *rsiptr;
2416+
rsiptr = rdbPopulateSaveInfo(&rsi);
24032417
int error;
2404-
if (rdbSaveRio(REPLICA_REQ_NONE, &aof, &error, RDBFLAGS_AOF_PREAMBLE, NULL) == C_ERR) {
2418+
if (rdbSaveRio(REPLICA_REQ_NONE, &aof, &error, RDBFLAGS_AOF_PREAMBLE, rsiptr) == C_ERR) {
24052419
errno = error;
24062420
goto werr;
24072421
}

src/call_reply.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#define REPLY_FLAG_ROOT (1 << 0)
3434
#define REPLY_FLAG_PARSED (1 << 1)
3535
#define REPLY_FLAG_RESP3 (1 << 2)
36+
#define REPLY_FLAG_EXACT_TYPE (1 << 3)
3637

3738
/* --------------------------------------------------------
3839
* An opaque struct used to parse a RESP protocol reply and
@@ -83,7 +84,9 @@ static void callReplyNullBulkString(void *ctx, const char *proto, size_t proto_l
8384

8485
static void callReplyNullArray(void *ctx, const char *proto, size_t proto_len) {
8586
CallReply *rep = ctx;
86-
callReplySetSharedData(rep, VALKEYMODULE_REPLY_NULL, proto, proto_len, 0);
87+
int type = rep->flags & REPLY_FLAG_EXACT_TYPE ? VALKEYMODULE_REPLY_ARRAY_NULL
88+
: VALKEYMODULE_REPLY_NULL;
89+
callReplySetSharedData(rep, type, proto, proto_len, 0);
8790
}
8891

8992
static void callReplyBulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
@@ -102,7 +105,9 @@ static void callReplyError(void *ctx, const char *str, size_t len, const char *p
102105

103106
static void callReplySimpleStr(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
104107
CallReply *rep = ctx;
105-
callReplySetSharedData(rep, VALKEYMODULE_REPLY_STRING, proto, proto_len, 0);
108+
int type = rep->flags & REPLY_FLAG_EXACT_TYPE ? VALKEYMODULE_REPLY_SIMPLE_STRING
109+
: VALKEYMODULE_REPLY_STRING;
110+
callReplySetSharedData(rep, type, proto, proto_len, 0);
106111
rep->len = len;
107112
rep->val.str = str;
108113
}
@@ -306,6 +311,7 @@ int callReplyType(CallReply *rep) {
306311

307312
/* Return reply string as buffer and len. Applicable to:
308313
* - VALKEYMODULE_REPLY_STRING
314+
* - VALKEYMODULE_REPLY_SIMPLE_STRING
309315
* - VALKEYMODULE_REPLY_ERROR
310316
*
311317
* The return value is borrowed from CallReply, so it must not be freed
@@ -316,7 +322,9 @@ int callReplyType(CallReply *rep) {
316322
*/
317323
const char *callReplyGetString(CallReply *rep, size_t *len) {
318324
callReplyParse(rep);
319-
if (rep->type != VALKEYMODULE_REPLY_STRING && rep->type != VALKEYMODULE_REPLY_ERROR) return NULL;
325+
if (rep->type != VALKEYMODULE_REPLY_STRING &&
326+
rep->type != VALKEYMODULE_REPLY_SIMPLE_STRING &&
327+
rep->type != VALKEYMODULE_REPLY_ERROR) return NULL;
320328
if (len) *len = rep->len;
321329
return rep->val.str;
322330
}
@@ -563,3 +571,14 @@ CallReply *callReplyCreateError(sds reply, void *private_data) {
563571
listAddNodeTail(deferred_error_list, sdsnew(err_buff));
564572
return callReplyCreate(err_buff, deferred_error_list, private_data);
565573
}
574+
575+
/* Enable exact reply type parsing to preserve type distinctions.
576+
*
577+
* This flag maintains the distinction between simple strings and bulk strings,
578+
* as well as preserving RESP2's distinction between null bulk strings and null arrays
579+
* when parsing the CallReply.
580+
*/
581+
void enableParseExactReplyTypeFlag(CallReply *rep) {
582+
serverAssert(!(rep->flags & REPLY_FLAG_PARSED));
583+
rep->flags |= REPLY_FLAG_EXACT_TYPE;
584+
}

src/call_reply.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,6 @@ int callReplyIsResp3(CallReply *rep);
5656
list *callReplyDeferredErrorList(CallReply *rep);
5757
void freeCallReply(CallReply *rep);
5858
CallReply *callReplyCreatePromise(void *private_data);
59+
void enableParseExactReplyTypeFlag(CallReply *rep);
5960

6061
#endif /* SRC_CALL_REPLY_H_ */

src/cli_common.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ void parseUri(const char *uri, const char *tool_name, cliConnInfo *connInfo, int
331331
!strncasecmp(redisTlsscheme, curr, strlen(redisTlsscheme))) {
332332
#ifdef USE_OPENSSL
333333
*tls_flag = 1;
334-
char *del = strstr(curr, "://");
334+
const char *del = strstr(curr, "://");
335335
curr += (del - curr) + 3;
336336
#else
337337
char *copy = strdup(curr);
@@ -341,7 +341,7 @@ void parseUri(const char *uri, const char *tool_name, cliConnInfo *connInfo, int
341341
exit(1);
342342
#endif
343343
} else if (!strncasecmp(scheme, curr, strlen(scheme)) || !strncasecmp(redisScheme, curr, strlen(redisScheme))) {
344-
char *del = strstr(curr, "://");
344+
const char *del = strstr(curr, "://");
345345
curr += (del - curr) + 3;
346346
} else {
347347
fprintf(stderr, "Invalid URI scheme\n");

0 commit comments

Comments
 (0)