Last active
December 3, 2021 16:36
-
-
Save hadjiprocopis/32e1ac08aad5253d26b180fc613ea64b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <time.h> | |
/* delete keys matching a pattern, comments welcome | |
author: Andreas Hadjiprocopis (https://github.com/hadjiprocopis) / bliako | |
date : 30/11/2021 | |
*/ | |
/* cvector is a headers-only vector implementation | |
from https://github.com/eteran/c-vector | |
*/ | |
#include <cvector.h> | |
#include <hiredis/hiredis.h> | |
#define time_difference_seconds(_TSTARTED,_TENDED) (((_TENDED.tv_sec - _TSTARTED.tv_sec) * 1000000 + (_TENDED.tv_nsec - _TSTARTED.tv_nsec) / 1000.0)/1000000.0) | |
int redis_mdel( | |
redisContext *redish, | |
const char *pattern | |
){ | |
struct timespec t[20]; | |
size_t TI = 0; | |
clock_gettime(CLOCK_MONOTONIC_RAW, &(t[TI++])); | |
fprintf(stdout, "redis_mdel() : called to delete with this pattern '%s' ...\n", pattern); | |
redisReply *reply, *subreply, *keys_reply, *key_reply; | |
char cmd[100], *akey; | |
// create a cvector with an initial size (which will be re-sized if needed) | |
// we will store the keys as they come from SCAN | |
cvector_vector_type(char*) keys = NULL; | |
int cursor = 0; | |
size_t iter = 0; | |
do { | |
sprintf(cmd, "SCAN %d MATCH %s", cursor, pattern); | |
reply = redisCommand(redish, cmd); | |
if( reply == NULL ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed (NULL reply) for this command '%s' (scan loop was %zu).\n", cmd, iter); return 1; } | |
else if( reply->type == REDIS_REPLY_ERROR ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed for this command '%s' (scan loop was %zu): '%s'\n", cmd, iter, reply->str); return 1;} | |
else if( reply->type != REDIS_REPLY_ARRAY ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed ((1): did not receive an REDIS_REPLY_ARRAY but type:%d) for this command '%s' (scan loop was %zu): '%s'\n", reply->type, cmd, iter, reply->str); return 1;} | |
// we now have reply as an array of 2 items: cursor and array-of-some-or-all-results | |
cursor = reply->element[0]->integer; | |
//fprintf(stdout, "redis_mdel() : command '%s', scan loop %zu : got cursor of %d.\n", cmd, iter, cursor); | |
keys_reply = reply->element[1]; | |
if( keys_reply == NULL ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed (NULL reply) for this command '%s' (scan loop was %zu).\n", cmd, iter); return 1; } | |
else if( keys_reply->type == REDIS_REPLY_ERROR ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed for this command '%s' (scan loop was %zu): '%s'\n", cmd, iter, keys_reply->str); return 1;} | |
else if( keys_reply->type != REDIS_REPLY_ARRAY ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed ((2): did not receive an REDIS_REPLY_ARRAY but type:%d) for this command '%s' (scan loop was %zu): '%s'\n", keys_reply->type, cmd, iter, keys_reply->str); return 1;} | |
size_t num_keys_fetched_this_time = keys_reply->elements; | |
//fprintf(stdout, "redis_mdel() : command '%s', scan loop %zu : got cursor of %d and %zu items.\n", cmd, iter, cursor, num_keys_fetched_this_time); | |
for(size_t ix=0;ix<num_keys_fetched_this_time;ix++){ | |
key_reply = keys_reply->element[ix]; | |
if( key_reply == NULL ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed (NULL reply) for this command '%s' (scan loop was %zu, ix=%zu).\n", cmd, iter, ix); return 1; } | |
else if( key_reply->type == REDIS_REPLY_ERROR ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed for this command '%s' (scan loop was %zu): '%s'\n", cmd, iter, key_reply->str); return 1;} | |
else if( key_reply->type != REDIS_REPLY_STRING ){ fprintf(stderr, "redis_mdel() : error, call to redisCommand() has failed (did not receive an REDIS_REPLY_STRING but type:%d) for this command '%s' (scan loop was %zu): '%s'\n", key_reply->type, cmd, iter, key_reply->str); return 1;} | |
//fprintf(stdout, "redis_mdel() : command '%s', scan loop %zu : got key back: '%s'.\n", cmd, iter, key_reply->str); | |
cvector_push_back(keys, strdup(key_reply->str)); | |
} | |
// free reply will free all sub-elements | |
freeReplyObject(reply); | |
iter++; | |
} while( cursor > 0 ); | |
size_t num_keys = cvector_size(keys); | |
clock_gettime(CLOCK_MONOTONIC_RAW, &(t[TI++])); | |
fprintf(stdout, "redis_mdel() : found %zu keys to delete using pattern '%s' in %lf seconds ...\n", num_keys, pattern, time_difference_seconds(t[TI-2], t[TI-1])); | |
// now we have keys, so del but using a pipeline for more efficiency | |
for(size_t i=num_keys;i-->0;){ | |
akey = (char *)(keys[i]); | |
// WARNING: IT DOES NOT LIKE quotes around key-string single or double!!!! | |
// sprintf(cmd, "DEL '%s'", akey); | |
sprintf(cmd, "DEL %s", akey); | |
if( redisAppendCommand(redish, cmd) != REDIS_OK ){ fprintf(stderr, "redis_mdel() : error, call to redisAppendCommand() has failed for command '%s'.\n", cmd); return 1; } | |
free(akey); | |
} | |
clock_gettime(CLOCK_MONOTONIC_RAW, &(t[TI++])); | |
fprintf(stdout, "redis_mdel() : sent %zu DEL commands via pipeline to Redis in %lf seconds ...\n", num_keys, time_difference_seconds(t[TI-2], t[TI-1])); | |
for(size_t i=num_keys;i-->0;){ | |
if( redisGetReply(redish, (void **) &reply ) != REDIS_OK ){ fprintf(stderr, "redis_mdel() : error, call to redisGetReply() has failed for reply %zu of %zu.\n", i, num_keys); return 1; } | |
if( reply == NULL ){ fprintf(stderr, "redis_mdel() : error, reply from 'redisGetReply()' has failed (got NULL) for count %zu of %zu.\n", i, num_keys); return 1; } | |
else if( reply->type == REDIS_REPLY_ERROR ){ fprintf(stderr, "redis_mdel() : error, reply from 'redisGetReply()' has failed for count %zu of %zu.\n", i, num_keys); return 1; } | |
else if( reply->type != REDIS_REPLY_INTEGER ){ fprintf(stderr, "redis_mdel() : error, reply from 'redisGetReply()' has failed (did not receive REDIS_REPLY_INTEGER but type:%d) for count %zu of %zu.\n", reply->type, i, num_keys); return 1; } | |
else if( reply->integer != 0 ){ fprintf(stderr, "redis_mdel() : error, reply (integer) was not zero but %lld, for count %zu of %zu.\n", reply->integer, i, num_keys); return 1; } | |
freeReplyObject(reply); | |
} | |
clock_gettime(CLOCK_MONOTONIC_RAW, &(t[TI++])); | |
fprintf(stdout, "redis_mdel() : check the reply of %zu DEL commands via pipeline from Redis in %lf seconds ...\n", num_keys, time_difference_seconds(t[TI-2], t[TI-1])); | |
// epilogue | |
cvector_free(keys); | |
clock_gettime(CLOCK_MONOTONIC_RAW, &(t[TI++])); | |
fprintf(stdout, "redis_mdel() : free'd the vector of %zu keys in %lf seconds ...\n", num_keys, time_difference_seconds(t[TI-2], t[TI-1])); | |
fprintf(stdout, "redis_mdel() : done deleted %zu keys matching the pattern '%s' in a total of %lf seconds.\n", num_keys, pattern, time_difference_seconds(t[0], t[TI-1])); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I have just realised that enclosing key name in quotes (single or double) does not work and so converted
sprintf(cmd, "DEL '%s'", akey);
to this:sprintf(cmd, "DEL %s", akey);