summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/src/estore.c
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
commitdcacc00e3750300617ba6e16eb346713f91a783a (patch)
tree38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/src/estore.c
parent58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff)
downloadcrep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz
Remove testing data
Diffstat (limited to 'examples/redis-unstable/src/estore.c')
-rw-r--r--examples/redis-unstable/src/estore.c496
1 files changed, 0 insertions, 496 deletions
diff --git a/examples/redis-unstable/src/estore.c b/examples/redis-unstable/src/estore.c
deleted file mode 100644
index 6335bd5..0000000
--- a/examples/redis-unstable/src/estore.c
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * estore.c -- Expiration Store implementation
- *
- * Copyright (c) 2011-Present, Redis Ltd.
- * All rights reserved.
- *
- * Licensed under your choice of (a) the Redis Source Available License 2.0
- * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
- * GNU Affero General Public License v3 (AGPLv3).
- */
-
-#include "fmacros.h"
-#include "estore.h"
-#include "zmalloc.h"
-#include "redisassert.h"
-#include "server.h"
-#include <string.h>
-
-/* Forward declaration of the estore structure */
-struct _estore {
- int flags; /* Flags for configuration options */
- EbucketsType *bucket_type; /* Type of buckets used in this store */
- ebuckets *ebArray; /* Array of ebuckets (one per slot in cluster mode, or just one) */
- int num_buckets_bits; /* Log2 of the number of buckets */
- int num_buckets; /* Number of buckets (1 << num_buckets_bits) */
- unsigned long long count; /* Total number of items in this estore */
- fenwickTree *buckets_sizes; /* Binary indexed tree (BIT) that describes cumulative key frequencies */
-};
-
-/* Get the appropriate bucket for a given eidx */
-ebuckets *estoreGetBuckets(estore *es, int eidx) {
- debugAssert(eidx < es->num_buckets);
- return &(es->ebArray[eidx]);
-}
-
-/* Create a new expiration store
- * type - Pointer to a static EbucketsType defining the bucket behavior.
- * num_buckets_bits - The log2 of the number of buckets (0 for 1 bucket,
- * CLUSTER_SLOT_MASK_BITS for CLUSTER_SLOTS buckets)
- * flags - Configuration flags
- */
-estore *estoreCreate(EbucketsType *type, int num_buckets_bits) {
- /* We can't support more than 2^16 buckets to be consistent with kvstore */
- assert(num_buckets_bits <= 16);
-
- estore *es = zmalloc(sizeof(estore));
- /* Store the bucket type */
- es->bucket_type = type;
-
- /* Calculate number of buckets based on num_buckets_bits */
- es->num_buckets_bits = num_buckets_bits;
- es->num_buckets = 1 << num_buckets_bits;
- es->buckets_sizes = es->num_buckets > 1 ? fwTreeCreate(num_buckets_bits) : NULL;
-
- /* Allocate the buckets array */
- es->ebArray = zcalloc(sizeof(ebuckets) * es->num_buckets);
-
- /* Initialize all buckets */
- for (int i = 0; i < es->num_buckets; i++) {
- es->ebArray[i] = ebCreate();
- }
-
- es->count = 0;
- return es;
-}
-
-/* Empty an expiration store (clear all entries but keep the structure) */
-void estoreEmpty(estore *es) {
- if (es == NULL) return;
-
- for (int i = 0; i < es->num_buckets; i++) {
- ebDestroy(&es->ebArray[i], es->bucket_type, NULL);
- es->ebArray[i] = ebCreate();
- }
-
- if (es->buckets_sizes) fwTreeClear(es->buckets_sizes);
- es->count = 0;
-}
-
-/* Check if the expiration store is empty */
-int estoreIsEmpty(estore *es) {
- return es->count == 0;
-}
-
-/* Get the first non-empty bucket index in the estore */
-int estoreGetFirstNonEmptyBucket(estore *es) {
- if (es->num_buckets == 1 || estoreSize(es) == 0)
- return 0;
- return fwTreeFindFirstNonEmpty(es->buckets_sizes);
-}
-
-/* Get the next non-empty bucket index after the given index */
-int estoreGetNextNonEmptyBucket(estore *es, int eidx) {
- if (es->num_buckets == 1) {
- assert(eidx == 0);
- return -1;
- }
- return fwTreeFindNextNonEmpty(es->buckets_sizes, eidx);
-}
-
-/* Release an expiration store (free all memory) */
-void estoreRelease(estore *es) {
- if (es == NULL) return;
-
- for (int i = 0; i < es->num_buckets; i++) {
- if (es->ebArray[i])
- ebDestroy(&es->ebArray[i], es->bucket_type, NULL);
- }
- fwTreeDestroy(es->buckets_sizes);
- zfree(es->ebArray);
- zfree(es);
-}
-
-/* Perform active expiration on a specific bucket */
-void estoreActiveExpire(estore *es, int eidx, ExpireInfo *info) {
- ebuckets *eb = estoreGetBuckets(es, eidx);
- uint64_t before = ebGetTotalItems(*eb, es->bucket_type);
- ebExpire(eb, es->bucket_type, info);
- /* If items expired (or updated), update the BIT and estore count */
- if (info->itemsExpired) {
- uint64_t diff = before - ebGetTotalItems(*eb, es->bucket_type);
- fwTreeUpdate(es->buckets_sizes, eidx, (long long) diff);
- es->count -= diff;
- }
-}
-
-/* Add item to estore with the given expiration time. The item must has
- * expireMeta already allocated. */
-void estoreAdd(estore *es, int eidx, eItem item, uint64_t when) {
- debugAssert(es != NULL && item != NULL);
-
- /* currently only used by hash field expiration. Verify it has expireMeta */
- debugAssert((((robj *)item)->encoding == OBJ_ENCODING_LISTPACK_EX) ||
- ((((robj *)item)->encoding == OBJ_ENCODING_HT) &&
- ((dict *) ((robj *)item)->ptr)->type == &entryHashDictTypeWithHFE));
-
- ebuckets *bucket = estoreGetBuckets(es, eidx);
- if (ebAdd(bucket, es->bucket_type, item, when) == 0) {
- es->count++;
- fwTreeUpdate(es->buckets_sizes, eidx, 1);
- }
-}
-
-/* Remove an item from the expiration store. Returns the expire time or EB_EXPIRE_TIME_INVALID */
-uint64_t estoreRemove(estore *es, int eidx, eItem item) {
- uint64_t expireTime;
- debugAssert(es != NULL && item != NULL);
-
- /* Currently only used by hash field expiration. gracefully ignore otherwise */
- kvobj *kv = (kvobj *) item;
- if ( (kv->type != OBJ_HASH) ||
- (kv->encoding == OBJ_ENCODING_LISTPACK) ||
- ((kv->encoding == OBJ_ENCODING_HT) && (((dict *)kv->ptr)->type != &entryHashDictTypeWithHFE)))
- return EB_EXPIRE_TIME_INVALID;
-
- /* If (ExpireMeta of kv) marked as trash, then it is already removed */
- if ((expireTime = ebGetExpireTime(es->bucket_type, item)) == EB_EXPIRE_TIME_INVALID)
- return EB_EXPIRE_TIME_INVALID;
-
- ebuckets *bucket = estoreGetBuckets(es, eidx);
- serverAssert(ebRemove(bucket, es->bucket_type, item)==1);
- es->count--;
- fwTreeUpdate(es->buckets_sizes, eidx, -1);
-
- return expireTime;
-}
-
-/* Update an item's expiration time in the store */
-void estoreUpdate(estore *es, int eidx, eItem item, uint64_t when) {
- debugAssert(es != NULL && item != NULL);
-
- /* currently only used by hash field expiration. Verify it has expireMeta */
- debugAssert((((robj *)item)->encoding == OBJ_ENCODING_LISTPACK_EX) ||
- ((((robj *)item)->encoding == OBJ_ENCODING_HT) &&
- ((dict *) ((robj *)item)->ptr)->type == &entryHashDictTypeWithHFE));
-
- debugAssert(ebGetExpireTime(es->bucket_type, item) != EB_EXPIRE_TIME_INVALID);
-
- ebuckets *bucket = estoreGetBuckets(es, eidx);
-
- /* Remove the item from its current position */
- serverAssert(ebRemove(bucket, es->bucket_type, item) != 0);
-
- /* Add the item back with the new expiration time */
- serverAssert(ebAdd(bucket, es->bucket_type, item, when) == 0);
-
- /* Note that estore count remain unchanged */
-}
-
-/* Get the total number of items in the expiration store */
-uint64_t estoreSize(estore *es) {
- return es->count;
-}
-
-/* Move ebuckets from one estore to another */
-void estoreMoveEbuckets(estore *src, estore *dst, int eidx) {
- serverAssert(src->num_buckets > eidx);
- serverAssert(src->num_buckets == dst->num_buckets);
- serverAssert(ebIsEmpty(dst->ebArray[eidx])); /* If it is NULL */
-
- /* Adjust source estore */
- ebuckets eb = src->ebArray[eidx];
- if (ebIsEmpty(eb)) return;
- int64_t count = (int64_t)ebGetTotalItems(eb, src->bucket_type);
- src->count -= count;
- fwTreeUpdate(src->buckets_sizes, eidx, -count);
- src->ebArray[eidx] = ebCreate(); /* Set to NULL actually.*/
-
- /* Move ebuckets to destination estore */
- dst->ebArray[eidx] = eb;
- dst->count += count;
- fwTreeUpdate(dst->buckets_sizes, eidx, count);
-}
-
-#ifdef REDIS_TEST
-#include <stdio.h>
-#include "testhelp.h"
-
-#define TEST(name) printf("test — %s\n", name);
-
-/* Test item structure for estore testing */
-typedef struct TestItem {
- kvobj kv; /* mimic kvobj of type HASH to pass checks in estore */
- ExpireMeta mexpire;
- int index;
-} TestItem;
-
-/* Test EbucketsType for estore testing */
-ExpireMeta *getTestItemExpireMeta(const eItem item) {
- return &((TestItem *)item)->mexpire;
-}
-
-void deleteTestItemCb(eItem item, void *ctx) {
- UNUSED(ctx);
- zfree(item);
-}
-
-EbucketsType testEbucketsType = {
- .getExpireMeta = getTestItemExpireMeta,
- .onDeleteItem = deleteTestItemCb,
- .itemsAddrAreOdd = 0,
-};
-
-/* Helper function to create a test item */
-static TestItem *createTestItem(int index) {
- TestItem *item = zmalloc(sizeof(TestItem));
- item->index = index;
- item->mexpire.trash = 1;
- /* mimic kvobj of type HASH to pass checks in estore */
- item->kv.type = OBJ_HASH;
- item->kv.encoding = OBJ_ENCODING_LISTPACK_EX;
- return item;
-}
-
-static ExpireAction activeExpireTestCb(eItem item, void *ctx) {
- UNUSED(ctx);
- zfree(item);
- return 0;
-}
-
-int estoreTest(int argc, char **argv, int flags) {
- UNUSED(argc);
- UNUSED(argv);
- UNUSED(flags);
-
- /* Initialize minimal server state needed for testing */
- server.hz = 10;
- server.unixtime = time(NULL);
-
- TEST("Create and destroy estore") {
- estore *es = estoreCreate(&testEbucketsType, 0);
- assert(es != NULL);
- assert(estoreIsEmpty(es));
- assert(estoreSize(es) == 0);
- estoreRelease(es);
- }
-
- TEST("Create estore with multiple buckets") {
- estore *es = estoreCreate(&testEbucketsType, 2); /* 4 buckets */
- assert(es != NULL);
- assert(estoreIsEmpty(es));
- assert(estoreSize(es) == 0);
-
- /* Test bucket access */
- ebuckets *bucket0 = estoreGetBuckets(es, 0);
- ebuckets *bucket1 = estoreGetBuckets(es, 1);
- ebuckets *bucket2 = estoreGetBuckets(es, 2);
- ebuckets *bucket3 = estoreGetBuckets(es, 3);
-
- assert(bucket0 != NULL);
- assert(bucket1 != NULL);
- assert(bucket2 != NULL);
- assert(bucket3 != NULL);
-
- /* All buckets should be different */
- assert(bucket0 != bucket1);
- assert(bucket0 != bucket2);
- assert(bucket1 != bucket3);
-
- estoreRelease(es);
- }
-
- TEST("Add and remove items") {
- estore *es = estoreCreate(&testEbucketsType, 1); /* 2 buckets */
-
- /* Test initial state */
- assert(estoreSize(es) == 0);
- assert(estoreIsEmpty(es));
-
- /* Create test items */
- TestItem *item1 = createTestItem(1);
- TestItem *item2 = createTestItem(2);
- TestItem *item3 = createTestItem(3);
-
- /* Add items to different buckets */
- estoreAdd(es, 0, item1, 1000);
- assert(estoreSize(es) == 1);
- assert(!estoreIsEmpty(es));
-
- estoreAdd(es, 1, item2, 2000);
- assert(estoreSize(es) == 2);
-
- estoreAdd(es, 0, item3, 3000); /* Add another item to bucket 0 */
- assert(estoreSize(es) == 3);
-
- /* Verify expiration times are set correctly */
- assert(ebGetMetaExpTime(&item1->mexpire) == 1000);
- assert(ebGetMetaExpTime(&item2->mexpire) == 2000);
- assert(ebGetMetaExpTime(&item3->mexpire) == 3000);
-
- /* Remove items */
- uint64_t expireTime1 = estoreRemove(es, 0, item1);
- assert(expireTime1 == 1000);
- assert(estoreSize(es) == 2);
- zfree(item1);
-
- uint64_t expireTime2 = estoreRemove(es, 1, item2);
- assert(expireTime2 == 2000);
- assert(estoreSize(es) == 1);
- zfree(item2);
-
- uint64_t expireTime3 = estoreRemove(es, 0, item3);
- assert(expireTime3 == 3000);
- assert(estoreSize(es) == 0);
- assert(estoreIsEmpty(es));
- zfree(item3);
-
- /* Clean up - items are freed by the onDeleteItem callback */
- estoreRelease(es);
- }
-
- TEST("Update item expiration") {
- estore *es = estoreCreate(&testEbucketsType, 0); /* 1 bucket */
-
- /* Create and add a test item */
- TestItem *item = createTestItem(1);
- estoreAdd(es, 0, item, 1000);
- assert(estoreSize(es) == 1);
-
- /* Verify initial expiration time */
- assert(ebGetMetaExpTime(&item->mexpire) == 1000);
-
- /* Update expiration time */
- estoreUpdate(es, 0, item, 2000);
- assert(estoreSize(es) == 1); /* Size should remain the same */
- assert(ebGetMetaExpTime(&item->mexpire) == 2000);
-
- /* Update again to a different time */
- estoreUpdate(es, 0, item, 500);
- assert(estoreSize(es) == 1);
- assert(ebGetMetaExpTime(&item->mexpire) == 500);
-
- /* Clean up */
- estoreRemove(es, 0, item);
- assert(estoreSize(es) == 0);
- zfree(item);
-
- estoreRelease(es);
- }
-
- TEST("Non-empty bucket iteration") {
- estore *es = estoreCreate(&testEbucketsType, 2); /* 4 buckets */
-
- /* Test bucket iteration on empty store */
- assert(estoreGetFirstNonEmptyBucket(es) == 0); /* Returns 0 when empty */
- assert(estoreGetNextNonEmptyBucket(es, 0) == -1); /* No next bucket when empty */
-
- /* Create test items and add to specific buckets */
- TestItem *item1 = createTestItem(1);
- TestItem *item2 = createTestItem(2);
- TestItem *item3 = createTestItem(3);
-
- /* Add to bucket 1 and 3 (skip 0 and 2) */
- estoreAdd(es, 1, item1, 1000);
- estoreAdd(es, 3, item2, 2000);
- estoreAdd(es, 3, item3, 3000); /* Add another item to bucket 3 */
-
- assert(estoreSize(es) == 3);
-
- /* Test iteration through non-empty buckets */
- int firstBucket = estoreGetFirstNonEmptyBucket(es);
- assert(firstBucket == 1);
-
- int nextBucket = estoreGetNextNonEmptyBucket(es, firstBucket);
- assert(nextBucket == 3);
-
- int lastBucket = estoreGetNextNonEmptyBucket(es, nextBucket);
- assert(lastBucket == -1); /* No more non-empty buckets */
-
- /* Test iteration from different starting points */
- assert(estoreGetNextNonEmptyBucket(es, 0) == 1);
- assert(estoreGetNextNonEmptyBucket(es, 2) == 3);
-
- /* Clean up */
- estoreRemove(es, 1, item1);
- zfree(item1);
- estoreRemove(es, 3, item2);
- zfree(item2);
- estoreRemove(es, 3, item3);
- zfree(item3);
- assert(estoreSize(es) == 0);
-
- estoreRelease(es);
- }
-
- TEST("Empty estore") {
- estore *es = estoreCreate(&testEbucketsType, 1); /* 2 buckets */
-
- /* Add some items */
- TestItem *item1 = createTestItem(1);
- TestItem *item2 = createTestItem(2);
- TestItem *item3 = createTestItem(3);
-
- estoreAdd(es, 0, item1, 1000);
- estoreAdd(es, 1, item2, 2000);
- estoreAdd(es, 0, item3, 3000);
- assert(estoreSize(es) == 3);
- assert(!estoreIsEmpty(es));
-
- /* Empty the store - this should call onDeleteItem for all items */
- estoreEmpty(es);
- assert(estoreSize(es) == 0);
- assert(estoreIsEmpty(es));
-
- /* Verify buckets are empty */
- assert(estoreGetFirstNonEmptyBucket(es) == 0); /* Returns 0 when empty */
- assert(estoreGetNextNonEmptyBucket(es, 0) == -1);
-
- estoreRelease(es);
- }
-
- TEST("Active expiration") {
- estore *es = estoreCreate(&testEbucketsType, 14); /* 2^14 buckets */
-
- /* Create items with different expiration times */
- TestItem *expiredItem1 = createTestItem(1);
- TestItem *expiredItem2 = createTestItem(2);
- TestItem *expiredItem3 = createTestItem(3);
- TestItem *futureItem = createTestItem(4);
-
- estoreAdd(es, 0, expiredItem1, 1023);
- estoreAdd(es, 0, expiredItem2, 2047);
- estoreAdd(es, 1, expiredItem3, 127);
- estoreAdd(es, 0, futureItem, 4095);
- assert(estoreSize(es) == 4);
-
- /* Perform active expiration */
- ExpireInfo expireInfo = {
- .maxToExpire = UINT64_MAX,
- .onExpireItem = activeExpireTestCb,
- .ctx = NULL,
- .now = 2048, /* Current time in milliseconds */
- .itemsExpired = 0
- };
-
- estoreActiveExpire(es, 0, &expireInfo);
-
- /* The expired items should be removed, future item should remain */
- assert(expireInfo.itemsExpired == 2);
- assert(estoreSize(es) == 2);
-
- estoreActiveExpire(es, 1, &expireInfo);
- assert(expireInfo.itemsExpired == 1);
- assert(estoreSize(es) == 1);
-
- /* Clean up remaining item */
- estoreRemove(es, 0, futureItem);
- zfree(futureItem);
- assert(estoreSize(es) == 0);
-
- estoreRelease(es);
- }
-
- return 0;
-}
-#endif