mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2024-12-14 11:58:02 +00:00
Add t_hash.c file.
Add some compact_object tests for set,hset,zset interfaces. Enable memory leak checking for mimalloc allocator in compact_object_test.
This commit is contained in:
parent
8c95facb47
commit
7ffbadd305
12 changed files with 1329 additions and 53 deletions
|
@ -9,6 +9,7 @@
|
|||
|
||||
extern "C" {
|
||||
#include "redis/intset.h"
|
||||
#include "redis/listpack.h"
|
||||
#include "redis/object.h"
|
||||
#include "redis/util.h"
|
||||
#include "redis/zmalloc.h" // for non-string objects.
|
||||
|
@ -242,7 +243,16 @@ void RobjWrapper::Free(std::pmr::memory_resource* mr) {
|
|||
LOG(FATAL) << "TBD";
|
||||
break;
|
||||
case OBJ_HASH:
|
||||
LOG(FATAL) << "Unsupported HASH type";
|
||||
switch (encoding) {
|
||||
case OBJ_ENCODING_HT:
|
||||
dictRelease((dict*)ptr);
|
||||
break;
|
||||
case OBJ_ENCODING_LISTPACK:
|
||||
lpFree((uint8_t*)ptr);
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "Unknown hset encoding type";
|
||||
}
|
||||
break;
|
||||
case OBJ_MODULE:
|
||||
LOG(FATAL) << "Unsupported OBJ_MODULE type";
|
||||
|
@ -520,11 +530,10 @@ robj* CompactObj::AsRObj() const {
|
|||
}
|
||||
|
||||
void CompactObj::SyncRObj() {
|
||||
CHECK_EQ(ROBJ_TAG, taglen_);
|
||||
|
||||
robj* obj = &tl.tmp_robj;
|
||||
|
||||
CHECK_EQ(u_.r_obj.type, obj->type);
|
||||
DCHECK_EQ(ROBJ_TAG, taglen_);
|
||||
DCHECK_EQ(u_.r_obj.type, obj->type);
|
||||
|
||||
u_.r_obj.encoding = obj->encoding;
|
||||
u_.r_obj.blob.Set(obj->ptr, 0);
|
||||
|
|
|
@ -204,6 +204,7 @@ class CompactObj {
|
|||
|
||||
unsigned Encoding() const;
|
||||
unsigned ObjType() const;
|
||||
|
||||
quicklist* GetQL() const;
|
||||
|
||||
// Takes ownership over o.
|
||||
|
@ -215,11 +216,12 @@ class CompactObj {
|
|||
// Requires: AsRObj() has been called before in the same thread in fiber-atomic section.
|
||||
void SyncRObj();
|
||||
|
||||
// For STR object.
|
||||
void SetInt(int64_t val);
|
||||
std::optional<int64_t> TryGetInt() const;
|
||||
|
||||
// For STR object.
|
||||
void SetString(std::string_view str);
|
||||
|
||||
void GetString(std::string* res) const;
|
||||
|
||||
size_t MallocUsed() const;
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
//
|
||||
#include "core/compact_object.h"
|
||||
|
||||
#include <mimalloc.h>
|
||||
#include <xxhash.h>
|
||||
|
||||
#include "base/gtest.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
extern "C" {
|
||||
#include "redis/object.h"
|
||||
#include "redis/redis_aux.h"
|
||||
#include "redis/zmalloc.h"
|
||||
}
|
||||
|
||||
|
@ -25,18 +28,35 @@ void PrintTo(const CompactObj& cobj, std::ostream* os) {
|
|||
|
||||
class CompactObjectTest : public ::testing::Test {
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
static void SetUpTestSuite() {
|
||||
InitRedisTables(); // to initialize server struct.
|
||||
|
||||
init_zmalloc_threadlocal();
|
||||
CompactObj::InitThreadLocal(pmr::get_default_resource());
|
||||
}
|
||||
|
||||
CompactObj cs_;
|
||||
static void TearDownTestSuite() {
|
||||
mi_heap_collect(mi_heap_get_backing(), true);
|
||||
|
||||
auto cb_visit = [](const mi_heap_t* heap, const mi_heap_area_t* area, void* block,
|
||||
size_t block_size, void* arg) {
|
||||
LOG(ERROR) << "Unfreed allocations: block_size " << block_size
|
||||
<< ", allocated: " << area->used * block_size;
|
||||
return true;
|
||||
};
|
||||
|
||||
mi_heap_visit_blocks(mi_heap_get_backing(), false /* do not visit all blocks*/, cb_visit,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CompactObj cobj_;
|
||||
string tmp_;
|
||||
};
|
||||
|
||||
TEST_F(CompactObjectTest, Basic) {
|
||||
robj* rv = createRawStringObject("foo", 3);
|
||||
cs_.ImportRObj(rv);
|
||||
cobj_.ImportRObj(rv);
|
||||
return;
|
||||
|
||||
CompactObj a;
|
||||
|
||||
|
@ -67,22 +87,21 @@ TEST_F(CompactObjectTest, NonInline) {
|
|||
}
|
||||
|
||||
TEST_F(CompactObjectTest, Int) {
|
||||
cs_.SetString("0");
|
||||
EXPECT_EQ(0, cs_.TryGetInt());
|
||||
EXPECT_EQ(cs_, "0");
|
||||
EXPECT_EQ("0", cs_.GetSlice(&tmp_));
|
||||
EXPECT_EQ(OBJ_STRING, cs_.ObjType());
|
||||
cs_.SetString("42");
|
||||
EXPECT_EQ(8181779779123079347, cs_.HashCode());
|
||||
EXPECT_EQ(OBJ_ENCODING_INT, cs_.Encoding());
|
||||
cobj_.SetString("0");
|
||||
EXPECT_EQ(0, cobj_.TryGetInt());
|
||||
EXPECT_EQ(cobj_, "0");
|
||||
EXPECT_EQ("0", cobj_.GetSlice(&tmp_));
|
||||
EXPECT_EQ(OBJ_STRING, cobj_.ObjType());
|
||||
cobj_.SetString("42");
|
||||
EXPECT_EQ(8181779779123079347, cobj_.HashCode());
|
||||
EXPECT_EQ(OBJ_ENCODING_INT, cobj_.Encoding());
|
||||
}
|
||||
|
||||
TEST_F(CompactObjectTest, MediumString) {
|
||||
CompactObj obj;
|
||||
string tmp(512, 'b');
|
||||
obj.SetString(tmp);
|
||||
obj.SetString(tmp);
|
||||
obj.Reset();
|
||||
cobj_.SetString(tmp);
|
||||
cobj_.SetString(tmp);
|
||||
cobj_.Reset();
|
||||
}
|
||||
|
||||
TEST_F(CompactObjectTest, AsciiUtil) {
|
||||
|
@ -98,4 +117,49 @@ TEST_F(CompactObjectTest, AsciiUtil) {
|
|||
ASSERT_EQ(data.substr(0, 7), actual);
|
||||
}
|
||||
|
||||
TEST_F(CompactObjectTest, IntSet) {
|
||||
robj* src = createIntsetObject();
|
||||
cobj_.ImportRObj(src);
|
||||
EXPECT_EQ(OBJ_SET, cobj_.ObjType());
|
||||
EXPECT_EQ(OBJ_ENCODING_INTSET, cobj_.Encoding());
|
||||
|
||||
robj* os = cobj_.AsRObj();
|
||||
EXPECT_EQ(0, setTypeSize(os));
|
||||
sds val1 = sdsnew("10");
|
||||
sds val2 = sdsdup(val1);
|
||||
|
||||
EXPECT_EQ(1, setTypeAdd(os, val1));
|
||||
EXPECT_EQ(0, setTypeAdd(os, val2));
|
||||
EXPECT_EQ(OBJ_ENCODING_INTSET, os->encoding);
|
||||
sdsfree(val1);
|
||||
sdsfree(val2);
|
||||
cobj_.SyncRObj();
|
||||
EXPECT_GT(cobj_.MallocUsed(), 0);
|
||||
}
|
||||
|
||||
TEST_F(CompactObjectTest, HSet) {
|
||||
robj* src = createHashObject();
|
||||
cobj_.ImportRObj(src);
|
||||
|
||||
EXPECT_EQ(OBJ_HASH, cobj_.ObjType());
|
||||
EXPECT_EQ(OBJ_ENCODING_LISTPACK, cobj_.Encoding());
|
||||
|
||||
robj* os = cobj_.AsRObj();
|
||||
|
||||
sds key1 = sdsnew("key1");
|
||||
sds val1 = sdsnew("val1");
|
||||
|
||||
|
||||
// returns 0 on insert.
|
||||
EXPECT_EQ(0, hashTypeSet(os, key1, val1, HASH_SET_TAKE_FIELD | HASH_SET_TAKE_VALUE));
|
||||
cobj_.SyncRObj();
|
||||
}
|
||||
|
||||
TEST_F(CompactObjectTest, ZSet) {
|
||||
// unrelated, checking sds static encoding used in zset special strings.
|
||||
char kMinStrData[] = "\110" "minstring";
|
||||
EXPECT_EQ(9, sdslen(kMinStrData + 1));
|
||||
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -72,7 +72,7 @@ template <typename V> class OpResult : public OpResultBase {
|
|||
}
|
||||
|
||||
private:
|
||||
V v_;
|
||||
V v_{};
|
||||
};
|
||||
|
||||
template <> class OpResult<void> : public OpResultBase {
|
||||
|
|
|
@ -10,7 +10,7 @@ endif()
|
|||
|
||||
add_library(redis_lib crc64.c crcspeed.c debug.c dict.c endianconv.c intset.c
|
||||
listpack.c mt19937-64.c object.c lzf_c.c lzf_d.c sds.c sha256.c
|
||||
quicklist.c redis_aux.c siphash.c t_set.c t_zset.c util.c ${ZMALLOC_SRC})
|
||||
quicklist.c redis_aux.c siphash.c t_hash.c t_set.c t_zset.c util.c ${ZMALLOC_SRC})
|
||||
|
||||
cxx_link(redis_lib ${ZMALLOC_DEPS})
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
* too strict. */
|
||||
static void dismissMemory(void* ptr, size_t size_hint) {
|
||||
}
|
||||
struct sharedObjectsStruct shared;
|
||||
|
||||
/* ===================== Creation and parsing of objects ==================== */
|
||||
|
||||
|
|
|
@ -121,7 +121,6 @@ static inline int sdsEncodedObject(const robj *o) {
|
|||
return o->encoding == OBJ_ENCODING_RAW || o->encoding == OBJ_ENCODING_EMBSTR;
|
||||
}
|
||||
|
||||
|
||||
/* Structure to hold set iteration abstraction. */
|
||||
typedef struct {
|
||||
robj *subject;
|
||||
|
@ -164,6 +163,35 @@ int setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele);
|
|||
sds setTypeNextObject(setTypeIterator *si);
|
||||
|
||||
|
||||
|
||||
/* hash set interface */
|
||||
|
||||
|
||||
/* Hash data type */
|
||||
#define HASH_SET_TAKE_FIELD (1<<0)
|
||||
#define HASH_SET_TAKE_VALUE (1<<1)
|
||||
#define HASH_SET_COPY 0
|
||||
|
||||
void hashTypeConvert(robj *o, int enc);
|
||||
void hashTypeTryConversion(robj *subject, robj **argv, int start, int end);
|
||||
int hashTypeExists(robj *o, sds key);
|
||||
int hashTypeDelete(robj *o, sds key);
|
||||
unsigned long hashTypeLength(const robj *o);
|
||||
hashTypeIterator *hashTypeInitIterator(robj *subject);
|
||||
void hashTypeReleaseIterator(hashTypeIterator *hi);
|
||||
int hashTypeNext(hashTypeIterator *hi);
|
||||
void hashTypeCurrentFromListpack(hashTypeIterator *hi, int what,
|
||||
unsigned char **vstr,
|
||||
unsigned int *vlen,
|
||||
long long *vll);
|
||||
sds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what);
|
||||
void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll);
|
||||
sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what);
|
||||
robj *hashTypeGetValueObject(robj *o, sds field);
|
||||
int hashTypeSet(robj *o, sds field, sds value, int flags);
|
||||
robj *hashTypeDup(robj *o);
|
||||
|
||||
|
||||
/* Macro used to initialize a Redis object allocated on the stack.
|
||||
* Note that this macro is taken near the structure definition to make sure
|
||||
* we'll update it when the structure is changed, to avoid bugs like
|
||||
|
@ -181,21 +209,5 @@ sds setTypeNextObject(setTypeIterator *si);
|
|||
#define PROTO_SHARED_SELECT_CMDS 10
|
||||
#define OBJ_SHARED_BULKHDR_LEN 32
|
||||
|
||||
struct sharedObjectsStruct {
|
||||
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,
|
||||
*colon, *queued, *null[4], *nullarray[4], *emptymap[4], *emptyset[4],
|
||||
*emptyarray, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
|
||||
*outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,
|
||||
*masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr,
|
||||
*busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
|
||||
*unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *unlink,
|
||||
*rpop, *lpop, *lpush, *rpoplpush, *lmove, *blmove, *zpopmin, *zpopmax,
|
||||
*emptyscan, *multi, *exec, *left, *right;
|
||||
sds minstring, maxstring;
|
||||
};
|
||||
|
||||
extern struct sharedObjectsStruct shared;
|
||||
|
||||
void initSharedStruct();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,6 +14,9 @@ void InitRedisTables() {
|
|||
server.page_size = sysconf(_SC_PAGESIZE);
|
||||
server.zset_max_listpack_entries = 128;
|
||||
server.zset_max_listpack_value = 64;
|
||||
server.set_max_intset_entries = 512;
|
||||
server.hash_max_listpack_entries = 512;
|
||||
server.hash_max_listpack_value = 64;
|
||||
}
|
||||
|
||||
// These functions are moved here from server.c
|
||||
|
@ -100,3 +103,14 @@ dictType zsetDictType = {
|
|||
NULL, /* val destructor */
|
||||
NULL /* allow to expand */
|
||||
};
|
||||
|
||||
/* Hash type hash table (note that small hashes are represented with listpacks) */
|
||||
dictType hashDictType = {
|
||||
dictSdsHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictSdsKeyCompare, /* key compare */
|
||||
dictSdsDestructor, /* key destructor */
|
||||
dictSdsDestructor, /* val destructor */
|
||||
NULL /* allow to expand */
|
||||
};
|
||||
|
|
|
@ -89,8 +89,8 @@ typedef struct ServerStub {
|
|||
|
||||
int rdb_save_incremental_fsync;
|
||||
size_t stat_peak_memory;
|
||||
size_t set_max_intset_entries, hash_max_ziplist_entries,
|
||||
hash_max_ziplist_value;
|
||||
size_t set_max_intset_entries, hash_max_listpack_entries,
|
||||
hash_max_listpack_value;
|
||||
size_t zset_max_listpack_entries;
|
||||
size_t zset_max_listpack_value;
|
||||
int sanitize_dump_payload; /* Enables deep sanitization for ziplist and listpack in RDB and RESTORE. */
|
||||
|
|
1171
src/redis/t_hash.c
Normal file
1171
src/redis/t_hash.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -72,6 +72,14 @@
|
|||
* Skiplist implementation of the low level API
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
// ROMAN: static representation of sds strings
|
||||
static char kMinStrData[] = "\110" "minstring";
|
||||
static char kMaxStrData[] = "\110" "minstring";
|
||||
|
||||
#define cminstring (kMinStrData + 1)
|
||||
#define cmaxstring (kMaxStrData + 1)
|
||||
|
||||
|
||||
int zslLexValueGteMin(sds value, const zlexrangespec *spec);
|
||||
int zslLexValueLteMax(sds value, const zlexrangespec *spec);
|
||||
|
||||
|
@ -586,12 +594,12 @@ int zslParseLexRangeItem(robj *item, sds *dest, int *ex) {
|
|||
case '+':
|
||||
if (c[1] != '\0') return C_ERR;
|
||||
*ex = 1;
|
||||
*dest = shared.maxstring;
|
||||
*dest = cmaxstring;
|
||||
return C_OK;
|
||||
case '-':
|
||||
if (c[1] != '\0') return C_ERR;
|
||||
*ex = 1;
|
||||
*dest = shared.minstring;
|
||||
*dest = cminstring;
|
||||
return C_OK;
|
||||
case '(':
|
||||
*ex = 1;
|
||||
|
@ -609,10 +617,10 @@ int zslParseLexRangeItem(robj *item, sds *dest, int *ex) {
|
|||
/* Free a lex range structure, must be called only after zslParseLexRange()
|
||||
* populated the structure with success (C_OK returned). */
|
||||
void zslFreeLexRange(zlexrangespec *spec) {
|
||||
if (spec->min != shared.minstring &&
|
||||
spec->min != shared.maxstring) sdsfree(spec->min);
|
||||
if (spec->max != shared.minstring &&
|
||||
spec->max != shared.maxstring) sdsfree(spec->max);
|
||||
if (spec->min != cminstring &&
|
||||
spec->min != cmaxstring) sdsfree(spec->min);
|
||||
if (spec->max != cminstring &&
|
||||
spec->max != cmaxstring) sdsfree(spec->max);
|
||||
}
|
||||
|
||||
/* Populate the lex rangespec according to the objects min and max.
|
||||
|
@ -641,8 +649,8 @@ int zslParseLexRange(robj *min, robj *max, zlexrangespec *spec) {
|
|||
* -inf and +inf for strings */
|
||||
int sdscmplex(sds a, sds b) {
|
||||
if (a == b) return 0;
|
||||
if (a == shared.minstring || b == shared.maxstring) return -1;
|
||||
if (a == shared.maxstring || b == shared.minstring) return 1;
|
||||
if (a == cminstring || b == cmaxstring) return -1;
|
||||
if (a == cmaxstring || b == cminstring) return 1;
|
||||
return sdscmp(a,b);
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,4 @@ int zslLexValueGteMin(sds value, const zlexrangespec* spec);
|
|||
int zslLexValueLteMax(sds value, const zlexrangespec* spec);
|
||||
int zsetZiplistValidateIntegrity(unsigned char* zl, size_t size, int deep);
|
||||
|
||||
extern size_t zset_max_ziplist_entries;
|
||||
extern size_t zset_max_ziplist_value;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue