summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db.c77
-rw-r--r--src/dict.c50
-rw-r--r--src/dict.h2
-rw-r--r--src/evict.c5
-rw-r--r--src/expire.c3
-rw-r--r--src/module.c59
-rw-r--r--src/redismodule.h63
-rw-r--r--src/server.c1
-rw-r--r--src/server.h13
-rw-r--r--src/t_set.c2
-rw-r--r--src/t_string.c4
11 files changed, 221 insertions, 58 deletions
diff --git a/src/db.c b/src/db.c
index 5ebd21e4b..265650126 100644
--- a/src/db.c
+++ b/src/db.c
@@ -218,8 +218,13 @@ int dbAddRDBLoad(redisDb *db, sds key, robj *val) {
* count of the new value is up to the caller.
* This function does not modify the expire time of the existing key.
*
+ * The 'overwrite' flag is an indication whether this is done as part of a
+ * complete replacement of their key, which can be thought as a deletion and
+ * replacement (in which case we need to emit deletion signals), or just an
+ * update of a value of an existing key (when false).
+ *
* The program is aborted if the key was not already present. */
-void dbOverwrite(redisDb *db, robj *key, robj *val) {
+static void dbSetValue(redisDb *db, robj *key, robj *val, int overwrite) {
dictEntry *de = dictFind(db->dict,key->ptr);
serverAssertWithInfo(NULL,key,de != NULL);
@@ -228,12 +233,22 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
val->lru = old->lru;
}
- /* Although the key is not really deleted from the database, we regard
- * overwrite as two steps of unlink+add, so we still need to call the unlink
- * callback of the module. */
- moduleNotifyKeyUnlink(key,old,db->id);
- /* We want to try to unblock any module clients or clients using a blocking XREADGROUP */
- signalDeletedKeyAsReady(db,key,old->type);
+ if (overwrite) {
+ /* RM_StringDMA may call dbUnshareStringValue which may free val, so we
+ * need to incr to retain old */
+ incrRefCount(old);
+ /* Although the key is not really deleted from the database, we regard
+ * overwrite as two steps of unlink+add, so we still need to call the unlink
+ * callback of the module. */
+ moduleNotifyKeyUnlink(key,old,db->id,DB_FLAG_KEY_OVERWRITE);
+ /* We want to try to unblock any module clients or clients using a blocking XREADGROUP */
+ signalDeletedKeyAsReady(db,key,old->type);
+ decrRefCount(old);
+ /* Because of RM_StringDMA, old may be changed, so we need get old again */
+ old = dictGetVal(de);
+ /* Entry in auxentry may be changed, so we need update auxentry */
+ auxentry = *de;
+ }
dictSetVal(db->dict, de, val);
if (server.lazyfree_lazy_server_del) {
@@ -244,6 +259,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
dictFreeVal(db->dict, &auxentry);
}
+/* Replace an existing key with a new value, we just replace value and don't
+ * emit any events */
+void dbReplaceValue(redisDb *db, robj *key, robj *val) {
+ dbSetValue(db, key, val, 0);
+}
+
/* High level Set operation. This function can be used in order to set
* a key, whatever it was existing or not, to a new object.
*
@@ -268,7 +289,7 @@ void setKey(client *c, redisDb *db, robj *key, robj *val, int flags) {
if (!keyfound) {
dbAdd(db,key,val);
} else {
- dbOverwrite(db,key,val);
+ dbSetValue(db,key,val,1);
}
incrRefCount(val);
if (!(flags & SETKEY_KEEPTTL)) removeExpire(db,key);
@@ -315,23 +336,33 @@ robj *dbRandomKey(redisDb *db) {
}
/* Helper for sync and async delete. */
-static int dbGenericDelete(redisDb *db, robj *key, int async) {
- /* Deleting an entry from the expires dict will not free the sds of
- * the key, because it is shared with the main dictionary. */
- if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
- dictEntry *de = dictUnlink(db->dict,key->ptr);
+int dbGenericDelete(redisDb *db, robj *key, int async, int flags) {
+ dictEntry **plink;
+ int table;
+ dictEntry *de = dictTwoPhaseUnlinkFind(db->dict,key->ptr,&plink,&table);
if (de) {
robj *val = dictGetVal(de);
+ /* RM_StringDMA may call dbUnshareStringValue which may free val, so we
+ * need to incr to retain val */
+ incrRefCount(val);
/* Tells the module that the key has been unlinked from the database. */
- moduleNotifyKeyUnlink(key,val,db->id);
+ moduleNotifyKeyUnlink(key,val,db->id,flags);
/* We want to try to unblock any module clients or clients using a blocking XREADGROUP */
signalDeletedKeyAsReady(db,key,val->type);
+ /* We should call decr before freeObjAsync. If not, the refcount may be
+ * greater than 1, so freeObjAsync doesn't work */
+ decrRefCount(val);
if (async) {
- freeObjAsync(key, val, db->id);
+ /* Because of dbUnshareStringValue, the val in de may change. */
+ freeObjAsync(key, dictGetVal(de), db->id);
dictSetVal(db->dict, de, NULL);
}
if (server.cluster_enabled) slotToKeyDelEntry(de, db);
- dictFreeUnlinkedEntry(db->dict,de);
+
+ /* Deleting an entry from the expires dict will not free the sds of
+ * the key, because it is shared with the main dictionary. */
+ if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
+ dictTwoPhaseUnlinkFree(db->dict,de,plink,table);
return 1;
} else {
return 0;
@@ -340,19 +371,19 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) {
/* Delete a key, value, and associated expiration entry if any, from the DB */
int dbSyncDelete(redisDb *db, robj *key) {
- return dbGenericDelete(db, key, 0);
+ return dbGenericDelete(db, key, 0, DB_FLAG_KEY_DELETED);
}
/* Delete a key, value, and associated expiration entry if any, from the DB. If
* the value consists of many allocations, it may be freed asynchronously. */
int dbAsyncDelete(redisDb *db, robj *key) {
- return dbGenericDelete(db, key, 1);
+ return dbGenericDelete(db, key, 1, DB_FLAG_KEY_DELETED);
}
/* This is a wrapper whose behavior depends on the Redis lazy free
* configuration. Deletes the key synchronously or asynchronously. */
int dbDelete(redisDb *db, robj *key) {
- return dbGenericDelete(db, key, server.lazyfree_lazy_server_del);
+ return dbGenericDelete(db, key, server.lazyfree_lazy_server_del, DB_FLAG_KEY_DELETED);
}
/* Prepare the string object stored at 'key' to be modified destructively
@@ -388,7 +419,7 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
robj *decoded = getDecodedObject(o);
o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded);
- dbOverwrite(db,key,o);
+ dbReplaceValue(db,key,o);
}
return o;
}
@@ -1561,10 +1592,7 @@ long long getExpire(redisDb *db, robj *key) {
void deleteExpiredKeyAndPropagate(redisDb *db, robj *keyobj) {
mstime_t expire_latency;
latencyStartMonitor(expire_latency);
- if (server.lazyfree_lazy_expire)
- dbAsyncDelete(db,keyobj);
- else
- dbSyncDelete(db,keyobj);
+ dbGenericDelete(db,keyobj,server.lazyfree_lazy_expire,DB_FLAG_KEY_EXPIRED);
latencyEndMonitor(expire_latency);
latencyAddSampleIfNeeded("expire-del",expire_latency);
notifyKeyspaceEvent(NOTIFY_EXPIRED,"expired",keyobj,db->id);
@@ -1657,6 +1685,7 @@ int keyIsExpired(redisDb *db, robj *key) {
* The return value of the function is 0 if the key is still valid,
* otherwise the function returns 1 if the key is expired. */
int expireIfNeeded(redisDb *db, robj *key, int flags) {
+ if (server.lazy_expire_disabled) return 0;
if (!keyIsExpired(db,key)) return 0;
/* If we are running in the context of a replica, instead of
diff --git a/src/dict.c b/src/dict.c
index 9ed461d62..7fecb2daa 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -541,6 +541,56 @@ void *dictFetchValue(dict *d, const void *key) {
return he ? dictGetVal(he) : NULL;
}
+/* Find an element from the table, also get the plink of the entry. The entry
+ * is returned if the element is found, and the user should later call
+ * `dictTwoPhaseUnlinkFree` with it in order to unlink and release it. Otherwise if
+ * the key is not found, NULL is returned. These two functions should be used in pair.
+ * `dictTwoPhaseUnlinkFind` pauses rehash and `dictTwoPhaseUnlinkFree` resumes rehash.
+ *
+ * We can use like this:
+ *
+ * dictEntry *de = dictTwoPhaseUnlinkFind(db->dict,key->ptr,&plink, &table);
+ * // Do something, but we can't modify the dict
+ * dictTwoPhaseUnlinkFree(db->dict,de,plink,table); // We don't need to lookup again
+ *
+ * If we want to find an entry before delete this entry, this an optimization to avoid
+ * dictFind followed by dictDelete. i.e. the first API is a find, and it gives some info
+ * to the second one to avoid repeating the lookup
+ */
+dictEntry *dictTwoPhaseUnlinkFind(dict *d, const void *key, dictEntry ***plink, int *table_index) {
+ uint64_t h, idx, table;
+
+ if (dictSize(d) == 0) return NULL; /* dict is empty */
+ if (dictIsRehashing(d)) _dictRehashStep(d);
+ h = dictHashKey(d, key);
+
+ for (table = 0; table <= 1; table++) {
+ idx = h & DICTHT_SIZE_MASK(d->ht_size_exp[table]);
+ dictEntry **ref = &d->ht_table[table][idx];
+ while(*ref) {
+ if (key==(*ref)->key || dictCompareKeys(d, key, (*ref)->key)) {
+ *table_index = table;
+ *plink = ref;
+ dictPauseRehashing(d);
+ return *ref;
+ }
+ ref = &(*ref)->next;
+ }
+ if (!dictIsRehashing(d)) return NULL;
+ }
+ return NULL;
+}
+
+void dictTwoPhaseUnlinkFree(dict *d, dictEntry *he, dictEntry **plink, int table_index) {
+ if (he == NULL) return;
+ d->ht_used[table_index]--;
+ *plink = he->next;
+ dictFreeKey(d, he);
+ dictFreeVal(d, he);
+ zfree(he);
+ dictResumeRehashing(d);
+}
+
/* A fingerprint is a 64 bit number that represents the state of the dictionary
* at a given time, it's just a few dict properties xored together.
* When an unsafe iterator is initialized, we get the dict fingerprint, and check
diff --git a/src/dict.h b/src/dict.h
index d8a655ca7..c818d6d4d 100644
--- a/src/dict.h
+++ b/src/dict.h
@@ -181,6 +181,8 @@ int dictReplace(dict *d, void *key, void *val);
int dictDelete(dict *d, const void *key);
dictEntry *dictUnlink(dict *d, const void *key);
void dictFreeUnlinkedEntry(dict *d, dictEntry *he);
+dictEntry *dictTwoPhaseUnlinkFind(dict *d, const void *key, dictEntry ***plink, int *table_index);
+void dictTwoPhaseUnlinkFree(dict *d, dictEntry *he, dictEntry **plink, int table_index);
void dictRelease(dict *d);
dictEntry * dictFind(dict *d, const void *key);
void *dictFetchValue(dict *d, const void *key);
diff --git a/src/evict.c b/src/evict.c
index f97285f2a..85a8d7ab0 100644
--- a/src/evict.c
+++ b/src/evict.c
@@ -678,10 +678,7 @@ int performEvictions(void) {
* we only care about memory used by the key space. */
delta = (long long) zmalloc_used_memory();
latencyStartMonitor(eviction_latency);
- if (server.lazyfree_lazy_eviction)
- dbAsyncDelete(db,keyobj);
- else
- dbSyncDelete(db,keyobj);
+ dbGenericDelete(db,keyobj,server.lazyfree_lazy_eviction,DB_FLAG_KEY_EVICTED);
latencyEndMonitor(eviction_latency);
latencyAddSampleIfNeeded("eviction-del",eviction_latency);
delta -= (long long) zmalloc_used_memory();
diff --git a/src/expire.c b/src/expire.c
index a106b0839..e05c7308a 100644
--- a/src/expire.c
+++ b/src/expire.c
@@ -637,8 +637,7 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
if (checkAlreadyExpired(when)) {
robj *aux;
- int deleted = server.lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
- dbSyncDelete(c->db,key);
+ int deleted = dbGenericDelete(c->db,key,server.lazyfree_lazy_expire,DB_FLAG_KEY_EXPIRED);
serverAssertWithInfo(c,key,deleted);
server.dirty++;
diff --git a/src/module.c b/src/module.c
index e336ebd19..2656e9749 100644
--- a/src/module.c
+++ b/src/module.c
@@ -216,7 +216,6 @@ struct RedisModuleKey {
} stream;
} u;
};
-typedef struct RedisModuleKey RedisModuleKey;
/* RedisModuleKey 'ztype' values. */
#define REDISMODULE_ZSET_RANGE_NONE 0 /* This must always be 0. */
@@ -8130,7 +8129,9 @@ void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid)
* If the subscriber performs an action triggering itself,
* it will not be notified about it. */
sub->active = 1;
+ server.lazy_expire_disabled++;
sub->notify_callback(&ctx, type, event, key);
+ server.lazy_expire_disabled--;
sub->active = 0;
moduleFreeContext(&ctx);
}
@@ -10603,6 +10604,7 @@ static uint64_t moduleEventVersions[] = {
-1, /* REDISMODULE_EVENT_REPL_ASYNC_LOAD */
-1, /* REDISMODULE_EVENT_EVENTLOOP */
-1, /* REDISMODULE_EVENT_CONFIG */
+ REDISMODULE_KEYINFO_VERSION, /* REDISMODULE_EVENT_KEY */
};
/* Register to be notified, via a callback, when the specified server event
@@ -10877,6 +10879,22 @@ static uint64_t moduleEventVersions[] = {
* // name of each modified configuration item
* uint32_t num_changes; // The number of elements in the config_names array
*
+ * * RedisModule_Event_Key
+ *
+ * Called when a key is removed from the keyspace. We can't modify any key in
+ * the event.
+ * The following sub events are available:
+ *
+ * * `REDISMODULE_SUBEVENT_KEY_DELETED`
+ * * `REDISMODULE_SUBEVENT_KEY_EXPIRED`
+ * * `REDISMODULE_SUBEVENT_KEY_EVICTED`
+ * * `REDISMODULE_SUBEVENT_KEY_OVERWRITTEN`
+ *
+ * The data pointer can be casted to a RedisModuleKeyInfo
+ * structure with the following fields:
+ *
+ * RedisModuleKey *key; // Key name
+ *
* The function returns REDISMODULE_OK if the module was successfully subscribed
* for the specified event. If the API is called from a wrong context or unsupported event
* is given then REDISMODULE_ERR is returned. */
@@ -10956,12 +10974,21 @@ int RM_IsSubEventSupported(RedisModuleEvent event, int64_t subevent) {
return subevent < _REDISMODULE_SUBEVENT_EVENTLOOP_NEXT;
case REDISMODULE_EVENT_CONFIG:
return subevent < _REDISMODULE_SUBEVENT_CONFIG_NEXT;
+ case REDISMODULE_EVENT_KEY:
+ return subevent < _REDISMODULE_SUBEVENT_KEY_NEXT;
default:
break;
}
return 0;
}
+typedef struct KeyInfo {
+ int32_t dbnum;
+ RedisModuleString *key;
+ robj *value;
+ int mode;
+} KeyInfo;
+
/* This is called by the Redis internals every time we want to fire an
* event that can be intercepted by some module. The pointer 'data' is useful
* in order to populate the event-specific structure when needed, in order
@@ -10998,6 +11025,8 @@ void moduleFireServerEvent(uint64_t eid, int subid, void *data) {
RedisModuleClientInfoV1 civ1;
RedisModuleReplicationInfoV1 riv1;
RedisModuleModuleChangeV1 mcv1;
+ RedisModuleKey key;
+ RedisModuleKeyInfoV1 ki = {REDISMODULE_KEYINFO_VERSION, &key};
/* Event specific context and data pointer setup. */
if (eid == REDISMODULE_EVENT_CLIENT_CHANGE) {
@@ -11029,12 +11058,21 @@ void moduleFireServerEvent(uint64_t eid, int subid, void *data) {
moduledata = data;
} else if (eid == REDISMODULE_EVENT_CONFIG) {
moduledata = data;
+ } else if (eid == REDISMODULE_EVENT_KEY) {
+ KeyInfo *info = data;
+ selectDb(ctx.client, info->dbnum);
+ moduleInitKey(&key, &ctx, info->key, info->value, info->mode);
+ moduledata = &ki;
}
el->module->in_hook++;
el->callback(&ctx,el->event,subid,moduledata);
el->module->in_hook--;
+ if (eid == REDISMODULE_EVENT_KEY) {
+ moduleCloseKey(&key);
+ }
+
moduleFreeContext(&ctx);
}
}
@@ -11078,9 +11116,21 @@ void processModuleLoadingProgressEvent(int is_aof) {
}
}
-/* When a module key is deleted (in dbAsyncDelete/dbSyncDelete/dbOverwrite), it
+/* When a key is deleted (in dbAsyncDelete/dbSyncDelete/setKey), it
* will be called to tell the module which key is about to be released. */
-void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid) {
+void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid, int flags) {
+ server.lazy_expire_disabled++;
+ int subevent = REDISMODULE_SUBEVENT_KEY_DELETED;
+ if (flags & DB_FLAG_KEY_EXPIRED) {
+ subevent = REDISMODULE_SUBEVENT_KEY_EXPIRED;
+ } else if (flags & DB_FLAG_KEY_EVICTED) {
+ subevent = REDISMODULE_SUBEVENT_KEY_EVICTED;
+ } else if (flags & DB_FLAG_KEY_OVERWRITE) {
+ subevent = REDISMODULE_SUBEVENT_KEY_OVERWRITTEN;
+ }
+ KeyInfo info = {dbid, key, val, REDISMODULE_WRITE};
+ moduleFireServerEvent(REDISMODULE_EVENT_KEY, subevent, &info);
+
if (val->type == OBJ_MODULE) {
moduleValue *mv = val->ptr;
moduleType *mt = mv->type;
@@ -11090,8 +11140,9 @@ void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid) {
mt->unlink2(&ctx,mv->value);
} else if (mt->unlink != NULL) {
mt->unlink(key,mv->value);
- }
+ }
}
+ server.lazy_expire_disabled--;
}
/* Return the free_effort of the module, it will automatically choose to call
diff --git a/src/redismodule.h b/src/redismodule.h
index 7f04f7ba4..d65687a23 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -6,6 +6,28 @@
#include <stdio.h>
#include <stdlib.h>
+
+typedef struct RedisModuleString RedisModuleString;
+typedef struct RedisModuleKey RedisModuleKey;
+
+/* -------------- Defines NOT common between core and modules ------------- */
+
+#if defined REDISMODULE_CORE
+/* Things only defined for the modules core (server), not exported to modules
+ * that include this file. */
+
+#define RedisModuleString robj
+
+#endif /* defined REDISMODULE_CORE */
+
+#if !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE
+/* Things defined for modules, but not for core-modules. */
+
+typedef long long mstime_t;
+typedef long long ustime_t;
+
+#endif /* !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE */
+
/* ---------------- Defines common between core and modules --------------- */
/* Error status return values. */
@@ -458,7 +480,8 @@ typedef void (*RedisModuleEventLoopOneShotFunc)(void *user_data);
#define REDISMODULE_EVENT_REPL_ASYNC_LOAD 14
#define REDISMODULE_EVENT_EVENTLOOP 15
#define REDISMODULE_EVENT_CONFIG 16
-#define _REDISMODULE_EVENT_NEXT 17 /* Next event flag, should be updated if a new event added. */
+#define REDISMODULE_EVENT_KEY 17
+#define _REDISMODULE_EVENT_NEXT 18 /* Next event flag, should be updated if a new event added. */
typedef struct RedisModuleEvent {
uint64_t id; /* REDISMODULE_EVENT_... defines. */
@@ -565,6 +588,10 @@ static const RedisModuleEvent
RedisModuleEvent_Config = {
REDISMODULE_EVENT_CONFIG,
1
+ },
+ RedisModuleEvent_Key = {
+ REDISMODULE_EVENT_KEY,
+ 1
};
/* Those are values that are used for the 'subevent' callback argument. */
@@ -633,6 +660,12 @@ static const RedisModuleEvent
#define REDISMODULE_SUBEVENT_EVENTLOOP_AFTER_SLEEP 1
#define _REDISMODULE_SUBEVENT_EVENTLOOP_NEXT 2
+#define REDISMODULE_SUBEVENT_KEY_DELETED 0
+#define REDISMODULE_SUBEVENT_KEY_EXPIRED 1
+#define REDISMODULE_SUBEVENT_KEY_EVICTED 2
+#define REDISMODULE_SUBEVENT_KEY_OVERWRITTEN 3
+#define _REDISMODULE_SUBEVENT_KEY_NEXT 4
+
#define _REDISMODULE_SUBEVENT_SHUTDOWN_NEXT 0
#define _REDISMODULE_SUBEVENT_CRON_LOOP_NEXT 0
#define _REDISMODULE_SUBEVENT_SWAPDB_NEXT 0
@@ -756,6 +789,16 @@ typedef struct RedisModuleSwapDbInfo {
#define RedisModuleSwapDbInfo RedisModuleSwapDbInfoV1
+#define REDISMODULE_KEYINFO_VERSION 1
+typedef struct RedisModuleKeyInfo {
+ uint64_t version; /* Not used since this structure is never passed
+ from the module to the core right now. Here
+ for future compatibility. */
+ RedisModuleKey *key; /* Opened key. */
+} RedisModuleKeyInfoV1;
+
+#define RedisModuleKeyInfo RedisModuleKeyInfoV1
+
typedef enum {
REDISMODULE_ACL_LOG_AUTH = 0, /* Authentication failure */
REDISMODULE_ACL_LOG_CMD, /* Command authorization failure */
@@ -764,7 +807,6 @@ typedef enum {
} RedisModuleACLLogEntryReason;
/* Incomplete structures needed by both the core and modules. */
-typedef struct RedisModuleString RedisModuleString;
typedef struct RedisModuleIO RedisModuleIO;
typedef struct RedisModuleDigest RedisModuleDigest;
typedef struct RedisModuleInfoCtx RedisModuleInfoCtx;
@@ -778,22 +820,6 @@ typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);
/* ------------------------- End of common defines ------------------------ */
-#if defined REDISMODULE_CORE
-/* Things only defined for the modules core (server), not exported to modules
- * that include this file. */
-
-#define RedisModuleString robj
-
-#endif /* defined REDISMODULE_CORE */
-
-#if !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE
-/* Things defined for modules, but not for core-modules. */
-
-typedef long long mstime_t;
-typedef long long ustime_t;
-
-#endif /* !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE */
-
/* ----------- The rest of the defines are only for modules ----------------- */
#if !defined REDISMODULE_CORE || defined REDISMODULE_CORE_MODULE
/* Things defined for modules and core-modules. */
@@ -826,7 +852,6 @@ typedef long long ustime_t;
/* Incomplete structures for compiler checks but opaque access. */
typedef struct RedisModuleCtx RedisModuleCtx;
typedef struct RedisModuleCommand RedisModuleCommand;
-typedef struct RedisModuleKey RedisModuleKey;
typedef struct RedisModuleCallReply RedisModuleCallReply;
typedef struct RedisModuleType RedisModuleType;
typedef struct RedisModuleBlockedClient RedisModuleBlockedClient;
diff --git a/src/server.c b/src/server.c
index dd0515412..52ce3d4d7 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1888,6 +1888,7 @@ void initServerConfig(void) {
server.bindaddr[j] = zstrdup(default_bindaddr[j]);
memset(server.listeners, 0x00, sizeof(server.listeners));
server.active_expire_enabled = 1;
+ server.lazy_expire_disabled = 0;
server.skip_checksum_validation = 0;
server.loading = 0;
server.async_loading = 0;
diff --git a/src/server.h b/src/server.h
index 190664ded..9dfbf81b7 100644
--- a/src/server.h
+++ b/src/server.h
@@ -291,6 +291,13 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
/* Key flags for when access type is unknown */
#define CMD_KEY_FULL_ACCESS (CMD_KEY_RW | CMD_KEY_ACCESS | CMD_KEY_UPDATE)
+/* Key flags for how key is removed */
+#define DB_FLAG_KEY_NONE 0
+#define DB_FLAG_KEY_DELETED (1ULL<<0)
+#define DB_FLAG_KEY_EXPIRED (1ULL<<1)
+#define DB_FLAG_KEY_EVICTED (1ULL<<2)
+#define DB_FLAG_KEY_OVERWRITE (1ULL<<3)
+
/* Channel flags share the same flag space as the key flags */
#define CMD_CHANNEL_PATTERN (1ULL<<11) /* The argument is a channel pattern */
#define CMD_CHANNEL_SUBSCRIBE (1ULL<<12) /* The command subscribes to channels */
@@ -1648,6 +1655,7 @@ struct redisServer {
int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */
int active_expire_enabled; /* Can be disabled for testing purposes. */
int active_expire_effort; /* From 1 (default) to 10, active effort. */
+ int lazy_expire_disabled; /* If > 0, don't trigger lazy expire */
int active_defrag_enabled;
int sanitize_dump_payload; /* Enables deep sanitization for ziplist and listpack in RDB and RESTORE. */
int skip_checksum_validation; /* Disable checksum validation for RDB and RESTORE payload. */
@@ -2440,7 +2448,7 @@ void moduleUnblockClient(client *c);
int moduleBlockedClientMayTimeout(client *c);
int moduleClientIsBlockedOnKeys(client *c);
void moduleNotifyUserChanged(client *c);
-void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid);
+void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid, int flags);
size_t moduleGetFreeEffort(robj *key, robj *val, int dbid);
size_t moduleGetMemUsage(robj *key, robj *val, size_t sample_size, int dbid);
robj *moduleTypeDupOrReply(client *c, robj *fromkey, robj *tokey, int todb, robj *value);
@@ -3154,7 +3162,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
void dbAdd(redisDb *db, robj *key, robj *val);
int dbAddRDBLoad(redisDb *db, sds key, robj *val);
-void dbOverwrite(redisDb *db, robj *key, robj *val);
+void dbReplaceValue(redisDb *db, robj *key, robj *val);
#define SETKEY_KEEPTTL 1
#define SETKEY_NO_SIGNAL 2
@@ -3162,6 +3170,7 @@ void dbOverwrite(redisDb *db, robj *key, robj *val);
#define SETKEY_DOESNT_EXIST 8
void setKey(client *c, redisDb *db, robj *key, robj *val, int flags);
robj *dbRandomKey(redisDb *db);
+int dbGenericDelete(redisDb *db, robj *key, int async, int flags);
int dbSyncDelete(redisDb *db, robj *key);
int dbDelete(redisDb *db, robj *key);
robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);
diff --git a/src/t_set.c b/src/t_set.c
index b56b38238..197cabcd9 100644
--- a/src/t_set.c
+++ b/src/t_set.c
@@ -885,7 +885,7 @@ void spopWithCountCommand(client *c) {
setTypeReleaseIterator(si);
/* Assign the new set as the key value. */
- dbOverwrite(c->db,c->argv[1],newset);
+ dbReplaceValue(c->db,c->argv[1],newset);
}
/* Don't propagate the command itself even if we incremented the
diff --git a/src/t_string.c b/src/t_string.c
index 832dceddf..7209b3ba7 100644
--- a/src/t_string.c
+++ b/src/t_string.c
@@ -612,7 +612,7 @@ void incrDecrCommand(client *c, long long incr) {
} else {
new = createStringObjectFromLongLongForValue(value);
if (o) {
- dbOverwrite(c->db,c->argv[1],new);
+ dbReplaceValue(c->db,c->argv[1],new);
} else {
dbAdd(c->db,c->argv[1],new);
}
@@ -667,7 +667,7 @@ void incrbyfloatCommand(client *c) {
}
new = createStringObjectFromLongDouble(value,1);
if (o)
- dbOverwrite(c->db,c->argv[1],new);
+ dbReplaceValue(c->db,c->argv[1],new);
else
dbAdd(c->db,c->argv[1],new);
signalModifiedKey(c,c->db,c->argv[1]);