Add support for nested snapshots
Former-commit-id: 43456c7807152062d59b2a90597b6204f637f5cd
This commit is contained in:
parent
219cd22fd6
commit
fda0f82d64
@ -1412,7 +1412,7 @@ int rewriteAppendOnlyFile(char *filename) {
|
|||||||
|
|
||||||
if (g_pserver->aof_use_rdb_preamble) {
|
if (g_pserver->aof_use_rdb_preamble) {
|
||||||
int error;
|
int error;
|
||||||
std::vector<redisDbPersistentData*> vecpdb;
|
std::vector<const redisDbPersistentData*> vecpdb;
|
||||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||||
{
|
{
|
||||||
vecpdb.push_back(&g_pserver->db[idb]);
|
vecpdb.push_back(&g_pserver->db[idb]);
|
||||||
|
127
src/db.cpp
127
src/db.cpp
@ -669,7 +669,43 @@ bool redisDbPersistentData::iterate(std::function<bool(const char*, robj*)> fn)
|
|||||||
return fResult;
|
return fResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool redisDbPersistentData::iterate(std::function<bool(const char*)> fn)
|
bool redisDbPersistentData::iterate_threadsafe(std::function<bool(const char*, robj_roptr o)> fn) const
|
||||||
|
{
|
||||||
|
dictIterator *di = dictGetSafeIterator(m_pdict);
|
||||||
|
dictEntry *de = nullptr;
|
||||||
|
bool fResult = true;
|
||||||
|
|
||||||
|
while((de = dictNext(di)) != nullptr)
|
||||||
|
{
|
||||||
|
if (!fn((const char*)dictGetKey(de), (robj*)dictGetVal(de)))
|
||||||
|
{
|
||||||
|
fResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dictReleaseIterator(di);
|
||||||
|
|
||||||
|
if (m_pdbSnapshot != nullptr)
|
||||||
|
{
|
||||||
|
fResult = m_pdbSnapshot->iterate_threadsafe([&](const char *key, robj_roptr o){
|
||||||
|
// Before passing off to the user we need to make sure it's not already in the
|
||||||
|
// the current set, and not deleted
|
||||||
|
dictEntry *deCurrent = dictFind(m_pdict, key);
|
||||||
|
if (deCurrent != nullptr)
|
||||||
|
return true;
|
||||||
|
dictEntry *deTombstone = dictFind(m_pdictTombstone, key);
|
||||||
|
if (deTombstone != nullptr)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Alright it's a key in the use keyspace, lets ensure it and then pass it off
|
||||||
|
return fn(key, o);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool redisDbPersistentData::iterate(std::function<bool(const char*)> fn) const
|
||||||
{
|
{
|
||||||
dictIterator *di = dictGetSafeIterator(m_pdict);
|
dictIterator *di = dictGetSafeIterator(m_pdict);
|
||||||
dictEntry *de = nullptr;
|
dictEntry *de = nullptr;
|
||||||
@ -704,7 +740,7 @@ bool redisDbPersistentData::iterate(std::function<bool(const char*)> fn)
|
|||||||
return fResult;
|
return fResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
void keysCommandCore(client *c, redisDbPersistentData *db, sds pattern)
|
void keysCommandCore(client *c, const redisDbPersistentData *db, sds pattern)
|
||||||
{
|
{
|
||||||
int plen = sdslen(pattern), allkeys;
|
int plen = sdslen(pattern), allkeys;
|
||||||
unsigned long numkeys = 0;
|
unsigned long numkeys = 0;
|
||||||
@ -737,7 +773,7 @@ int prepareClientToWrite(client *c, bool fAsync);
|
|||||||
void keysCommand(client *c) {
|
void keysCommand(client *c) {
|
||||||
sds pattern = szFromObj(c->argv[1]);
|
sds pattern = szFromObj(c->argv[1]);
|
||||||
|
|
||||||
redisDbPersistentData *snapshot = nullptr;
|
const redisDbPersistentData *snapshot = nullptr;
|
||||||
if (!(c->flags & (CLIENT_MULTI | CLIENT_BLOCKED)))
|
if (!(c->flags & (CLIENT_MULTI | CLIENT_BLOCKED)))
|
||||||
snapshot = c->db->createSnapshot(c->mvccCheckpoint);
|
snapshot = c->db->createSnapshot(c->mvccCheckpoint);
|
||||||
if (snapshot != nullptr)
|
if (snapshot != nullptr)
|
||||||
@ -1428,6 +1464,11 @@ expireEntry *redisDbPersistentData::getExpire(robj_roptr key) {
|
|||||||
return itrExpire.operator->();
|
return itrExpire.operator->();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const expireEntry *redisDbPersistentData::getExpire(robj_roptr key) const
|
||||||
|
{
|
||||||
|
return const_cast<redisDbPersistentData*>(this)->getExpire(key);
|
||||||
|
}
|
||||||
|
|
||||||
/* Propagate expires into slaves and the AOF file.
|
/* Propagate expires into slaves and the AOF file.
|
||||||
* When a key expires in the master, a DEL operation for this key is sent
|
* When a key expires in the master, a DEL operation for this key is sent
|
||||||
* to all the slaves and the AOF file if enabled.
|
* to all the slaves and the AOF file if enabled.
|
||||||
@ -1892,6 +1933,7 @@ unsigned int countKeysInSlot(unsigned int hashslot) {
|
|||||||
|
|
||||||
void redisDbPersistentData::initialize()
|
void redisDbPersistentData::initialize()
|
||||||
{
|
{
|
||||||
|
m_pdbSnapshot = nullptr;
|
||||||
m_pdict = dictCreate(&dbDictType,this);
|
m_pdict = dictCreate(&dbDictType,this);
|
||||||
m_pdictTombstone = dictCreate(&dbDictType,this);
|
m_pdictTombstone = dictCreate(&dbDictType,this);
|
||||||
m_setexpire = new(MALLOC_LOCAL) expireset();
|
m_setexpire = new(MALLOC_LOCAL) expireset();
|
||||||
@ -2120,45 +2162,85 @@ void redisDbPersistentData::processChanges()
|
|||||||
m_setchanged.clear();
|
m_setchanged.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
redisDbPersistentData *redisDbPersistentData::createSnapshot(uint64_t mvccCheckpoint)
|
const redisDbPersistentData *redisDbPersistentData::createSnapshot(uint64_t mvccCheckpoint)
|
||||||
{
|
{
|
||||||
serverAssert(GlobalLocksAcquired());
|
serverAssert(GlobalLocksAcquired());
|
||||||
if (m_spdbSnapshotHOLDER != nullptr)
|
if (m_spdbSnapshotHOLDER != nullptr)
|
||||||
{
|
{
|
||||||
if (mvccCheckpoint <= m_spdbSnapshotHOLDER->mvccCheckpoint)
|
if (mvccCheckpoint <= m_spdbSnapshotHOLDER->mvccCheckpoint)
|
||||||
{
|
{
|
||||||
++m_snapshotRefcount;
|
m_spdbSnapshotHOLDER->m_refCount++;
|
||||||
return m_spdbSnapshotHOLDER.get();
|
return m_spdbSnapshotHOLDER.get();
|
||||||
}
|
}
|
||||||
return nullptr;
|
serverLog(LL_WARNING, "Nested snapshot created");
|
||||||
}
|
}
|
||||||
auto spdb = std::make_unique<redisDbPersistentData>();
|
auto spdb = std::unique_ptr<redisDbPersistentData>(new (MALLOC_LOCAL) redisDbPersistentData());
|
||||||
|
|
||||||
spdb->m_fAllChanged = false;
|
spdb->m_fAllChanged = false;
|
||||||
spdb->m_fTrackingChanges = 0;
|
spdb->m_fTrackingChanges = 0;
|
||||||
spdb->m_pdict = m_pdict;
|
spdb->m_pdict = m_pdict;
|
||||||
|
spdb->m_pdictTombstone = m_pdictTombstone;
|
||||||
spdb->m_pdict->iterators++; // fake an iterator so it doesn't rehash
|
spdb->m_pdict->iterators++; // fake an iterator so it doesn't rehash
|
||||||
|
spdb->m_spdbSnapshotHOLDER = std::move(m_spdbSnapshotHOLDER);
|
||||||
|
spdb->m_pdbSnapshot = m_pdbSnapshot;
|
||||||
|
spdb->m_refCount = 1;
|
||||||
if (m_setexpire != nullptr)
|
if (m_setexpire != nullptr)
|
||||||
spdb->m_setexpire = m_setexpire;
|
spdb->m_setexpire = m_setexpire;
|
||||||
|
|
||||||
m_pdict = dictCreate(&dbDictType,this);
|
m_pdict = dictCreate(&dbDictType,this);
|
||||||
|
m_pdictTombstone = dictCreate(&dbDictType, this);
|
||||||
m_setexpire = new (MALLOC_LOCAL) expireset();
|
m_setexpire = new (MALLOC_LOCAL) expireset();
|
||||||
|
|
||||||
m_spdbSnapshotHOLDER = std::move(spdb);
|
m_spdbSnapshotHOLDER = std::move(spdb);
|
||||||
m_pdbSnapshot = m_spdbSnapshotHOLDER.get();
|
m_pdbSnapshot = m_spdbSnapshotHOLDER.get();
|
||||||
++m_snapshotRefcount;
|
|
||||||
|
// Finally we need to take a ref on all our children snapshots. This ensures they aren't free'd before we are
|
||||||
|
redisDbPersistentData *pdbSnapshotNext = m_pdbSnapshot->m_spdbSnapshotHOLDER.get();
|
||||||
|
while (pdbSnapshotNext != nullptr)
|
||||||
|
{
|
||||||
|
pdbSnapshotNext->m_refCount++;
|
||||||
|
pdbSnapshotNext = pdbSnapshotNext->m_spdbSnapshotHOLDER.get();
|
||||||
|
}
|
||||||
|
|
||||||
return m_pdbSnapshot;
|
return m_pdbSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void redisDbPersistentData::recursiveFreeSnapshots(redisDbPersistentData *psnapshot)
|
||||||
|
{
|
||||||
|
std::vector<redisDbPersistentData*> m_stackSnapshots;
|
||||||
|
// gather a stack of snapshots, we do this so we can free them in reverse
|
||||||
|
|
||||||
|
// Note: we don't touch the incoming psnapshot since the parent is free'ing that one
|
||||||
|
while ((psnapshot = psnapshot->m_spdbSnapshotHOLDER.get()) != nullptr)
|
||||||
|
{
|
||||||
|
m_stackSnapshots.push_back(psnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto itr = m_stackSnapshots.rbegin(); itr != m_stackSnapshots.rend(); ++itr)
|
||||||
|
{
|
||||||
|
endSnapshot(*itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void redisDbPersistentData::endSnapshot(const redisDbPersistentData *psnapshot)
|
void redisDbPersistentData::endSnapshot(const redisDbPersistentData *psnapshot)
|
||||||
{
|
{
|
||||||
if (!GlobalLocksAcquired())
|
// Note: This function is dependent on GlobalLocksAcquried(), but rdb background saving has a weird case where
|
||||||
serverLog(LL_WARNING, "Global locks not acquired");
|
// a seperate thread holds the lock for it. Yes that's pretty crazy and should be fixed somehow...
|
||||||
serverAssert(m_spdbSnapshotHOLDER.get() == psnapshot);
|
|
||||||
|
|
||||||
--m_snapshotRefcount;
|
if (m_spdbSnapshotHOLDER.get() != psnapshot)
|
||||||
if (m_snapshotRefcount > 0)
|
{
|
||||||
|
serverAssert(m_spdbSnapshotHOLDER != nullptr);
|
||||||
|
m_spdbSnapshotHOLDER->endSnapshot(psnapshot);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_spdbSnapshotHOLDER->m_refCount--;
|
||||||
|
if (m_spdbSnapshotHOLDER->m_refCount > 0)
|
||||||
|
return;
|
||||||
|
serverAssert(m_spdbSnapshotHOLDER->m_refCount == 0);
|
||||||
|
|
||||||
|
// Alright we're ready to be free'd, but first dump all the refs on our child snapshots
|
||||||
|
recursiveFreeSnapshots(m_spdbSnapshotHOLDER.get());
|
||||||
|
|
||||||
m_spdbSnapshotHOLDER->m_pdict->iterators--;
|
m_spdbSnapshotHOLDER->m_pdict->iterators--;
|
||||||
|
|
||||||
@ -2166,7 +2248,7 @@ void redisDbPersistentData::endSnapshot(const redisDbPersistentData *psnapshot)
|
|||||||
{
|
{
|
||||||
// the database was cleared so we don't need to recover the snapshot
|
// the database was cleared so we don't need to recover the snapshot
|
||||||
dictEmpty(m_pdictTombstone, nullptr);
|
dictEmpty(m_pdictTombstone, nullptr);
|
||||||
m_spdbSnapshotHOLDER = nullptr;
|
m_spdbSnapshotHOLDER = std::move(m_spdbSnapshotHOLDER->m_spdbSnapshotHOLDER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2220,12 +2302,25 @@ void redisDbPersistentData::endSnapshot(const redisDbPersistentData *psnapshot)
|
|||||||
std::swap(m_setexpire, m_spdbSnapshotHOLDER->m_setexpire);
|
std::swap(m_setexpire, m_spdbSnapshotHOLDER->m_setexpire);
|
||||||
|
|
||||||
// Finally free the snapshot
|
// Finally free the snapshot
|
||||||
m_spdbSnapshotHOLDER = nullptr;
|
if (m_pdbSnapshot != nullptr && m_spdbSnapshotHOLDER->m_pdbSnapshot != nullptr)
|
||||||
m_pdbSnapshot = nullptr;
|
{
|
||||||
|
m_pdbSnapshot = m_spdbSnapshotHOLDER->m_pdbSnapshot;
|
||||||
|
m_spdbSnapshotHOLDER->m_pdbSnapshot = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_pdbSnapshot = nullptr;
|
||||||
|
}
|
||||||
|
m_spdbSnapshotHOLDER = std::move(m_spdbSnapshotHOLDER->m_spdbSnapshotHOLDER);
|
||||||
|
serverAssert(m_spdbSnapshotHOLDER != nullptr || m_pdbSnapshot == nullptr);
|
||||||
|
serverAssert(m_pdbSnapshot == m_spdbSnapshotHOLDER.get() || m_pdbSnapshot == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
redisDbPersistentData::~redisDbPersistentData()
|
redisDbPersistentData::~redisDbPersistentData()
|
||||||
{
|
{
|
||||||
|
serverAssert(m_spdbSnapshotHOLDER == nullptr);
|
||||||
|
serverAssert(m_pdbSnapshot == nullptr);
|
||||||
|
serverAssert(m_refCount == 0);
|
||||||
dictRelease(m_pdict);
|
dictRelease(m_pdict);
|
||||||
if (m_pdictTombstone)
|
if (m_pdictTombstone)
|
||||||
dictRelease(m_pdictTombstone);
|
dictRelease(m_pdictTombstone);
|
||||||
|
@ -347,7 +347,7 @@ uint8_t LFULogIncr(uint8_t counter) {
|
|||||||
* This function is used in order to scan the dataset for the best object
|
* This function is used in order to scan the dataset for the best object
|
||||||
* to fit: as we check for the candidate, we incrementally decrement the
|
* to fit: as we check for the candidate, we incrementally decrement the
|
||||||
* counter of the scanned objects if needed. */
|
* counter of the scanned objects if needed. */
|
||||||
unsigned long LFUDecrAndReturn(robj *o) {
|
unsigned long LFUDecrAndReturn(robj_roptr o) {
|
||||||
unsigned long ldt = o->lru >> 8;
|
unsigned long ldt = o->lru >> 8;
|
||||||
unsigned long counter = o->lru & 255;
|
unsigned long counter = o->lru & 255;
|
||||||
unsigned long num_periods = g_pserver->lfu_decay_time ? LFUTimeElapsed(ldt) / g_pserver->lfu_decay_time : 0;
|
unsigned long num_periods = g_pserver->lfu_decay_time ? LFUTimeElapsed(ldt) / g_pserver->lfu_decay_time : 0;
|
||||||
|
@ -6,7 +6,7 @@ extern "C" {
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
|
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
|
||||||
int rdbSaveS3(char *s3bucket, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
int rdbSaveS3(char *s3bucket, const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
||||||
{
|
{
|
||||||
int status = EXIT_FAILURE;
|
int status = EXIT_FAILURE;
|
||||||
int fd[2];
|
int fd[2];
|
||||||
|
30
src/rdb.cpp
30
src/rdb.cpp
@ -755,7 +755,7 @@ size_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {
|
|||||||
|
|
||||||
/* Save a Redis object.
|
/* Save a Redis object.
|
||||||
* Returns -1 on error, number of bytes written on success. */
|
* Returns -1 on error, number of bytes written on success. */
|
||||||
ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
|
ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj_roptr key) {
|
||||||
ssize_t n = 0, nwritten = 0;
|
ssize_t n = 0, nwritten = 0;
|
||||||
|
|
||||||
if (o->type == OBJ_STRING) {
|
if (o->type == OBJ_STRING) {
|
||||||
@ -970,7 +970,7 @@ ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
|
|||||||
RedisModuleIO io;
|
RedisModuleIO io;
|
||||||
moduleValue *mv = (moduleValue*)ptrFromObj(o);
|
moduleValue *mv = (moduleValue*)ptrFromObj(o);
|
||||||
moduleType *mt = mv->type;
|
moduleType *mt = mv->type;
|
||||||
moduleInitIOContext(io,mt,rdb,key);
|
moduleInitIOContext(io,mt,rdb,key.unsafe_robjcast());
|
||||||
|
|
||||||
/* Write the "module" identifier as prefix, so that we'll be able
|
/* Write the "module" identifier as prefix, so that we'll be able
|
||||||
* to call the right module during loading. */
|
* to call the right module during loading. */
|
||||||
@ -1034,7 +1034,7 @@ size_t rdbSavedObjectLen(robj *o) {
|
|||||||
* On error -1 is returned.
|
* On error -1 is returned.
|
||||||
* On success if the key was actually saved 1 is returned, otherwise 0
|
* On success if the key was actually saved 1 is returned, otherwise 0
|
||||||
* is returned (the key was already expired). */
|
* is returned (the key was already expired). */
|
||||||
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, expireEntry *pexpire) {
|
int rdbSaveKeyValuePair(rio *rdb, robj_roptr key, robj_roptr val, const expireEntry *pexpire) {
|
||||||
int savelru = g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LRU;
|
int savelru = g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LRU;
|
||||||
int savelfu = g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU;
|
int savelfu = g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU;
|
||||||
|
|
||||||
@ -1115,12 +1115,12 @@ int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int saveKey(rio *rdb, redisDbPersistentData *db, int flags, size_t *processed, const char *keystr, robj *o)
|
int saveKey(rio *rdb, const redisDbPersistentData *db, int flags, size_t *processed, const char *keystr, robj_roptr o)
|
||||||
{
|
{
|
||||||
robj key;
|
robj key;
|
||||||
|
|
||||||
initStaticStringObject(key,(char*)keystr);
|
initStaticStringObject(key,(char*)keystr);
|
||||||
expireEntry *pexpire = db->getExpire(&key);
|
const expireEntry *pexpire = db->getExpire(&key);
|
||||||
|
|
||||||
if (rdbSaveKeyValuePair(rdb,&key,o,pexpire) == -1)
|
if (rdbSaveKeyValuePair(rdb,&key,o,pexpire) == -1)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1145,7 +1145,7 @@ int saveKey(rio *rdb, redisDbPersistentData *db, int flags, size_t *processed, c
|
|||||||
* When the function returns C_ERR and if 'error' is not NULL, the
|
* When the function returns C_ERR and if 'error' is not NULL, the
|
||||||
* integer pointed by 'error' is set to the value of errno just after the I/O
|
* integer pointed by 'error' is set to the value of errno just after the I/O
|
||||||
* error. */
|
* error. */
|
||||||
int rdbSaveRio(rio *rdb, redisDbPersistentData **rgpdb, int *error, int flags, rdbSaveInfo *rsi) {
|
int rdbSaveRio(rio *rdb, const redisDbPersistentData **rgpdb, int *error, int flags, rdbSaveInfo *rsi) {
|
||||||
dictEntry *de;
|
dictEntry *de;
|
||||||
dictIterator *di = NULL;
|
dictIterator *di = NULL;
|
||||||
char magic[10];
|
char magic[10];
|
||||||
@ -1161,7 +1161,7 @@ int rdbSaveRio(rio *rdb, redisDbPersistentData **rgpdb, int *error, int flags, r
|
|||||||
if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr;
|
if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr;
|
||||||
|
|
||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
redisDbPersistentData *db = rgpdb[j];
|
const redisDbPersistentData *db = rgpdb[j];
|
||||||
if (db->size() == 0) continue;
|
if (db->size() == 0) continue;
|
||||||
|
|
||||||
/* Write the SELECT DB opcode */
|
/* Write the SELECT DB opcode */
|
||||||
@ -1181,7 +1181,7 @@ int rdbSaveRio(rio *rdb, redisDbPersistentData **rgpdb, int *error, int flags, r
|
|||||||
|
|
||||||
/* Iterate this DB writing every entry */
|
/* Iterate this DB writing every entry */
|
||||||
size_t ckeysExpired = 0;
|
size_t ckeysExpired = 0;
|
||||||
bool fSavedAll = db->iterate([&](const char *keystr, robj *o)->bool {
|
bool fSavedAll = db->iterate_threadsafe([&](const char *keystr, robj_roptr o)->bool {
|
||||||
if (o->FExpires())
|
if (o->FExpires())
|
||||||
++ckeysExpired;
|
++ckeysExpired;
|
||||||
|
|
||||||
@ -1239,7 +1239,7 @@ werr:
|
|||||||
* While the suffix is the 40 bytes hex string we announced in the prefix.
|
* While the suffix is the 40 bytes hex string we announced in the prefix.
|
||||||
* This way processes receiving the payload can understand when it ends
|
* This way processes receiving the payload can understand when it ends
|
||||||
* without doing any processing of the content. */
|
* without doing any processing of the content. */
|
||||||
int rdbSaveRioWithEOFMark(rio *rdb, redisDbPersistentData **rgpdb, int *error, rdbSaveInfo *rsi) {
|
int rdbSaveRioWithEOFMark(rio *rdb, const redisDbPersistentData **rgpdb, int *error, rdbSaveInfo *rsi) {
|
||||||
char eofmark[RDB_EOF_MARK_SIZE];
|
char eofmark[RDB_EOF_MARK_SIZE];
|
||||||
|
|
||||||
getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);
|
getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);
|
||||||
@ -1257,7 +1257,7 @@ werr: /* Write error. */
|
|||||||
return C_ERR;
|
return C_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rdbSaveFp(FILE *fp, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
int rdbSaveFp(FILE *fp, const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
rio rdb;
|
rio rdb;
|
||||||
@ -1274,9 +1274,9 @@ int rdbSaveFp(FILE *fp, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
|||||||
return C_OK;
|
return C_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rdbSave(redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
int rdbSave(const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
||||||
{
|
{
|
||||||
std::vector<redisDbPersistentData*> vecdb;
|
std::vector<const redisDbPersistentData*> vecdb;
|
||||||
if (rgpdb == nullptr)
|
if (rgpdb == nullptr)
|
||||||
{
|
{
|
||||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||||
@ -1296,7 +1296,7 @@ int rdbSave(redisDbPersistentData **rgpdb, rdbSaveInfo *rsi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
|
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
|
||||||
int rdbSaveFile(char *filename, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi) {
|
int rdbSaveFile(char *filename, const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi) {
|
||||||
char tmpfile[256];
|
char tmpfile[256];
|
||||||
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
|
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@ -1360,7 +1360,7 @@ werr:
|
|||||||
struct rdbSaveThreadArgs
|
struct rdbSaveThreadArgs
|
||||||
{
|
{
|
||||||
rdbSaveInfo rsi;
|
rdbSaveInfo rsi;
|
||||||
redisDbPersistentData *rgpdb[1]; // NOTE: Variable Length
|
const redisDbPersistentData *rgpdb[1]; // NOTE: Variable Length
|
||||||
};
|
};
|
||||||
|
|
||||||
void *rdbSaveThread(void *vargs)
|
void *rdbSaveThread(void *vargs)
|
||||||
@ -2453,7 +2453,7 @@ struct rdbSaveSocketThreadArgs
|
|||||||
int *fds;
|
int *fds;
|
||||||
int numfds;
|
int numfds;
|
||||||
uint64_t *clientids;
|
uint64_t *clientids;
|
||||||
redisDbPersistentData *rgpdb[1];
|
const redisDbPersistentData *rgpdb[1];
|
||||||
};
|
};
|
||||||
void *rdbSaveToSlavesSocketsThread(void *vargs)
|
void *rdbSaveToSlavesSocketsThread(void *vargs)
|
||||||
{
|
{
|
||||||
|
11
src/rdb.h
11
src/rdb.h
@ -140,16 +140,15 @@ int rdbLoadFile(const char *filename, rdbSaveInfo *rsi);
|
|||||||
int rdbSaveBackground(rdbSaveInfo *rsi);
|
int rdbSaveBackground(rdbSaveInfo *rsi);
|
||||||
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
|
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
|
||||||
void rdbRemoveTempFile(int childpid);
|
void rdbRemoveTempFile(int childpid);
|
||||||
int rdbSave(redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
int rdbSave(const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
||||||
int rdbSaveFile(char *filename, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
int rdbSaveFile(char *filename, const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
||||||
int rdbSaveFp(FILE *pf, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
int rdbSaveFp(FILE *pf, const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
||||||
int rdbSaveS3(char *path, redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
int rdbSaveS3(char *path, const redisDbPersistentData **rgpdb, rdbSaveInfo *rsi);
|
||||||
int rdbLoadS3(char *path, rdbSaveInfo *rsi);
|
int rdbLoadS3(char *path, rdbSaveInfo *rsi);
|
||||||
ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key);
|
ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj_roptr key);
|
||||||
size_t rdbSavedObjectLen(robj *o);
|
size_t rdbSavedObjectLen(robj *o);
|
||||||
robj *rdbLoadObject(int type, rio *rdb, robj *key, uint64_t mvcc_tstamp);
|
robj *rdbLoadObject(int type, rio *rdb, robj *key, uint64_t mvcc_tstamp);
|
||||||
void backgroundSaveDoneHandler(int exitcode, int bysignal);
|
void backgroundSaveDoneHandler(int exitcode, int bysignal);
|
||||||
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime);
|
|
||||||
robj *rdbLoadStringObject(rio *rdb);
|
robj *rdbLoadStringObject(rio *rdb);
|
||||||
ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj);
|
ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj);
|
||||||
ssize_t rdbSaveRawString(rio *rdb, const unsigned char *s, size_t len);
|
ssize_t rdbSaveRawString(rio *rdb, const unsigned char *s, size_t len);
|
||||||
|
@ -1183,6 +1183,7 @@ void dictSdsNOPDestructor(void *, void *) {}
|
|||||||
|
|
||||||
void dictDbKeyDestructor(void *privdata, void *key)
|
void dictDbKeyDestructor(void *privdata, void *key)
|
||||||
{
|
{
|
||||||
|
DICT_NOTUSED(privdata);
|
||||||
sdsfree((sds)key);
|
sdsfree((sds)key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
src/server.h
26
src/server.h
@ -935,7 +935,7 @@ public:
|
|||||||
bool FEmpty() const noexcept { return m_vecexpireEntries.empty(); }
|
bool FEmpty() const noexcept { return m_vecexpireEntries.empty(); }
|
||||||
const subexpireEntry &nextExpireEntry() const noexcept { return m_vecexpireEntries.front(); }
|
const subexpireEntry &nextExpireEntry() const noexcept { return m_vecexpireEntries.front(); }
|
||||||
void popfrontExpireEntry() { m_vecexpireEntries.erase(m_vecexpireEntries.begin()); }
|
void popfrontExpireEntry() { m_vecexpireEntries.erase(m_vecexpireEntries.begin()); }
|
||||||
const subexpireEntry &operator[](size_t idx) { return m_vecexpireEntries[idx]; }
|
const subexpireEntry &operator[](size_t idx) const { return m_vecexpireEntries[idx]; }
|
||||||
size_t size() const noexcept { return m_vecexpireEntries.size(); }
|
size_t size() const noexcept { return m_vecexpireEntries.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -951,11 +951,11 @@ public:
|
|||||||
class iter
|
class iter
|
||||||
{
|
{
|
||||||
friend class expireEntry;
|
friend class expireEntry;
|
||||||
expireEntry *m_pentry = nullptr;
|
const expireEntry *m_pentry = nullptr;
|
||||||
size_t m_idx = 0;
|
size_t m_idx = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
iter(expireEntry *pentry, size_t idx)
|
iter(const expireEntry *pentry, size_t idx)
|
||||||
: m_pentry(pentry), m_idx(idx)
|
: m_pentry(pentry), m_idx(idx)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -1035,6 +1035,7 @@ public:
|
|||||||
|
|
||||||
inline bool FFat() const noexcept { return m_when == LLONG_MIN; }
|
inline bool FFat() const noexcept { return m_when == LLONG_MIN; }
|
||||||
expireEntryFat *pfatentry() { assert(FFat()); return u.m_pfatentry; }
|
expireEntryFat *pfatentry() { assert(FFat()); return u.m_pfatentry; }
|
||||||
|
const expireEntryFat *pfatentry() const { assert(FFat()); return u.m_pfatentry; }
|
||||||
|
|
||||||
|
|
||||||
bool operator==(const char *key) const noexcept
|
bool operator==(const char *key) const noexcept
|
||||||
@ -1087,8 +1088,8 @@ public:
|
|||||||
u.m_pfatentry->expireSubKey(subkey, when);
|
u.m_pfatentry->expireSubKey(subkey, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter begin() { return iter(this, 0); }
|
iter begin() const { return iter(this, 0); }
|
||||||
iter end()
|
iter end() const
|
||||||
{
|
{
|
||||||
if (FFat())
|
if (FFat())
|
||||||
return iter(this, u.m_pfatentry->size());
|
return iter(this, u.m_pfatentry->size());
|
||||||
@ -1103,7 +1104,7 @@ public:
|
|||||||
pfatentry()->m_vecexpireEntries.begin() + itr.m_idx);
|
pfatentry()->m_vecexpireEntries.begin() + itr.m_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FGetPrimaryExpire(long long *pwhen)
|
bool FGetPrimaryExpire(long long *pwhen) const
|
||||||
{
|
{
|
||||||
*pwhen = -1;
|
*pwhen = -1;
|
||||||
for (auto itr : *this)
|
for (auto itr : *this)
|
||||||
@ -1280,10 +1281,12 @@ public:
|
|||||||
void emptyDbAsync();
|
void emptyDbAsync();
|
||||||
// Note: If you do not need the obj then use the objless iterator version. It's faster
|
// Note: If you do not need the obj then use the objless iterator version. It's faster
|
||||||
bool iterate(std::function<bool(const char*, robj*)> fn);
|
bool iterate(std::function<bool(const char*, robj*)> fn);
|
||||||
bool iterate(std::function<bool(const char*)> fn);
|
bool iterate_threadsafe(std::function<bool(const char*, robj_roptr o)> fn) const;
|
||||||
|
bool iterate(std::function<bool(const char*)> fn) const;
|
||||||
void setExpire(robj *key, robj *subkey, long long when);
|
void setExpire(robj *key, robj *subkey, long long when);
|
||||||
void setExpire(expireEntry &&e);
|
void setExpire(expireEntry &&e);
|
||||||
expireEntry *getExpire(robj_roptr key);
|
expireEntry *getExpire(robj_roptr key);
|
||||||
|
const expireEntry *getExpire(robj_roptr key) const;
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
void trackChanges() { m_fTrackingChanges++; }
|
void trackChanges() { m_fTrackingChanges++; }
|
||||||
@ -1296,7 +1299,7 @@ public:
|
|||||||
expireset *setexpireUnsafe() { return m_setexpire; }
|
expireset *setexpireUnsafe() { return m_setexpire; }
|
||||||
const expireset *setexpire() { return m_setexpire; }
|
const expireset *setexpire() { return m_setexpire; }
|
||||||
|
|
||||||
redisDbPersistentData *createSnapshot(uint64_t mvccCheckpoint);
|
const redisDbPersistentData *createSnapshot(uint64_t mvccCheckpoint);
|
||||||
void endSnapshot(const redisDbPersistentData *psnapshot);
|
void endSnapshot(const redisDbPersistentData *psnapshot);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1304,6 +1307,7 @@ private:
|
|||||||
void ensure(const char *key, dictEntry **de);
|
void ensure(const char *key, dictEntry **de);
|
||||||
void storeDatabase();
|
void storeDatabase();
|
||||||
void storeKey(const char *key, size_t cchKey, robj *o);
|
void storeKey(const char *key, size_t cchKey, robj *o);
|
||||||
|
void recursiveFreeSnapshots(redisDbPersistentData *psnapshot);
|
||||||
|
|
||||||
// Keyspace
|
// Keyspace
|
||||||
dict *m_pdict = nullptr; /* The keyspace for this DB */
|
dict *m_pdict = nullptr; /* The keyspace for this DB */
|
||||||
@ -1322,7 +1326,7 @@ private:
|
|||||||
// in a snapshot
|
// in a snapshot
|
||||||
redisDbPersistentData *m_pdbSnapshot = nullptr;
|
redisDbPersistentData *m_pdbSnapshot = nullptr;
|
||||||
std::unique_ptr<redisDbPersistentData> m_spdbSnapshotHOLDER;
|
std::unique_ptr<redisDbPersistentData> m_spdbSnapshotHOLDER;
|
||||||
int m_snapshotRefcount = 0;
|
int m_refCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Redis database representation. There are multiple databases identified
|
/* Redis database representation. There are multiple databases identified
|
||||||
@ -2555,7 +2559,7 @@ int writeCommandsDeniedByDiskError(void);
|
|||||||
|
|
||||||
/* RDB persistence */
|
/* RDB persistence */
|
||||||
#include "rdb.h"
|
#include "rdb.h"
|
||||||
int rdbSaveRio(rio *rdb, redisDbPersistentData **rgpdb, int *error, int flags, rdbSaveInfo *rsi);
|
int rdbSaveRio(rio *rdb, const redisDbPersistentData **rgpdb, int *error, int flags, rdbSaveInfo *rsi);
|
||||||
void killRDBChild(void);
|
void killRDBChild(void);
|
||||||
|
|
||||||
/* AOF persistence */
|
/* AOF persistence */
|
||||||
@ -2885,7 +2889,7 @@ void evictionPoolAlloc(void);
|
|||||||
#define LFU_INIT_VAL 5
|
#define LFU_INIT_VAL 5
|
||||||
unsigned long LFUGetTimeInMinutes(void);
|
unsigned long LFUGetTimeInMinutes(void);
|
||||||
uint8_t LFULogIncr(uint8_t value);
|
uint8_t LFULogIncr(uint8_t value);
|
||||||
unsigned long LFUDecrAndReturn(robj *o);
|
unsigned long LFUDecrAndReturn(robj_roptr o);
|
||||||
|
|
||||||
/* Keys hashing / comparison functions for dict.c hash tables. */
|
/* Keys hashing / comparison functions for dict.c hash tables. */
|
||||||
uint64_t dictSdsHash(const void *key);
|
uint64_t dictSdsHash(const void *key);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user