1
0
Fork 0
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:
Roman Gershman 2022-03-01 12:35:02 +02:00
parent 8c95facb47
commit 7ffbadd305
12 changed files with 1329 additions and 53 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -72,7 +72,7 @@ template <typename V> class OpResult : public OpResultBase {
}
private:
V v_;
V v_{};
};
template <> class OpResult<void> : public OpResultBase {

View file

@ -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})

View file

@ -58,7 +58,6 @@
* too strict. */
static void dismissMemory(void* ptr, size_t size_hint) {
}
struct sharedObjectsStruct shared;
/* ===================== Creation and parsing of objects ==================== */

View file

@ -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

View file

@ -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 */
};

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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);
}

View file

@ -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