├── pytest ├── dump.rdb └── test.py ├── .gitignore ├── Makefile ├── LICENSE ├── stats.h ├── snowflake.h ├── README.md ├── module.c ├── snowflake.c └── redismodule.h /pytest/dump.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erans/redissnowflake/HEAD/pytest/dump.rdb -------------------------------------------------------------------------------- /pytest/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from rmtest import ModuleTestCase 3 | import os 4 | 5 | module_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../redissnowflakemodule.so') 6 | 7 | class SnowflakeTestCase(ModuleTestCase(module_path, module_args=('1','1'))): 8 | 9 | def testCmd(self): 10 | with self.redis() as r: 11 | self.assertIsNotNone(r.execute_command('snowflake.getid')) 12 | 13 | if __name__ == '__main__': 14 | unittest.main() 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | .direnv 54 | .envrc 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEBUGFLAGS = -g -ggdb -O2 2 | ifeq ($(DEBUG), 1) 3 | DEBUGFLAGS = -g -ggdb -O0 4 | endif 5 | 6 | # find the OS 7 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 8 | CFLAGS = -Wall -Wno-unused-function $(DEBUGFLAGS) -fPIC -std=gnu99 -D_GNU_SOURCE 9 | CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc') 10 | 11 | # Compile flags for linux / osx 12 | ifeq ($(uname_S),Linux) 13 | SHOBJ_CFLAGS ?= -fno-common -g -ggdb 14 | SHOBJ_LDFLAGS ?= -shared -Bsymbolic -Bsymbolic-functions 15 | else 16 | CFLAGS += -mmacosx-version-min=10.6 17 | SHOBJ_CFLAGS ?= -dynamic -fno-common -g -ggdb 18 | SHOBJ_LDFLAGS ?= -dylib -exported_symbol _RedisModule_OnLoad -macosx_version_min 10.6 19 | endif 20 | 21 | SOURCEDIR=$(shell pwd -P) 22 | CC_SOURCES = $(wildcard $(SOURCEDIR)/*.c) $(wildcard $(SOURCEDIR)/dep/*.c) 23 | CC_OBJECTS = $(patsubst $(SOURCEDIR)/%.c, $(SOURCEDIR)/%.o, $(CC_SOURCES)) 24 | 25 | all: redissnowflakemodule.so 26 | 27 | redissnowflakemodule.so: $(CC_OBJECTS) 28 | $(LD) -o $@ $(CC_OBJECTS) $(SHOBJ_LDFLAGS) -lc 29 | 30 | clean: 31 | rm -rvf *.xo *.so *.o *.a 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Eran Sandler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /stats.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dwayn Matthies 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __SNOWFLAKED_STATS__ 24 | #define __SNOWFLAKED_STATS__ 25 | 26 | #include 27 | 28 | 29 | struct _app_stats { 30 | time_t started_at; 31 | char *version; 32 | long int ids; 33 | long int waits; 34 | long int seq_max; 35 | int region_id; 36 | int worker_id; 37 | long int seq_cap; 38 | } app_stats; 39 | 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /snowflake.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dwayn Matthies 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __SNOWFLAKE__ 24 | #define __SNOWFLAKE__ 25 | 26 | // the timestamp in milliseconds of the start of the custom epoch 27 | #define SNOWFLAKE_EPOCH 1388534400000 //Midnight January 1, 2014 28 | 29 | #define SNOWFLAKE_TIME_BITS 41 30 | #define SNOWFLAKE_REGIONID_BITS 4 31 | #define SNOWFLAKE_WORKERID_BITS 10 32 | #define SNOWFLAKE_SEQUENCE_BITS 8 33 | 34 | struct _snowflake_state { 35 | // milliseconds since SNOWFLAKE_EPOCH 36 | long int time; 37 | long int seq_max; 38 | long int worker_id; 39 | long int region_id; 40 | long int seq; 41 | long int time_shift_bits; 42 | long int region_shift_bits; 43 | long int worker_shift_bits; 44 | } snowflake_global_state; 45 | 46 | long int snowflake_id(); 47 | int snowflake_init(int region_id, int worker_id); 48 | 49 | #endif /* __SNOWFLAKE__ */ 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redis Snowflake 2 | 3 | Written by Eran Sandler ([@erans](https://twitter.com/erans)) 4 | 5 | This is a simple Redis module which generates unique IDs based on [Twitter's Snowflake](https://github.com/twitter/snowflake/tree/snowflake-2010) at high scale with some simple guarantees. 6 | 7 | This Redis module wraps code from [snowflaked](https://github.com/dwayn/snowflaked) made by Dwayn Matthies ([dwayn](https://github.com/dwayn)). Thanks for the code Dwayn! 8 | 9 | Some of Snowflake's benefits include: 10 | 11 | * **Uncoordinated** - For high availability within and across data centers, machines generating IDs should not have to coordinate with each other. 12 | 13 | * **(Roughly) Time Ordered** - it can guarantee that the ID numbers will be k-sorted (references: http://portal.acm.org/citation.cfm?id=70413.70419 and http://portal.acm.org/citation.cfm?id=110778.110783) within a reasonable bound (1 second is currently promised). 14 | 15 | * **Directly Sortable** - The IDs are sortable without loading the full objects that they represent, using the above order. 16 | 17 | * **Compact** - There are many otherwise reasonable solutions to this problem that require 128 bit numbers. For various reasons, we need to keep our IDs under 64 bits. 18 | 19 | ### ID Composition 20 | * time - 41 bits (millisecond precision w/ a custom epoch gives us 69 years) 21 | * region_id - 4 bits - gives us 16 different region or data centers to work with 22 | * worker_id - 10 bits - gives us up to 1024 workers per region_id 23 | * sequence_id - 8 bits - gives us up to 256 IDs per worker in the same millisecond 24 | 25 | ## Compilation 26 | 27 | Just `make` it. 28 | 29 | ## Usage 30 | 31 | `redis-server --loadmodule redissnowflake.so [region_id] [worker_id]` 32 | 33 | Example: 34 | 35 | `redis-server --loadmodule redissnowflake.so 1 1` 36 | 37 | If you require multiple such server/services in the same region, change the worker_id value. 38 | 39 | If you require multiple such server/services in different regions, change the region_id value. 40 | -------------------------------------------------------------------------------- /module.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Eran Sandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include 23 | #include "snowflake.h" 24 | 25 | #include "redismodule.h" 26 | 27 | int GetIdCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 28 | RedisModule_ReplyWithLongLong(ctx, snowflake_id()); 29 | return REDISMODULE_OK; 30 | } 31 | 32 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 33 | if (RedisModule_Init(ctx, "snowflake", 1, REDISMODULE_APIVER_1) == 34 | REDISMODULE_ERR) { 35 | return REDISMODULE_ERR; 36 | } 37 | 38 | if (argc != 2) { 39 | RedisModule_Log(ctx, "error", "Missing region_id and worker_id parameters"); 40 | return REDISMODULE_ERR; 41 | } 42 | 43 | long long region_id; 44 | if (RedisModule_StringToLongLong(argv[0], ®ion_id) == REDISMODULE_ERR) { 45 | RedisModule_Log(ctx, "error", "Invalid region_id value"); 46 | return REDISMODULE_ERR; 47 | } 48 | 49 | long long worker_id; 50 | if (RedisModule_StringToLongLong(argv[1], &worker_id) == REDISMODULE_ERR) { 51 | RedisModule_Log(ctx, "error", "Invalid worker_id value"); 52 | return REDISMODULE_ERR; 53 | } 54 | 55 | RedisModule_Log(ctx, "info", "using region_id: %u worker_id: %u", region_id, worker_id); 56 | 57 | if (snowflake_init(region_id, worker_id) != 1) { 58 | RedisModule_Log(ctx, "error", "Failed to initialize snowflake"); 59 | return REDISMODULE_ERR; 60 | } 61 | 62 | if (RedisModule_CreateCommand(ctx, "snowflake.getid", GetIdCommand, "readonly", 63 | 1, 1, 1) == REDISMODULE_ERR) { 64 | return REDISMODULE_ERR; 65 | } 66 | 67 | return REDISMODULE_OK; 68 | } 69 | -------------------------------------------------------------------------------- /snowflake.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dwayn Matthies 3 | 4 | Minor adjustments by Eran Sandler 5 | to be used in redissnowflake project 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | */ 25 | 26 | #include "stats.h" 27 | #include "snowflake.h" 28 | #include 29 | #include 30 | 31 | long int snowflake_id() { 32 | struct timeval tp; 33 | gettimeofday(&tp, NULL); 34 | long int millisecs = tp.tv_sec * 1000 + tp.tv_usec / 1000 - SNOWFLAKE_EPOCH; 35 | long int id = 0L; 36 | 37 | // Catch NTP clock adjustment that rolls time backwards and sequence number overflow 38 | if ((snowflake_global_state.seq > snowflake_global_state.seq_max ) || snowflake_global_state.time > millisecs) { 39 | ++app_stats.waits; 40 | while (snowflake_global_state.time >= millisecs) { 41 | gettimeofday(&tp, NULL); 42 | millisecs = tp.tv_sec * 1000 + tp.tv_usec / 1000 - SNOWFLAKE_EPOCH; 43 | } 44 | } 45 | 46 | if (snowflake_global_state.time < millisecs) { 47 | snowflake_global_state.time = millisecs; 48 | snowflake_global_state.seq = 0L; 49 | } 50 | 51 | 52 | id = (millisecs << snowflake_global_state.time_shift_bits) 53 | | (snowflake_global_state.region_id << snowflake_global_state.region_shift_bits) 54 | | (snowflake_global_state.worker_id << snowflake_global_state.worker_shift_bits) 55 | | (snowflake_global_state.seq++); 56 | 57 | if (app_stats.seq_max < snowflake_global_state.seq) 58 | app_stats.seq_max = snowflake_global_state.seq; 59 | 60 | ++app_stats.ids; 61 | return id; 62 | } 63 | 64 | int snowflake_init(int region_id, int worker_id) { 65 | int max_region_id = (1 << SNOWFLAKE_REGIONID_BITS) - 1; 66 | if(region_id < 0 || region_id > max_region_id){ 67 | //printf("Region ID must be in the range : 0-%d\n", max_region_id); 68 | return -1; 69 | } 70 | int max_worker_id = (1 << SNOWFLAKE_WORKERID_BITS) - 1; 71 | if(worker_id < 0 || worker_id > max_worker_id){ 72 | //printf("Worker ID must be in the range: 0-%d\n", max_worker_id); 73 | return -1; 74 | } 75 | 76 | snowflake_global_state.time_shift_bits = SNOWFLAKE_REGIONID_BITS + SNOWFLAKE_WORKERID_BITS + SNOWFLAKE_SEQUENCE_BITS; 77 | snowflake_global_state.region_shift_bits = SNOWFLAKE_WORKERID_BITS + SNOWFLAKE_SEQUENCE_BITS; 78 | snowflake_global_state.worker_shift_bits = SNOWFLAKE_SEQUENCE_BITS; 79 | 80 | snowflake_global_state.worker_id = worker_id; 81 | snowflake_global_state.region_id = region_id; 82 | snowflake_global_state.seq_max = (1L << SNOWFLAKE_SEQUENCE_BITS) - 1; 83 | snowflake_global_state.seq = 0L; 84 | snowflake_global_state.time = 0L; 85 | app_stats.seq_cap = snowflake_global_state.seq_max; 86 | app_stats.waits = 0L; 87 | app_stats.seq_max = 0L; 88 | app_stats.ids = 0L; 89 | app_stats.region_id = region_id; 90 | app_stats.worker_id = worker_id; 91 | return 1; 92 | } 93 | -------------------------------------------------------------------------------- /redismodule.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISMODULE_H 2 | #define REDISMODULE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* ---------------- Defines common between core and modules --------------- */ 9 | 10 | /* Error status return values. */ 11 | #define REDISMODULE_OK 0 12 | #define REDISMODULE_ERR 1 13 | 14 | /* API versions. */ 15 | #define REDISMODULE_APIVER_1 1 16 | 17 | /* API flags and constants */ 18 | #define REDISMODULE_READ (1<<0) 19 | #define REDISMODULE_WRITE (1<<1) 20 | 21 | #define REDISMODULE_LIST_HEAD 0 22 | #define REDISMODULE_LIST_TAIL 1 23 | 24 | /* Key types. */ 25 | #define REDISMODULE_KEYTYPE_EMPTY 0 26 | #define REDISMODULE_KEYTYPE_STRING 1 27 | #define REDISMODULE_KEYTYPE_LIST 2 28 | #define REDISMODULE_KEYTYPE_HASH 3 29 | #define REDISMODULE_KEYTYPE_SET 4 30 | #define REDISMODULE_KEYTYPE_ZSET 5 31 | #define REDISMODULE_KEYTYPE_MODULE 6 32 | 33 | /* Reply types. */ 34 | #define REDISMODULE_REPLY_UNKNOWN -1 35 | #define REDISMODULE_REPLY_STRING 0 36 | #define REDISMODULE_REPLY_ERROR 1 37 | #define REDISMODULE_REPLY_INTEGER 2 38 | #define REDISMODULE_REPLY_ARRAY 3 39 | #define REDISMODULE_REPLY_NULL 4 40 | 41 | /* Postponed array length. */ 42 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 43 | 44 | /* Expire */ 45 | #define REDISMODULE_NO_EXPIRE -1 46 | 47 | /* Sorted set API flags. */ 48 | #define REDISMODULE_ZADD_XX (1<<0) 49 | #define REDISMODULE_ZADD_NX (1<<1) 50 | #define REDISMODULE_ZADD_ADDED (1<<2) 51 | #define REDISMODULE_ZADD_UPDATED (1<<3) 52 | #define REDISMODULE_ZADD_NOP (1<<4) 53 | 54 | /* Hash API flags. */ 55 | #define REDISMODULE_HASH_NONE 0 56 | #define REDISMODULE_HASH_NX (1<<0) 57 | #define REDISMODULE_HASH_XX (1<<1) 58 | #define REDISMODULE_HASH_CFIELDS (1<<2) 59 | #define REDISMODULE_HASH_EXISTS (1<<3) 60 | 61 | /* A special pointer that we can use between the core and the module to signal 62 | * field deletion, and that is impossible to be a valid pointer. */ 63 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 64 | 65 | /* Error messages. */ 66 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 67 | 68 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 69 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 70 | 71 | #define REDISMODULE_NOT_USED(V) ((void) V) 72 | 73 | /* ------------------------- End of common defines ------------------------ */ 74 | 75 | #ifndef REDISMODULE_CORE 76 | 77 | typedef long long mstime_t; 78 | 79 | /* Incomplete structures for compiler checks but opaque access. */ 80 | typedef struct RedisModuleCtx RedisModuleCtx; 81 | typedef struct RedisModuleKey RedisModuleKey; 82 | typedef struct RedisModuleString RedisModuleString; 83 | typedef struct RedisModuleCallReply RedisModuleCallReply; 84 | typedef struct RedisModuleIO RedisModuleIO; 85 | typedef struct RedisModuleType RedisModuleType; 86 | typedef struct RedisModuleDigest RedisModuleDigest; 87 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 88 | 89 | typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 90 | 91 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 92 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 93 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 94 | typedef size_t (*RedisModuleTypeMemUsageFunc)(void *value); 95 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 96 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 97 | 98 | #define REDISMODULE_TYPE_METHOD_VERSION 1 99 | typedef struct RedisModuleTypeMethods { 100 | uint64_t version; 101 | RedisModuleTypeLoadFunc rdb_load; 102 | RedisModuleTypeSaveFunc rdb_save; 103 | RedisModuleTypeRewriteFunc aof_rewrite; 104 | RedisModuleTypeMemUsageFunc mem_usage; 105 | RedisModuleTypeDigestFunc digest; 106 | RedisModuleTypeFreeFunc free; 107 | } RedisModuleTypeMethods; 108 | 109 | #define REDISMODULE_GET_API(name) \ 110 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) 111 | 112 | #define REDISMODULE_API_FUNC(x) (*x) 113 | 114 | 115 | void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes); 116 | void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes); 117 | void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr); 118 | void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size); 119 | char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str); 120 | int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); 121 | int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); 122 | int REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); 123 | int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); 124 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); 125 | int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); 126 | int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); 127 | void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); 128 | void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); 129 | int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); 130 | size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); 131 | int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); 132 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); 133 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 134 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); 135 | void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); 136 | int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); 137 | long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); 138 | size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); 139 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); 140 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); 141 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); 142 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); 143 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); 144 | void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); 145 | const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len); 146 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); 147 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); 148 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); 149 | void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); 150 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); 151 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); 152 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); 153 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); 154 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); 155 | int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll); 156 | int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d); 157 | void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); 158 | int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 159 | int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); 160 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); 161 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); 162 | int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); 163 | int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); 164 | char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); 165 | int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); 166 | mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); 167 | int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); 168 | int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); 169 | int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); 170 | int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); 171 | int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); 172 | void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); 173 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 174 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 175 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 176 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 177 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); 178 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); 179 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); 180 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); 181 | int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); 182 | int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); 183 | int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); 184 | void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); 185 | unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); 186 | void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes); 187 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods); 188 | int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value); 189 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key); 190 | void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key); 191 | void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value); 192 | uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io); 193 | void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value); 194 | int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io); 195 | void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 196 | void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s); 197 | void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len); 198 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io); 199 | char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr); 200 | void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value); 201 | double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io); 202 | void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value); 203 | float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io); 204 | void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...); 205 | void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...); 206 | int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len); 207 | void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str); 208 | int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b); 209 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io); 210 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms); 211 | int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata); 212 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx); 213 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx); 214 | void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx); 215 | int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc); 216 | long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void); 217 | 218 | /* This is included inline inside each Redis module. */ 219 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); 220 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 221 | void *getapifuncptr = ((void**)ctx)[0]; 222 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; 223 | REDISMODULE_GET_API(Alloc); 224 | REDISMODULE_GET_API(Calloc); 225 | REDISMODULE_GET_API(Free); 226 | REDISMODULE_GET_API(Realloc); 227 | REDISMODULE_GET_API(Strdup); 228 | REDISMODULE_GET_API(CreateCommand); 229 | REDISMODULE_GET_API(SetModuleAttribs); 230 | REDISMODULE_GET_API(WrongArity); 231 | REDISMODULE_GET_API(ReplyWithLongLong); 232 | REDISMODULE_GET_API(ReplyWithError); 233 | REDISMODULE_GET_API(ReplyWithSimpleString); 234 | REDISMODULE_GET_API(ReplyWithArray); 235 | REDISMODULE_GET_API(ReplySetArrayLength); 236 | REDISMODULE_GET_API(ReplyWithStringBuffer); 237 | REDISMODULE_GET_API(ReplyWithString); 238 | REDISMODULE_GET_API(ReplyWithNull); 239 | REDISMODULE_GET_API(ReplyWithCallReply); 240 | REDISMODULE_GET_API(ReplyWithDouble); 241 | REDISMODULE_GET_API(ReplySetArrayLength); 242 | REDISMODULE_GET_API(GetSelectedDb); 243 | REDISMODULE_GET_API(SelectDb); 244 | REDISMODULE_GET_API(OpenKey); 245 | REDISMODULE_GET_API(CloseKey); 246 | REDISMODULE_GET_API(KeyType); 247 | REDISMODULE_GET_API(ValueLength); 248 | REDISMODULE_GET_API(ListPush); 249 | REDISMODULE_GET_API(ListPop); 250 | REDISMODULE_GET_API(StringToLongLong); 251 | REDISMODULE_GET_API(StringToDouble); 252 | REDISMODULE_GET_API(Call); 253 | REDISMODULE_GET_API(CallReplyProto); 254 | REDISMODULE_GET_API(FreeCallReply); 255 | REDISMODULE_GET_API(CallReplyInteger); 256 | REDISMODULE_GET_API(CallReplyType); 257 | REDISMODULE_GET_API(CallReplyLength); 258 | REDISMODULE_GET_API(CallReplyArrayElement); 259 | REDISMODULE_GET_API(CallReplyStringPtr); 260 | REDISMODULE_GET_API(CreateStringFromCallReply); 261 | REDISMODULE_GET_API(CreateString); 262 | REDISMODULE_GET_API(CreateStringFromLongLong); 263 | REDISMODULE_GET_API(CreateStringFromString); 264 | REDISMODULE_GET_API(CreateStringPrintf); 265 | REDISMODULE_GET_API(FreeString); 266 | REDISMODULE_GET_API(StringPtrLen); 267 | REDISMODULE_GET_API(AutoMemory); 268 | REDISMODULE_GET_API(Replicate); 269 | REDISMODULE_GET_API(ReplicateVerbatim); 270 | REDISMODULE_GET_API(DeleteKey); 271 | REDISMODULE_GET_API(StringSet); 272 | REDISMODULE_GET_API(StringDMA); 273 | REDISMODULE_GET_API(StringTruncate); 274 | REDISMODULE_GET_API(GetExpire); 275 | REDISMODULE_GET_API(SetExpire); 276 | REDISMODULE_GET_API(ZsetAdd); 277 | REDISMODULE_GET_API(ZsetIncrby); 278 | REDISMODULE_GET_API(ZsetScore); 279 | REDISMODULE_GET_API(ZsetRem); 280 | REDISMODULE_GET_API(ZsetRangeStop); 281 | REDISMODULE_GET_API(ZsetFirstInScoreRange); 282 | REDISMODULE_GET_API(ZsetLastInScoreRange); 283 | REDISMODULE_GET_API(ZsetFirstInLexRange); 284 | REDISMODULE_GET_API(ZsetLastInLexRange); 285 | REDISMODULE_GET_API(ZsetRangeCurrentElement); 286 | REDISMODULE_GET_API(ZsetRangeNext); 287 | REDISMODULE_GET_API(ZsetRangePrev); 288 | REDISMODULE_GET_API(ZsetRangeEndReached); 289 | REDISMODULE_GET_API(HashSet); 290 | REDISMODULE_GET_API(HashGet); 291 | REDISMODULE_GET_API(IsKeysPositionRequest); 292 | REDISMODULE_GET_API(KeyAtPos); 293 | REDISMODULE_GET_API(GetClientId); 294 | REDISMODULE_GET_API(PoolAlloc); 295 | REDISMODULE_GET_API(CreateDataType); 296 | REDISMODULE_GET_API(ModuleTypeSetValue); 297 | REDISMODULE_GET_API(ModuleTypeGetType); 298 | REDISMODULE_GET_API(ModuleTypeGetValue); 299 | REDISMODULE_GET_API(SaveUnsigned); 300 | REDISMODULE_GET_API(LoadUnsigned); 301 | REDISMODULE_GET_API(SaveSigned); 302 | REDISMODULE_GET_API(LoadSigned); 303 | REDISMODULE_GET_API(SaveString); 304 | REDISMODULE_GET_API(SaveStringBuffer); 305 | REDISMODULE_GET_API(LoadString); 306 | REDISMODULE_GET_API(LoadStringBuffer); 307 | REDISMODULE_GET_API(SaveDouble); 308 | REDISMODULE_GET_API(LoadDouble); 309 | REDISMODULE_GET_API(SaveFloat); 310 | REDISMODULE_GET_API(LoadFloat); 311 | REDISMODULE_GET_API(EmitAOF); 312 | REDISMODULE_GET_API(Log); 313 | REDISMODULE_GET_API(LogIOError); 314 | REDISMODULE_GET_API(StringAppendBuffer); 315 | REDISMODULE_GET_API(RetainString); 316 | REDISMODULE_GET_API(StringCompare); 317 | REDISMODULE_GET_API(GetContextFromIO); 318 | REDISMODULE_GET_API(BlockClient); 319 | REDISMODULE_GET_API(UnblockClient); 320 | REDISMODULE_GET_API(IsBlockedReplyRequest); 321 | REDISMODULE_GET_API(IsBlockedTimeoutRequest); 322 | REDISMODULE_GET_API(GetBlockedClientPrivateData); 323 | REDISMODULE_GET_API(AbortBlock); 324 | REDISMODULE_GET_API(Milliseconds); 325 | 326 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 327 | return REDISMODULE_OK; 328 | } 329 | 330 | #else 331 | 332 | /* Things only defined for the modules core, not exported to modules 333 | * including this file. */ 334 | #define RedisModuleString robj 335 | 336 | #endif /* REDISMODULE_CORE */ 337 | #endif /* REDISMOUDLE_H */ 338 | --------------------------------------------------------------------------------