Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .config/typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ seeked = "seeked"

[type.c.extend-words]
arange = "arange"
Threadsave = "Threadsave"
fo = "fo"
frst = "frst"
limite = "limite"
Expand Down
1 change: 1 addition & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -3280,6 +3280,7 @@ standardConfig static_configs[] = {
createBoolConfig("replica-ignore-maxmemory", "slave-ignore-maxmemory", MODIFIABLE_CONFIG, server.repl_replica_ignore_maxmemory, 1, NULL, NULL),
createBoolConfig("jemalloc-bg-thread", NULL, MODIFIABLE_CONFIG, server.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread),
createBoolConfig("activedefrag", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.active_defrag_enabled, CONFIG_ACTIVE_DEFRAG_DEFAULT, isValidActiveDefrag, NULL),
createBoolConfig("forkless-options-supported", NULL, IMMUTABLE_CONFIG, server.forkless_options_supported, 0, NULL, NULL),
createBoolConfig("syslog-enabled", NULL, IMMUTABLE_CONFIG, server.syslog_enabled, 0, NULL, NULL),
createBoolConfig("cluster-enabled", NULL, IMMUTABLE_CONFIG, server.cluster_enabled, 0, NULL, NULL),
createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG | DENY_LOADING_CONFIG, server.aof_enabled, 0, NULL, updateAppendOnly),
Expand Down
104 changes: 98 additions & 6 deletions src/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,82 @@
* so if expire is set later, we don't need to reallocate the object. */
#define KEY_SIZE_TO_INCLUDE_EXPIRE_THRESHOLD 128

/* Get beginning of embedded data, which may contain expire, metadata, key, and/or value.
* Embedded data flags must be accurate when called. */
static unsigned char *objectEmbeddedData(const robj *o) {
unsigned char *data = (void *)(o + 1);
if (o->hasembval) data -= sizeof(void *);
return data;
}

/* ===================== Object Metadata Management ========================= */

/* Static variable to store metadata size. Set once at server initialization. */
static size_t object_metadata_size = 0;

/* Set the metadata size.
* Size should not be changed once set. */
void objectSetMetadataSize(size_t size) {
/* Metadata size already set - only allow setting to the same value */
if (object_metadata_size == size) return;

/* When current size is 0 and the incoming size is not - setting for the first time */
serverAssert(object_metadata_size == 0);

/* Check that all databases are empty */
if (server.db != NULL) {
for (int j = 0; j < server.dbnum; j++) {
if (server.db[j] != NULL) {
serverAssert(kvstoreSize(server.db[j]->keys) == 0);
}
}
}

object_metadata_size = size;
}

/* Calculate the size of metadata for an object.
* Returns the configured metadata size if the object has an embedded key, 0 otherwise. */
size_t objectGetMetadataSize(const robj *o) {
if (o->hasembkey) return object_metadata_size;
return 0;
}

/* Get a void pointer to the metadata for an object.
* Returns NULL if the object doesn't have metadata.
* The caller must cast this to the appropriate metadata structure type.
*
* Memory layout visualization for objects:
*
* ┌─────────────────────────────────────────────────────────────────┐
* │ robj (struct serverObject) │
* │ - type, encoding, lru, hasexpire, hasembkey, hasembval... │
* ├─────────────────────────────────────────────────────────────────┤
* │ expire field (optional, if hasexpire == 1) │
* │ - long long (8 bytes) │
* ├─────────────────────────────────────────────────────────────────┤
* │ metadata (optional, if hasembkey == 1 && metadata_size > 0) │
* │ - (object_metadata_size) │ ← objectGetMetadata returns pointer here
* ├─────────────────────────────────────────────────────────────────┤
* │ embedded key (if hasembkey == 1) │
* ├─────────────────────────────────────────────────────────────────┤
* │ embedded value (if hasembval == 1) │
* └─────────────────────────────────────────────────────────────────┘
*/
void *objectGetMetadata(const robj *o) {
if (object_metadata_size == 0 || !o->hasembkey) return NULL;

/* The memory after the struct where we embedded metadata. */
unsigned char *data = objectEmbeddedData(o);

/* If expire field exists, metadata is after it */
if (o->hasexpire) {
data += sizeof(long long);
}

return (void *)data;
}

/* ===================== Creation and parsing of objects ==================== */

/* Creates an object, optionally with embedded key and expire fields. The key
Expand All @@ -62,10 +138,12 @@ static robj *createUnembeddedObjectWithKeyAndExpire(int type, void *val, const_s
size_t key_sds_len = has_embkey ? sdslen(key) : 0;
char key_sds_type = has_embkey ? sdsReqType(key_sds_len) : 0;
size_t key_sds_size = has_embkey ? sdsReqSize(key_sds_len, key_sds_type) : 0;
size_t metadata_size = has_embkey ? object_metadata_size : 0;
size_t min_size = sizeof(robj);
if (has_expire) {
min_size += sizeof(long long);
}
min_size += metadata_size;
if (has_embkey) {
/* Size of embedded key, incl. 1 byte for prefixed sds hdr size. */
min_size += 1 + key_sds_size;
Expand Down Expand Up @@ -98,6 +176,12 @@ static robj *createUnembeddedObjectWithKeyAndExpire(int type, void *val, const_s
data += sizeof(long long);
}

/* Initialize metadata to zero */
if (metadata_size > 0) {
memset(data, 0, metadata_size);
data += metadata_size;
}

/* Copy embedded key. */
if (o->hasembkey) {
*data++ = sdsHdrSize(key_sds_type);
Expand Down Expand Up @@ -140,12 +224,6 @@ robj *createRawStringObject(const char *ptr, size_t len) {
return createObject(OBJ_STRING, sdsnewlen(ptr, len));
}

/* Get beginning of embedded data, which may contain expire, key, and/or value. Embedded data flags must be accurate when called. */
static unsigned char *objectEmbeddedData(const robj *o) {
unsigned char *data = (void *)(o + 1);
if (o->hasembval) data -= sizeof(void *);
return data;
}

/* Creates a new embedded string object and copies the content of key, val_ptr
* and expire to the new object. LRU is set to 0. */
Expand All @@ -159,6 +237,7 @@ static robj *createEmbeddedStringObjectWithKeyAndExpire(const char *val_ptr,
char key_sds_type = has_embkey ? sdsReqType(key_sds_len) : 0;
size_t key_sds_size = has_embkey ? sdsReqSize(key_sds_len, key_sds_type) : 0;
size_t val_sds_size = sdsReqSize(val_len, SDS_TYPE_8);
size_t metadata_size = has_embkey ? object_metadata_size : 0;
if (val_sds_size < sizeof(void *)) {
val_sds_size = sizeof(void *); /* Ensure it's possible to "unembed" value later */
}
Expand All @@ -168,6 +247,7 @@ static robj *createEmbeddedStringObjectWithKeyAndExpire(const char *val_ptr,
if (expire != EXPIRY_NONE) {
min_size += sizeof(long long);
}
min_size += metadata_size;
if (has_embkey) {
/* Size of embedded key, incl. 1 byte for prefixed sds hdr size. */
min_size += 1 + key_sds_size;
Expand Down Expand Up @@ -201,6 +281,12 @@ static robj *createEmbeddedStringObjectWithKeyAndExpire(const char *val_ptr,
data += sizeof(long long);
}

/* Initialize metadata to zero */
if (metadata_size > 0) {
memset(data, 0, metadata_size);
data += metadata_size;
}

/* Copy embedded key. */
if (o->hasembkey) {
*data++ = sdsHdrSize(key_sds_type);
Expand Down Expand Up @@ -234,6 +320,7 @@ static bool shouldEmbedStringObject(size_t val_len, const_sds key, long long exp
if (key) {
size_t key_len = sdslen(key);
size += sdsReqSize(key_len, sdsReqType(key_len)) + 1; /* 1 byte for prefixed sds hdr size */
size += object_metadata_size;
}
size += (expire != EXPIRY_NONE) * sizeof(long long);
size += sdsReqSize(val_len, SDS_TYPE_8);
Expand Down Expand Up @@ -269,6 +356,8 @@ void *objectGetVal(const robj *o) {
data += sizeof(long long);
}
if (o->hasembkey) {
/* Skip metadata */
data += objectGetMetadataSize(o);
/* Skip embedded key */
uint8_t hdr_size = *(uint8_t *)data;
data += 1 + hdr_size; /* +1 for header size byte */
Expand All @@ -287,6 +376,9 @@ sds objectGetKey(const robj *o) {
data += sizeof(long long);
}
if (o->hasembkey) {
/* Skip metadata */
data += objectGetMetadataSize(o);
/* Skip header size byte */
uint8_t hdr_size = *(uint8_t *)data;
data += 1 + hdr_size;
return (sds)data;
Expand Down
8 changes: 8 additions & 0 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,7 @@ void initServerConfig(void) {
for (j = 0; j < CONFIG_DEFAULT_BINDADDR_COUNT; j++) server.bindaddr[j] = zstrdup(default_bindaddr[j]);
memset(server.listeners, 0x00, sizeof(server.listeners));
server.active_expire_enabled = 1;
server.forkless_options_supported = 0;
server.lazy_expire_disabled = 0;
server.skip_checksum_validation = 0;
server.loading = 0;
Expand Down Expand Up @@ -2995,6 +2996,13 @@ void initServer(void) {

server.dbnum = server.cluster_enabled ? server.config_databases_cluster : server.config_databases;
server.db = zcalloc(sizeof(serverDb *) * server.dbnum);

/* Set object metadata size before creating any database key objects */
if (server.forkless_options_supported) {
objectSetMetadataSize(sizeof(uint32_t)); /* This is a placeholder until Threadsave defines a metadata structure */
/* 4 bytes for iterator_epoch for now*/
}

createDatabaseIfNeeded(0); /* The default database should always exist */

evictionPoolAlloc(); /* Initialize the LRU keys pool. */
Expand Down
36 changes: 22 additions & 14 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -784,25 +784,27 @@ typedef struct ValkeyModuleType moduleType;
* The optional variable-sized embedded data has 2 possible layouts. If value is embedded (hasembval == 1)
* the `val_ptr` pointer is not used - instead the val data is embedded:
*
* +------+----------+-----+------------+----------+--------+-----------------+---------+------------+
* | type | encoding | lru | has* flags | refcount | expire | key_header_size | key sds | value data |
* +------+----------+-----+------------+----------+--------+-----------------+---------+------------+
* ^ ^ ^ ^
* | | | |
* | | | +--- present because hasembval == 1
* | | |
* | +-----------------+--- present if hasembkey == 1
* +------+----------+-----+------------+----------+--------+----------+-----------------+---------+------------+
* | type | encoding | lru | has* flags | refcount | expire | metadata | key_header_size | key sds | value data |
* +------+----------+-----+------------+----------+--------+----------+-----------------+---------+------------+
* ^ ^ ^ ^ ^
* | | | | |
* | | | | +--- present because hasembval == 1
* | | | |
* | +----------+-----------------+--- present if hasembkey == 1
* |
* |
* +--- present if hasexpire == 1
*
* Otherwise value is not embedded and we use the `val_ptr` pointer:
*
* +------+----------+-----+------------+----------+---------+--------+-----------------+---------+
* | type | encoding | lru | has* flags | refcount | val_ptr | expire | key_header_size | key sds |
* +------+----------+-----+------------+----------+---------+--------+-----------------+---------+
* ^ ^ ^ ^
* | | | |
* | | +-----------------+--- present if hasembkey == 1
* +------+----------+-----+------------+----------+---------+--------+----------+-----------------+---------+
* | type | encoding | lru | has* flags | refcount | val_ptr | expire | metadata | key_header_size | key sds |
* +------+----------+-----+------------+----------+---------+--------+----------+-----------------+---------+
* ^ ^ ^ ^ ^
* | | | | |
* | | +----------+-----------------+--- present if hasembkey == 1
* | |
* | |
* | +--- present if hasexpire == 1
* |
Expand Down Expand Up @@ -2040,6 +2042,7 @@ struct valkeyServer {
int rdb_checksum; /* Use RDB checksum? */
int rdb_del_sync_files; /* Remove RDB files used only for SYNC if
the instance does not use persistence. */
int forkless_options_supported; /* Enable forkless options support. */
time_t lastsave; /* Unix time of last successful save */
time_t lastbgsave_try; /* Unix time of last attempted bgsave */
time_t rdb_save_time_last; /* Time used by last RDB save run. */
Expand Down Expand Up @@ -3157,6 +3160,11 @@ uint8_t objectGetLFUFrequency(robj *o);
uint32_t objectGetLRUIdleSecs(robj *o);
uint32_t objectGetIdleness(robj *o);

/* Object metadata management */
void objectSetMetadataSize(size_t size);
size_t objectGetMetadataSize(const robj *o);
void *objectGetMetadata(const robj *o);

/* Synchronous I/O with timeout */
ssize_t syncWrite(int fd, char *ptr, ssize_t size, long long timeout);
ssize_t syncRead(int fd, char *ptr, ssize_t size, long long timeout);
Expand Down
Loading
Loading