diff options
Diffstat (limited to 'examples/redis-unstable/src/keymeta.h')
| -rw-r--r-- | examples/redis-unstable/src/keymeta.h | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/examples/redis-unstable/src/keymeta.h b/examples/redis-unstable/src/keymeta.h new file mode 100644 index 0000000..43b4242 --- /dev/null +++ b/examples/redis-unstable/src/keymeta.h @@ -0,0 +1,182 @@ +/* + * Key Metadata (keymeta) + * + * High-level idea + * ---------------- + * keymeta is a framework for attaching & maintaining metadata to keys. + * + * - Up to 8 different metadata classes can be registered globally. First one + * is reserved for EXPIRE. + * - Each class has a unique ID and name, like modules datat-types names, yet + * it has its own namespace. + * - Each key metadata class provides a set of callbacks for key lifecycle operations, + * ensuring consistent handling across copy, rename, logical removal (unlink), + * actual deallocation (free), persistence (RDB/AOF), and defragmentation. + * - Each key can carry up to 8 independent metadata values. Each value is related + * to a specific metadata class. + * - The 8-byte slot can either hold inline data or a pointer/handle to a larger, + * externally managed structure. + * + * Relation to other components + * ---------------------------- + * - kvobj: 8 bits metabits field in kvobj is used to indicate active metadata. + * bit number corresponds to class ID. + * - Expiration: class ID 0 is reserved for TTL/expire; + * - Registration: redisServer.keyMetaClass[] stores registered classes. Modules + * register via keyMetaClassCreate (see redismodule.h) and may provide callbacks + * for persistence, copy/rename behavior, and lifecycle hooks (unlink/free). + * - modules: modules can register metadata classes and provide callbacks. + */ + +#ifndef __KEYMETA_H +#define __KEYMETA_H + +#include <stdint.h> +#include <stddef.h> +#include "sds.h" +#include "object.h" + +/* fwd decls */ +struct redisDb; +struct redisObject; +struct RedisModuleIO; +struct RedisModuleKeyOptCtx; +struct RedisModuleDefragCtx; +struct RedisModule; +typedef int KeyMetaClassId; /* Index into redisServer.keyMetaClass[] */ + +/* kvmeta - Metadata to be attached to kvobj */ +#define KEY_META_ID_EXPIRE 0 /* Must be first */ +/* IDs 1..7 are available for modules */ +#define KEY_META_ID_MODULE_FIRST 1 +#define KEY_META_ID_MODULE_LAST 7 +#define KEY_META_ID_MAX 8 + +#define KEY_META_MAX_NUM_MODULES (KEY_META_ID_MODULE_LAST - KEY_META_ID_MODULE_FIRST + 1) + +#define KEY_META_MASK_NONE 0 +#define KEY_META_MASK_MODULES (((1U << KEY_META_MAX_NUM_MODULES) - 1) << KEY_META_ID_MODULE_FIRST) +#define KEY_META_MASK_EXPIRE (1U << KEY_META_ID_EXPIRE) + +/* RDB load callback: Return 1 to attach, 0 to skip, -1 on error */ +typedef int (*KeyMetaLoadFunc)(RedisModuleIO *rdb, uint64_t *meta, int encver); +typedef void (*KeyMetaSaveFunc)(RedisModuleIO *rdb, void *value, uint64_t *meta); +typedef void (*KeyMetaAOFRewriteFunc)(RedisModuleIO *aof, void *value, uint64_t meta); +typedef void (*KeyMetaFreeFunc)(const char *keyname, uint64_t meta); +typedef int (*KeyMetaCopyFunc)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); +typedef int (*KeyMetaRenameFunc)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); +typedef int (*KeyMetaDefragFunc)(RedisModuleDefragCtx *ctx, RedisModuleString *keyname, uint64_t meta); +typedef size_t (*KeyMetaMemUsageFunc)(struct RedisModuleKeyOptCtx *ctx, size_t sample_size, uint64_t meta); +typedef size_t (*KeyMetaFreeEffortFunc)(struct RedisModuleKeyOptCtx *ctx, uint64_t meta); +typedef void (*KeyMetaUnlinkFunc)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); +typedef int (*KeyMetaMoveFunc)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); + +/* For explanation, see struct RedisModuleKeyMetaClassConfig */ +typedef struct KeyMetaClassConf { +#define KEY_META_FLAGS_RDB_MASK 0x7 /* First 3 flags are serialized into RDB with key */ +#define KEY_META_FLAG_ALLOW_IGNORE 0 /* Aligned with: REDISMODULE_META_ALLOW_IGNORE */ +#define KEY_META_FLAG_RBB_RESERVED_1 1 /* Reserved for future use */ +#define KEY_META_FLAG_RBB_RESERVED_2 2 /* Reserved for future use */ + uint64_t flags; + + /* Sentinel value meaning "no resource attached". It guarantees callbacks are + * ONLY invoked when meta != reset_value. This prevents double-free, avoids + * persisting sentinels to RDB/AOF, and simplifies module logic. */ + uint64_t reset_value; + + int (*copy)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); + int (*rename)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); + int (*move)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); + void (*unlink)(struct RedisModuleKeyOptCtx *ctx, uint64_t *meta); + void (*free)(const char *keyname, uint64_t meta); + int (*rdb_load)(struct RedisModuleIO *rdb, uint64_t *meta, int metaver); + void (*rdb_save)(struct RedisModuleIO *rdb, void *value, uint64_t *meta); + void (*aof_rewrite)(struct RedisModuleIO *aof, void *value, uint64_t meta); + + /****************************** TBD: ******************************/ + int (*defrag) (struct RedisModuleDefragCtx *ctx, struct redisObject *key, uint64_t meta); + size_t (*mem_usage)(struct RedisModuleKeyOptCtx *ctx, size_t sample_size, uint64_t meta); + size_t (*free_effort)(struct RedisModuleKeyOptCtx *ctx, uint64_t meta); +} KeyMetaClassConf; + +/* KeyMetaSpec - Used by dbAddInternal() to describe metadata of a new key */ +typedef struct KeyMetaSpec { + uint16_t numMeta; /* Num active metadata entries. Aligned with metabits */ + uint16_t metabits; + + /* Array of metadata values. Entries are populated in reverse order + * (from the end of the array backward) to make bulk copying with + * memcpy more efficient. During insertion, the next slot is: + * meta[KEY_META_ID_MAX - (++numMeta)] + * + * For example if numMeta=2, and metabits=0b101, then the last entry holds + * value for class 0, and the previous entry holds value for class 2. + */ + uint64_t meta[KEY_META_ID_MAX]; +} KeyMetaSpec; + +/* init Keys metadata on server startup */ +void keyMetaInit(void); + +/* Key metadata event callbacks */ +void keyMetaOnUnlink(struct redisDb *db, robj *key,kvobj *kv); +void keyMetaOnFree(kvobj *kv); +void keyMetaOnRename(struct redisDb *db, kvobj *kv, robj *oldKey, robj *newKey, KeyMetaSpec *kms); +void keyMetaOnMove(kvobj *kv, robj *key, int srcDbId, int dstDbId, KeyMetaSpec *kms); +void keyMetaOnCopy(kvobj *kv, robj *srcKey, robj *dstKey, int srcDbId, int dstDbId, KeyMetaSpec *kms); +int keyMetaOnAof(rio *r, robj *key, kvobj *kv, int dbid); + +/* RDB serialization */ +int rdbSaveKeyMetadata(rio *rdb, robj *key, kvobj *kv, int dbid); +int rdbLoadKeyMetadata(rio *rdb, int dbid, int numClasses, KeyMetaSpec *kms); + +void keyMetaResetModuleValues(kvobj *kv); +void keyMetaTransition(kvobj *kvOld, kvobj *kvNew); + +/* return 0 if failed to create. Otherwise return handle (between 1 and 7) */ +KeyMetaClassId keyMetaClassCreate(struct RedisModule *ctx, const char *metaname, int metaver, KeyMetaClassConf *conf); +/* Destroy (release) a previously created class. Return 1 on success, 0 on failure. */ +int keyMetaClassRelease(KeyMetaClassId class_id); + +kvobj *keyMetaSetMetadata(struct redisDb *db, kvobj *kv, KeyMetaClassId kmcId, uint64_t metadata); +int keyMetaGetMetadata(KeyMetaClassId kmcId, kvobj *kv, uint64_t *metadata); +int keyMetaRemoveMetadata(KeyMetaClassId kmcId, RedisModuleKey *key); + +/* bit operations on metabits */ +static inline uint32_t getNumMeta(uint16_t metabits); +static inline uint32_t getModuleMetaBits(uint16_t metabits); + +/********** Inline functions **********/ + +static inline void keyMetaResetValues(kvobj *kv) { + if (unlikely(kv->metabits & KEY_META_MASK_MODULES)) + keyMetaResetModuleValues(kv); + /* Must be first meta (optimized) */ + if (kv->metabits & KEY_META_MASK_EXPIRE) + ((uint64_t *)kv)[-1] = -1; +} + +static inline void keyMetaSpecInit(KeyMetaSpec *keymeta) { + /* Enough to init metabits and numMeta. meta[] is not used. */ + keymeta->metabits = 0; + keymeta->numMeta = 0; +} + +/* Add metadata to keymeta spec. metaid must be in range 0..7 and added in order! */ +void keyMetaSpecAdd(KeyMetaSpec *keymeta, int metaid, uint64_t metaval); + +/* Free any metadata stored in a KeyMetaSpec. This is called when RDB load fails after + * some metadata has been loaded. It invokes the free cb for each metadata class that + * was already loaded, preventing memory leaks from partially-loaded metadata. */ +void keyMetaSpecCleanup(KeyMetaSpec *kms); + +static inline uint32_t getNumMeta(uint16_t metabits) { + /* Assumed expire is always first meta */ + return __builtin_popcount(metabits); +} + +static inline uint32_t getModuleMetaBits(uint16_t metabits) { + return metabits & KEY_META_MASK_MODULES; +} + +#endif // __KEYMETA_H |
