From ce54b8f089e5011465527313b6439fafd8e6a910 Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Sat, 23 May 2026 04:34:58 +0800 Subject: [PATCH 1/8] [feature] add gtid gap log --- src/Makefile | 4 ++-- src/config.c | 1 + src/server.c | 1 + src/server.h | 3 +++ src/xredis_adaptation_version.c | 1 + src/xredis_gtid_gap_log.c | 1 + tests/test_helper.tcl | 1 + 7 files changed, 10 insertions(+), 2 deletions(-) create mode 120000 src/xredis_adaptation_version.c create mode 120000 src/xredis_gtid_gap_log.c diff --git a/src/Makefile b/src/Makefile index 970374fe1b8..e53e3fbc3e9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -221,7 +221,7 @@ ifdef OPENSSL_PREFIX endif # Include paths to dependencies -FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/xredis-gtid/include +FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/xredis-gtid/include ifdef SWAP FINAL_CFLAGS+= -I../deps/tdigest -I../deps/rocksdb/include endif @@ -322,7 +322,7 @@ endif REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX) REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX) REDIS_SWAP_OBJ=ctrip_swap.o ctrip_swap_adlist.o ctrip_lru_cache.o ctrip_swap_async.o ctrip_swap_batch.o ctrip_swap_cmd.o ctrip_swap_data.o ctrip_swap_debug.o ctrip_swap_evict.o ctrip_swap_exec.o ctrip_swap_expire.o ctrip_swap_hash.o ctrip_swap_set.o ctrip_swap_list.o ctrip_swap_iter.o ctrip_swap_zset.o ctrip_swap_meta.o ctrip_swap_object.o ctrip_swap_rdb.o ctrip_swap_repl.o ctrip_swap_rio.o ctrip_swap_rocks.o ctrip_swap_stat.o ctrip_swap_sync.o ctrip_swap_thread.o ctrip_swap_util.o ctrip_swap_lock.o ctrip_swap_string.o ctrip_swap_bitmap.o ctrip_swap_compact.o ctrip_swap_slowlog.o ctrip_swap_blocked.o ctrip_cuckoo_hash.o ctrip_cuckoo_filter.o ctrip_swap_filter.o ctrip_absent_cache.o ctrip_swap_load.o ctrip_swap_dirty.o ctrip_swap_persist.o ctrip_roaring_bitmap.o ctrip_swap_rordb.o ctrip_wtdigest.o ctrip_swap_server.o -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o ctrip.o xredis_gtid.o xredis_gtid_aof.o xredis_gtid_repl.o xredis_gtid_rs.o xredis_gtid_rdb.o ctrip_heartbeat.o +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o ctrip.o xredis_gtid.o xredis_gtid_aof.o xredis_gtid_repl.o xredis_gtid_rs.o xredis_gtid_rdb.o xredis_gtid_gap_log.o xredis_adaptation_version.o ctrip_heartbeat.o ifdef SWAP REDIS_SERVER_OBJ+= $(REDIS_SWAP_OBJ) diff --git a/src/config.c b/src/config.c index 489a2cfbdfd..4cd79e15641 100644 --- a/src/config.c +++ b/src/config.c @@ -3190,6 +3190,7 @@ standardConfig configs[] = { /* ctrip configs */ createBoolConfig("gtid-enabled", NULL, MODIFIABLE_CONFIG, server.gtid_enabled, 0, NULL, updateGtidEnabled), + createBoolConfig("gtid-gaplog-enabled", NULL, MODIFIABLE_CONFIG, server.gtid_gaplog_enabled, 1, NULL, NULL), #ifdef USE_OPENSSL createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */ diff --git a/src/server.c b/src/server.c index 203d68a09bd..ca50543d8e0 100644 --- a/src/server.c +++ b/src/server.c @@ -3521,6 +3521,7 @@ void initServer(void) { server.gtid_lost = gtidSetNew(); xsyncUuidInterestedInit(); gtidInitialInfoInit(server.gtid_initial); + server.gtid_gap_log = createGtidGapLog(); server.gtid_xsync_fullresync_indicator = 0; server.gtid_executed_cmd_count = 0; server.gtid_ignored_cmd_count = 0; diff --git a/src/server.h b/src/server.h index 3c74272f8d6..bb65c998923 100644 --- a/src/server.h +++ b/src/server.h @@ -1741,6 +1741,9 @@ struct redisServer { long long gtid_ignored_cmd_count; long long gtid_executed_cmd_count; long long gtid_sync_stat[GTID_SYNC_TYPES]; + int gtid_gaplog_enabled; + gtidGapLog* gtid_gap_log; + /* importing mode */ mstime_t importing_end_time; /* in milliseconds */ int importing_expire_enabled; diff --git a/src/xredis_adaptation_version.c b/src/xredis_adaptation_version.c new file mode 120000 index 00000000000..324fe6944cd --- /dev/null +++ b/src/xredis_adaptation_version.c @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_adaptation_version.c \ No newline at end of file diff --git a/src/xredis_gtid_gap_log.c b/src/xredis_gtid_gap_log.c new file mode 120000 index 00000000000..d9bf28f3f7b --- /dev/null +++ b/src/xredis_gtid_gap_log.c @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_gtid_gap_log.c \ No newline at end of file diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index dd20be9a60f..09721f21980 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -18,6 +18,7 @@ set ::gtid_tests { gtid/replication-psync gtid/sync gtid/xsync + gtid/gaplog } set ::all_tests { From 46c4ae9fbb3c0f9431daa105fa58797615e955a8 Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Mon, 25 May 2026 15:59:29 +0800 Subject: [PATCH 2/8] [feature] redisCommand add parse key/subkey function (only gtid) --- src/Makefile | 6 +- src/server.c | 3 + src/server.h | 5 + src/xredis_cmdparse.c | 266 ++++++++++++++++++++++++++++++++++++++++ src/xredis_commands.def | 1 + tests/test_helper.tcl | 2 + 6 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 src/xredis_cmdparse.c create mode 120000 src/xredis_commands.def diff --git a/src/Makefile b/src/Makefile index e53e3fbc3e9..946c60deaeb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -221,7 +221,7 @@ ifdef OPENSSL_PREFIX endif # Include paths to dependencies -FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/xredis-gtid/include +FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/xredis-gtid/include -I../deps/xredis-gtid/xredis ifdef SWAP FINAL_CFLAGS+= -I../deps/tdigest -I../deps/rocksdb/include endif @@ -302,6 +302,8 @@ ifdef SWAP FINAL_CFLAGS+=-DENABLE_SWAP=1 endif +FINAL_CFLAGS+=-DENABLE_CMDPARSE=1 + REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS) REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL) @@ -322,7 +324,7 @@ endif REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX) REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX) REDIS_SWAP_OBJ=ctrip_swap.o ctrip_swap_adlist.o ctrip_lru_cache.o ctrip_swap_async.o ctrip_swap_batch.o ctrip_swap_cmd.o ctrip_swap_data.o ctrip_swap_debug.o ctrip_swap_evict.o ctrip_swap_exec.o ctrip_swap_expire.o ctrip_swap_hash.o ctrip_swap_set.o ctrip_swap_list.o ctrip_swap_iter.o ctrip_swap_zset.o ctrip_swap_meta.o ctrip_swap_object.o ctrip_swap_rdb.o ctrip_swap_repl.o ctrip_swap_rio.o ctrip_swap_rocks.o ctrip_swap_stat.o ctrip_swap_sync.o ctrip_swap_thread.o ctrip_swap_util.o ctrip_swap_lock.o ctrip_swap_string.o ctrip_swap_bitmap.o ctrip_swap_compact.o ctrip_swap_slowlog.o ctrip_swap_blocked.o ctrip_cuckoo_hash.o ctrip_cuckoo_filter.o ctrip_swap_filter.o ctrip_absent_cache.o ctrip_swap_load.o ctrip_swap_dirty.o ctrip_swap_persist.o ctrip_roaring_bitmap.o ctrip_swap_rordb.o ctrip_wtdigest.o ctrip_swap_server.o -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o ctrip.o xredis_gtid.o xredis_gtid_aof.o xredis_gtid_repl.o xredis_gtid_rs.o xredis_gtid_rdb.o xredis_gtid_gap_log.o xredis_adaptation_version.o ctrip_heartbeat.o +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o ctrip.o xredis_gtid.o xredis_gtid_aof.o xredis_gtid_repl.o xredis_gtid_rs.o xredis_gtid_rdb.o xredis_gtid_gap_log.o xredis_adaptation_version.o ctrip_heartbeat.o xredis_cmdparse.o ifdef SWAP REDIS_SERVER_OBJ+= $(REDIS_SWAP_OBJ) diff --git a/src/server.c b/src/server.c index ca50543d8e0..27bad55a938 100644 --- a/src/server.c +++ b/src/server.c @@ -3521,6 +3521,9 @@ void initServer(void) { server.gtid_lost = gtidSetNew(); xsyncUuidInterestedInit(); gtidInitialInfoInit(server.gtid_initial); +#ifdef ENABLE_CMDPARSE + cmdParseBindToCommands(); +#endif server.gtid_gap_log = createGtidGapLog(); server.gtid_xsync_fullresync_indicator = 0; server.gtid_executed_cmd_count = 0; diff --git a/src/server.h b/src/server.h index bb65c998923..d2ee2435d3b 100644 --- a/src/server.h +++ b/src/server.h @@ -1798,6 +1798,11 @@ struct redisCommand { ACLs. A connection is able to execute a given command if the user associated to the connection has this command bit set in the bitmap of allowed commands. */ +#ifdef ENABLE_CMDPARSE + /* 供 swap 和 gtid 共用的命令 key 解析接口(放在末尾以避免破坏命令表的位置初始化) */ + int (*cmdparse_count)(robj **argv, int argc); + void (*cmdparse_parse)(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key); +#endif }; struct redisError { diff --git a/src/xredis_cmdparse.c b/src/xredis_cmdparse.c new file mode 100644 index 00000000000..b4475a444c2 --- /dev/null +++ b/src/xredis_cmdparse.c @@ -0,0 +1,266 @@ +#include "xredis_cmdparse.h" +#include "server.h" + +/* ================================================================ + * 辅助函数:sds 转大写(server.commands 的 key 为大写) + * ================================================================ */ +static sds sdsdupupper(sds s) { + sds result = sdsdup(s); + for (size_t i = 0; i < sdslen(result); i++) { + result[i] = toupper((unsigned char)result[i]); + } + return result; +} + +/* ================================================================ + * 各命令的 count / parse 实现 + * ================================================================ */ + +/* --- del / unlink:多个 key --- */ +static int cmdCountDel(robj **argv, int argc) { + UNUSED(argv); + return argc > 1 ? argc - 1 : 0; +} +static void cmdParseDel(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + for (int i = 1; i < argc; i++) { + on_key(ctx, OBJ_UNKNOWN, i, 0, 0, 0, NULL); + } +} + +/* --- 单个 key,无 subkeys(通过命令名推断类型)--- */ +static int cmdCountSingleKey(robj **argv, int argc) { + UNUSED(argv); UNUSED(argc); + return 1; +} +static int cmdKeyTypeFromCommand(struct redisCommand *cmd) { + if (cmd->flags & CMD_CATEGORY_STRING) return OBJ_STRING; + if (cmd->flags & CMD_CATEGORY_LIST) return OBJ_LIST; + if (cmd->flags & CMD_CATEGORY_HASH) return OBJ_HASH; + if (cmd->flags & CMD_CATEGORY_SET) return OBJ_SET; + if (cmd->flags & CMD_CATEGORY_SORTEDSET) return OBJ_ZSET; + if (cmd->flags & CMD_CATEGORY_BITMAP) return OBJ_STRING; + return OBJ_UNKNOWN; +} +static void cmdParseSingleKey(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); UNUSED(argc); + on_key(ctx, cmdKeyTypeFromCommand(cmd), 1, 0, 0, 0, NULL); +} + +/* --- mset / msetnx:多个 key,间隔出现 --- */ +static int cmdCountMset(robj **argv, int argc) { + UNUSED(argv); + return argc > 1 ? (argc - 1) / 2 : 0; +} +static void cmdParseMset(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); UNUSED(argv); + for (int i = 1; i < argc; i += 2) { + on_key(ctx, OBJ_STRING, i, 0, 0, 0, NULL); + } +} + +/* --- hset / hmset:单个 key + subkeys(field), 步长为2, 从argv[2]开始 --- */ +static void cmdParseHset(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + int subkeys_count = (argc - 2) / 2; + on_key(ctx, OBJ_HASH, 1, subkeys_count, 2, 2, NULL); +} + +/* --- hdel:单个 key + 多个 subkeys, 步长为1, 从argv[2]开始 --- */ +static void cmdParseHdel(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + int subkeys_count = argc - 2; + on_key(ctx, OBJ_HASH, 1, subkeys_count, 2, 1, NULL); +} + +/* --- hsetnx / hincrby / hincrbyfloat:单个 key + 1 个 subkey --- */ +static void cmdParseHsetnx(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + if (argc >= 3) { + on_key(ctx, OBJ_HASH, 1, 1, 2, 1, NULL); + } else { + on_key(ctx, OBJ_HASH, 1, 0, 0, 0, NULL); + } +} + +/* --- sadd / srem / spop:单个 key + 多个 subkeys, 步长为1, 从argv[2]开始 --- */ +static void cmdParseSadd(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + int subkeys_count = argc - 2; + on_key(ctx, OBJ_SET, 1, subkeys_count, 2, 1, NULL); +} + +/* --- smove:2 个 key,各带 1 个 subkey(member=argv[3])--- */ +static int cmdCountRename(robj **argv, int argc) { + UNUSED(argv); + return argc >= 3 ? 2 : (argc >= 2 ? 1 : 0); +} +static void cmdParseSmove(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); UNUSED(argv); + if (argc >= 4) { + on_key(ctx, OBJ_SET, 1, 1, 3, 1, NULL); + on_key(ctx, OBJ_SET, 2, 1, 3, 1, NULL); + } else if (argc >= 3) { + on_key(ctx, OBJ_SET, 1, 0, 0, 0, NULL); + on_key(ctx, OBJ_SET, 2, 0, 0, 0, NULL); + } else if (argc >= 2) { + on_key(ctx, OBJ_SET, 1, 0, 0, 0, NULL); + } +} + +/* --- zadd:单个 key + member subkeys, 步长为2 --- */ +static void cmdParseZadd(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + int i = 2; + while (i < argc) { + sds arg = (sds)argv[i]->ptr; + if (!strcasecmp(arg, "nx") || !strcasecmp(arg, "xx") || + !strcasecmp(arg, "ch") || !strcasecmp(arg, "incr") || + !strcasecmp(arg, "gt") || !strcasecmp(arg, "lt")) { + i++; + } else { + break; + } + } + int subkeys_count = (argc - i) / 2; + on_key(ctx, OBJ_ZSET, 1, subkeys_count, i + 1, 2, NULL); +} + +/* --- zrem:单个 key + 多个 subkeys, 步长为1, 从argv[2]开始 --- */ +static void cmdParseZrem(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + int subkeys_count = argc - 2; + on_key(ctx, OBJ_ZSET, 1, subkeys_count, 2, 1, NULL); +} + +/* --- zincrby:单个 key + 1 个 subkey --- */ +static void cmdParseZincrby(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + if (argc >= 4) { + on_key(ctx, OBJ_ZSET, 1, 1, 3, 1, NULL); + } else { + on_key(ctx, OBJ_ZSET, 1, 0, 0, 0, NULL); + } +} + +/* --- rename / renamenx:2 个 key --- */ +static void cmdParseRename(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); + if (argc >= 3) { + on_key(ctx, OBJ_UNKNOWN, 1, 0, 0, 0, NULL); + on_key(ctx, OBJ_UNKNOWN, 2, 0, 0, 0, NULL); + } else if (argc >= 2) { + on_key(ctx, OBJ_UNKNOWN, 1, 0, 0, 0, NULL); + } +} + +/* --- bitop:argv[1]=操作符, argv[2+]=key(参考 server 表 firstkey=2)--- */ +static int cmdCountBitOp(robj **argv, int argc) { + UNUSED(argv); + return argc > 2 ? argc - 2 : 0; +} +static void cmdParseBitOp(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); UNUSED(argv); + for (int i = 2; i < argc; i++) { + on_key(ctx, OBJ_STRING, i, 0, 0, 0, NULL); + } +} + +/* --- zunionstore / zinterstore / zdiffstore:dest + numkeys 个 key --- */ +static int cmdCountZstore(robj **argv, int argc) { + UNUSED(argv); + if (argc < 4) return argc >= 2 ? 1 : 0; + long long numkeys = 0; + if (getLongLongFromObject(argv[2], &numkeys) != C_OK || numkeys < 1) { + return 1; /* dest only */ + } + return 1 + (int)numkeys; +} +static void cmdParseZstore(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + UNUSED(dbid); UNUSED(argv); + if (argc < 2) return; + on_key(ctx, OBJ_ZSET, 1, 0, 0, 0, NULL); /* dest */ + if (argc < 4) return; + long long numkeys = 0; + if (getLongLongFromObject(argv[2], &numkeys) != C_OK || numkeys < 1) return; + int max_keys = argc - 3; + if (numkeys > max_keys) numkeys = max_keys; + for (long long i = 0; i < numkeys; i++) { + on_key(ctx, OBJ_ZSET, 3 + (int)i, 0, 0, 0, NULL); + } +} + +/* ================================================================ + * 引入由 generate_cmdparse_commands.py 自动生成的命令注册表 + * ================================================================ */ +#include "xredis_commands.def" + +/* ================================================================ + * 公共接口:通过 server.commands 查找 + * ================================================================ */ + +/* 计算命令中包含的 key 条目数(通过 lookupCommand 从 server.commands 取) */ +int cmdParseCountKeys(robj **argv, int argc) { + if (argc < 1) return 0; + sds cmd_upper = sdsdupupper((sds)argv[0]->ptr); + struct redisCommand *cmd = lookupCommand(cmd_upper); + sdsfree(cmd_upper); + if (cmd != NULL && cmd->cmdparse_count != NULL) { + return cmd->cmdparse_count(argv, argc); + } + /* unknown command fallback */ + return argc >= 2 ? 1 : 0; +} + +/* 解析命令,通过回调通知每个 key 的位置描述(通过 lookupCommand 从 server.commands 取) */ +void cmdParseKeys(int dbid, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + if (argc < 1) return; + sds cmd_upper = sdsdupupper((sds)argv[0]->ptr); + struct redisCommand *cmd = lookupCommand(cmd_upper); + sdsfree(cmd_upper); + if (cmd != NULL && cmd->cmdparse_parse != NULL) { + cmd->cmdparse_parse(dbid, cmd, argv, argc, ctx, on_key); + } else if (argc >= 2) { + /* unknown command fallback */ + serverLog(LL_WARNING, "Unknown command '%s' for key propagation", (sds)argv[0]->ptr); + // on_key(ctx, OBJ_UNKNOWN, 1, 0, 0, 0, NULL); + serverPanic("unknown command fallback"); + } +} + +/* 绑定 cmdparse 函数到 server.commands 中的所有命令 */ +void cmdParseBindToCommands(void) { + int i; + for (i = 0; cmd_parse_commands[i].name != NULL; i++) { + sds name = sdsnew(cmd_parse_commands[i].name); + for (size_t j = 0; j < sdslen(name); j++) { + name[j] = toupper((unsigned char)name[j]); + } + struct redisCommand *cmd = lookupCommand(name); + sdsfree(name); + if (cmd != NULL) { + cmd->cmdparse_count = cmd_parse_commands[i].count; + cmd->cmdparse_parse = cmd_parse_commands[i].parse; + } + } +} + +/* 通过命令名查找 count 函数(从 server.commands 取) */ +int (*cmdParseGetCountFunc(const char *cmd_name))(robj **argv, int argc) { + if (cmd_name == NULL) return NULL; + struct redisCommand *cmd = lookupCommandByCString(cmd_name); + if (cmd != NULL) { + return cmd->cmdparse_count; + } + return NULL; +} + +/* 通过命令名查找 parse 函数(从 server.commands 取) */ +void (*cmdParseGetParseFunc(const char *cmd_name))(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { + if (cmd_name == NULL) return NULL; + struct redisCommand *cmd = lookupCommandByCString(cmd_name); + if (cmd != NULL) { + return cmd->cmdparse_parse; + } + return NULL; +} diff --git a/src/xredis_commands.def b/src/xredis_commands.def new file mode 120000 index 00000000000..3853a467905 --- /dev/null +++ b/src/xredis_commands.def @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_commands.def \ No newline at end of file diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 09721f21980..e7a4a6585df 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -19,6 +19,8 @@ set ::gtid_tests { gtid/sync gtid/xsync gtid/gaplog + gtid/gaplog_commands + gtid/gaplog_write_commands } set ::all_tests { From 61dce410dc383571306ac734be8d3414a8c2e446 Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Mon, 25 May 2026 21:16:02 +0800 Subject: [PATCH 3/8] [feature] swap change to parse key/subkey --- deps/xredis-gtid | 2 +- src/ctrip_swap.h | 33 +--- src/ctrip_swap_cmd.c | 348 ++++++++++++++++++++++-------------------- src/server.c | 2 +- src/server.h | 2 - src/xredis_cmdparse.c | 267 +------------------------------- 6 files changed, 186 insertions(+), 468 deletions(-) mode change 100644 => 120000 src/xredis_cmdparse.c diff --git a/deps/xredis-gtid b/deps/xredis-gtid index e642bd25ac3..7d8aa82656f 160000 --- a/deps/xredis-gtid +++ b/deps/xredis-gtid @@ -1 +1 @@ -Subproject commit e642bd25ac3a282b04472834dcb2928b8cbbcf47 +Subproject commit 7d8aa82656f7ebdbd39a2b84d9a1b7456290c1a9 diff --git a/src/ctrip_swap.h b/src/ctrip_swap.h index e720b74f101..feb467e5bf2 100644 --- a/src/ctrip_swap.h +++ b/src/ctrip_swap.h @@ -244,22 +244,6 @@ int getKeyRequestsMetaScan(int dbid, struct redisCommand *cmd, robj **argv, int int getKeyRequestsSort(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -#define getKeyRequestsHsetnx getKeyRequestsHset -#define getKeyRequestsHget getKeyRequestsHmget -#define getKeyRequestsHdel getKeyRequestsHmget -#define getKeyRequestsHstrlen getKeyRequestsHmget -#define getKeyRequestsHincrby getKeyRequestsHget -#define getKeyRequestsHincrbyfloat getKeyRequestsHmget -#define getKeyRequestsHexists getKeyRequestsHmget -int getKeyRequestsHset(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsHmget(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsHlen(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); - -#define getKeyRequestsSadd getKeyRequestSmembers -#define getKeyRequestsSrem getKeyRequestSmembers -#define getKeyRequestsSdiffstore getKeyRequestsSinterstore -#define getKeyRequestsSunionstore getKeyRequestsSinterstore -int getKeyRequestSmembers(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestSmove(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsSinterstore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); @@ -278,18 +262,14 @@ int getKeyRequestsLindex(int dbid, struct redisCommand *cmd, robj **argv, int ar int getKeyRequestsLrange(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsLtrim(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsZAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsZScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsZMScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsZincrby(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrange(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrangestore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsSinterstore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZpopMin(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZpopMax(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsBzpopMin(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsBzpopMax(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrangeByScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrevrangeByScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsZremRangeByScore1(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); #define getKeyRequestsZremRangeByScore getKeyRequestsZrangeByScore int getKeyRequestsZrevrangeByLex(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrangeByLex(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); @@ -298,16 +278,10 @@ int getKeyRequestsZlexCount(int dbid, struct redisCommand *cmd, robj **argv, int #define getKeyRequestsSdiffstore getKeyRequestsSinterstore #define getKeyRequestsSunionstore getKeyRequestsSinterstore -#define getKeyRequestsZrem getKeyRequestsZScore -int getKeyRequestsGeoAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsGeoRadius(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsGeoHash(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsGeoDist(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsGeoSearch(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsGeoSearchStore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); #define getKeyRequestsGeoRadiusByMember getKeyRequestsGeoRadius -#define getKeyRequestsGeoPos getKeyRequestsGeoHash int getKeyRequestsGtid(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); @@ -317,9 +291,6 @@ int getKeyRequestsGetbit(int dbid, struct redisCommand *cmd, robj **argv, int ar int getKeyRequestsBitcount(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsBitpos(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsBitop(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsBitField(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); - -int getKeyRequestsMemory(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsMemory(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); diff --git a/src/ctrip_swap_cmd.c b/src/ctrip_swap_cmd.c index 6d6453a6b23..fd84d8c10dc 100644 --- a/src/ctrip_swap_cmd.c +++ b/src/ctrip_swap_cmd.c @@ -29,6 +29,7 @@ #include "ctrip_swap.h" #include #include "slowlog.h" +#include "xredis_cmdparse.h" struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"module",moduleCommand,-2, @@ -95,11 +96,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"bitfield",bitfieldCommand,-2, "write use-memory @bitmap @swap_bitmap", - 0,NULL,getKeyRequestsBitField,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"bitfield_ro",bitfieldroCommand,-2, "read-only fast @bitmap @swap_bitmap", - 0,NULL,getKeyRequestsBitField,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"setrange",setrangeCommand,4, "write use-memory @string @swap_string", @@ -207,11 +208,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"sadd",saddCommand,-3, "write use-memory fast @set @swap_set", - 0,NULL,getKeyRequestsSadd,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"srem",sremCommand,-3, "write fast @set @swap_set", - 0,NULL,getKeyRequestsSrem,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, /* smove cmd intention flags set by getKeyRequestSmove */ {"smove",smoveCommand,4, @@ -220,11 +221,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"sismember",sismemberCommand,3, "read-only fast @set @swap_set", - 0,NULL,getKeyRequestSmembers,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"smismember",smismemberCommand,-3, "read-only fast @set @swap_set", - 0,NULL,getKeyRequestSmembers,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"scard",swap_scardCommand,2, "read-only fast @set @swap_set", @@ -272,15 +273,15 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { /* (zset type) write command flag should be SWAP_IN_DEL, Because the index (score_cf data) needs to be deleted */ {"zadd",zaddCommand,-4, "write use-memory fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZAdd,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zincrby",zincrbyCommand,4, "write use-memory fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZincrby,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zrem",zremCommand,-3, "write fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZrem,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zremrangebyscore",zremrangebyscoreCommand,4, "write @sortedset @swap_zset", @@ -360,11 +361,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"zscore",zscoreCommand,3, "read-only fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZScore,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"zmscore",zmscoreCommand,-3, "read-only fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZMScore,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"zrank",zrankCommand,3, "read-only fast @sortedset @swap_zset", @@ -380,19 +381,19 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"zpopmin",zpopminCommand,-2, "write fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZpopMin,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zpopmax",zpopmaxCommand,-2, "write fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZpopMax,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"bzpopmin",bzpopminCommand,-3, "write no-script fast @sortedset @blocking @swap_zset", - 0,NULL,getKeyRequestsZpopMin,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, + 0,NULL,getKeyRequestsBzpopMin,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, {"bzpopmax",bzpopmaxCommand,-3, "write no-script fast @sortedset @blocking @swap_zset", - 0,NULL,getKeyRequestsZpopMax,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, + 0,NULL,getKeyRequestsBzpopMax,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, {"zrandmember",zrandmemberCommand,-2, "read-only random @sortedset @swap_zset", @@ -400,35 +401,35 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"hset",hsetCommand,-4, "write use-memory fast @hash @swap_hash", - 0,NULL,getKeyRequestsHset,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hsetnx",hsetnxCommand,4, "write use-memory fast @hash @swap_hash", - 0,NULL,getKeyRequestsHsetnx,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hget",hgetCommand,3, "read-only fast @hash @swap_hash", - 0,NULL,getKeyRequestsHget,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hmset",hsetCommand,-4, "write use-memory fast @hash @swap_hash", - 0,NULL,getKeyRequestsHset,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hmget",hmgetCommand,-3, "read-only fast @hash @swap_hash", - 0,NULL,getKeyRequestsHmget,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hincrby",hincrbyCommand,4, "write use-memory fast @hash @swap_hash", - 0,NULL,getKeyRequestsHincrby,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hincrbyfloat",hincrbyfloatCommand,4, "write use-memory fast @hash @swap_hash", - 0,NULL,getKeyRequestsHincrbyfloat,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hdel",hdelCommand,-3, "write fast @hash @swap_hash", - 0,NULL,getKeyRequestsHdel,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"hlen",hlenCommand,2, "read-only fast @hash @swap_hash", @@ -436,7 +437,7 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"hstrlen",hstrlenCommand,3, "read-only fast @hash @swap_hash", - 0,NULL,getKeyRequestsHstrlen,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hkeys",hkeysCommand,2, "read-only to-sort @hash @swap_hash", @@ -452,7 +453,7 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"hexists",hexistsCommand,3, "read-only fast @hash @swap_hash", - 0,NULL,getKeyRequestsHexists,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"hrandfield",hrandfieldCommand,-2, "read-only random @hash @swap_hash", @@ -803,7 +804,7 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"geoadd",geoaddCommand,-5, "write use-memory @geo @swap_zset", - 0,NULL,getKeyRequestsGeoAdd,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, /* GEORADIUS has store options that may write. */ {"georadius",georadiusCommand,-6, @@ -824,15 +825,15 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"geohash",geohashCommand,-2, "read-only @geo @swap_zset", - 0,NULL,getKeyRequestsGeoHash,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"geopos",geoposCommand,-2, "read-only @geo @swap_zset", - 0,NULL,getKeyRequestsGeoPos,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"geodist",geodistCommand,-4, "read-only @geo @swap_zset", - 0,NULL,getKeyRequestsGeoDist,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, {"geosearch",geosearchCommand,-7, "read-only @geo @swap_zset", @@ -1354,32 +1355,67 @@ void getKeyRequestsFreeResult(getKeyRequestsResult *result) { } } +/* ====== swapOnKey bridge: cmdparse → getKeyRequestsResult ====== */ + +typedef struct { + getKeyRequestsResult *result; +} swapCmdParseCtx; + + +/* cmdparse key/subkey => getKeyRequestsResult */ +static void swapOnKey(void *ctx, int dbid, struct redisCommand* cmd, robj** argv, int argc, int key_arg_idx, + int subkeys_count, int subkeys_start, + int subkeys_step, const int *subkey_arg_idxs, + const cmdParseKeyExtra *extra) { + swapCmdParseCtx *sctx = (swapCmdParseCtx *)ctx; + getKeyRequestsResult *result = sctx->result; + int intention = cmd->intention; + int intention_flags = cmd->intention_flags; + uint64_t cmd_flags = cmd->flags; + + + if (subkeys_count > 0) { + robj *key = argv[key_arg_idx]; + incrRefCount(key); + robj **subkeys = zmalloc(subkeys_count * sizeof(robj *)); + for (int i = 0; i < subkeys_count; i++) { + int idx = subkey_arg_idxs ? subkey_arg_idxs[i] + : subkeys_start + i * subkeys_step; + robj *subkey = argv[idx]; + incrRefCount(subkey); + subkeys[i] = subkey; + } + getKeyRequestsAppendSubkeyResult(result, REQUEST_LEVEL_KEY, key, + subkeys_count, subkeys, + intention, intention_flags, cmd_flags, dbid); + return; + } + + /* only key */ + robj *key = argv[key_arg_idx]; + incrRefCount(key); + getKeyRequestsAppendSubkeyResult(result, REQUEST_LEVEL_KEY, key, + 0, NULL, intention, intention_flags, cmd_flags, dbid); + +} + /* NOTE that result.{key,subkeys} are ONLY REFS to client argv (since client * outlives getKeysResult if no swap action happend. key, subkey will be * copied (using incrRefCount) when async swap acutally proceed. */ static int _getSingleCmdKeyRequests(int dbid, struct redisCommand* cmd, robj** argv, int argc, getKeyRequestsResult *result) { - if (cmd->getkeyrequests_proc == NULL) { - int i, numkeys; - getKeysResult keys = GETKEYS_RESULT_INIT; - /* whole key swaping, swaps defined by command arity. */ - numkeys = getKeysFromCommand(cmd,argv,argc,&keys); - getKeyRequestsPrepareResult(result,result->num+numkeys); - for (i = 0; i < numkeys; i++) { - robj *key = argv[keys.keys[i]]; - - incrRefCount(key); - getKeyRequestsAppendSubkeyResult(result,REQUEST_LEVEL_KEY,key,0,NULL, - cmd->intention,cmd->intention_flags,cmd->flags, dbid); - } - getKeysFreeResult(&keys); - return 0; + if (cmd->getkeyrequests_proc != NULL) { + return cmd->getkeyrequests_proc(dbid,cmd,argv,argc,result); } else if (cmd->flags & CMD_MODULE) { /* TODO support module */ + return 0; } else { - return cmd->getkeyrequests_proc(dbid,cmd,argv,argc,result); + swapCmdParseCtx ctx = { + .result = result, + }; + cmdParseKeys(dbid, cmd, argv, argc, &ctx, swapOnKey); + return 0; } - return 0; } static void getSingleCmdKeyRequests(client *c, getKeyRequestsResult *result) { @@ -1599,56 +1635,6 @@ int getKeyRequestsZdiffstore(int dbid, struct redisCommand *cmd, robj **argv, in return getKeyRequestsZunionInterDiffGeneric(dbid, cmd, argv, argc, result, SET_OP_DIFF); } -#define GETKEYS_RESULT_SUBKEYS_INIT_LEN 8 -#define GETKEYS_RESULT_SUBKEYS_LINER_LEN 1024 - -int getKeyRequestsSingleKeyWithSubkeys(int dbid, struct redisCommand *cmd, robj **argv, - int argc, struct getKeyRequestsResult *result, - int key_index, int first_subkey, int last_subkey, int subkey_step) { - int i, num = 0, capacity = GETKEYS_RESULT_SUBKEYS_INIT_LEN; - robj *key, **subkeys = NULL; - UNUSED(cmd); - - subkeys = zmalloc(capacity*sizeof(robj*)); - getKeyRequestsPrepareResult(result,result->num+1); - - key = argv[key_index]; - incrRefCount(key); - - if (last_subkey < 0) last_subkey += argc; - for (i = first_subkey; i <= last_subkey; i += subkey_step) { - robj *subkey = argv[i]; - if (num >= capacity) { - if (capacity < GETKEYS_RESULT_SUBKEYS_LINER_LEN) - capacity *= 2; - else - capacity += GETKEYS_RESULT_SUBKEYS_LINER_LEN; - - subkeys = zrealloc(subkeys, capacity*sizeof(robj*)); - } - incrRefCount(subkey); - subkeys[num++] = subkey; - } - getKeyRequestsAppendSubkeyResult(result,REQUEST_LEVEL_KEY,key,num,subkeys, - cmd->intention,cmd->intention_flags,cmd->flags, dbid); - - return 0; -} - -int getKeyRequestsHset(int dbid,struct redisCommand *cmd, robj **argv, int argc, - struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid,cmd,argv,argc,result,1,2,-1,2); -} - -int getKeyRequestsHmget(int dbid, struct redisCommand *cmd, robj **argv, int argc, - struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid,cmd,argv,argc,result,1,2,-1,1); -} - -int getKeyRequestSmembers(int dbid, struct redisCommand *cmd, robj **argv, int argc, - struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid,cmd,argv,argc,result,1,2,-1,1); -} int getKeyRequestSmove(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { @@ -1903,57 +1889,113 @@ int getKeyRequestsLtrim(int dbid, struct redisCommand *cmd, robj **argv, result,1,2,3,1/*num_ranges*/,(long)start,(long)stop,(int)1/*reverse*/); return 0; } -/** zset **/ -int getKeyRequestsZAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - int first_score = 2; - while(first_score < argc) { - char *opt = argv[first_score]->ptr; - if ( - strcasecmp(opt,"nx") != 0 && - strcasecmp(opt,"xx") != 0 && - strcasecmp(opt,"ch") != 0 && - strcasecmp(opt,"incr") != 0 && - strcasecmp(opt,"gt") != 0 && - strcasecmp(opt,"lt") != 0 - ) { - break; - } - first_score++; + +int getKeyRequestsBzpopMin(int dbid, struct redisCommand *cmd, robj **argv, + int argc, struct getKeyRequestsResult *result) { + + for (int i = 1; i < argc - 1; i++) { + robj *key = argv[i]; + incrRefCount(key); + + /* bzpopmin one summber */ + zrangespec *spec = zmalloc(sizeof(zrangespec)); + spec->min = -INFINITY; + spec->max = +INFINITY; + spec->minex = 0; + spec->maxex = 0; + + getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, + 0, /* reverse=0: min score first */ + spec, + 1, /* limit=1 */ + cmd->intention, + cmd->intention_flags, + cmd->flags, dbid); } - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, first_score + 1, -1, 2); + return C_OK; } -int getKeyRequestsZScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd,argv,argc,result,1,2,-1,1); -} +int getKeyRequestsBzpopMax(int dbid, struct redisCommand *cmd, robj **argv, + int argc, struct getKeyRequestsResult *result) { + for (int i = 1; i < argc - 1; i++) { + robj *key = argv[i]; + incrRefCount(key); -int getKeyRequestsZincrby(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 3, -1, 2); + zrangespec *spec = zmalloc(sizeof(zrangespec)); + spec->min = -INFINITY; + spec->max = +INFINITY; + spec->minex = 0; + spec->maxex = 0; + + getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, + 1, /* reverse=1: max score first */ + spec, + 1, /* limit=1 */ + cmd->intention, + cmd->intention_flags, + cmd->flags, dbid); + } + return C_OK; } -int getKeyRequestsZMScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 2, -1, 1); -} +int getKeyRequestsZpopMin(int dbid, struct redisCommand *cmd, robj **argv, + int argc, struct getKeyRequestsResult *result) { + long count = 1; -#define ZMIN -1 -#define ZMAX 1 -int getKeyRequestsZpopGeneric(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result, int flags) { - UNUSED(cmd), UNUSED(flags); - getKeyRequestsPrepareResult(result,result->num+ argc - 2); - for(int i = 1; i < argc - 1; i++) { - incrRefCount(argv[i]); - getKeyRequestsAppendSubkeyResult(result, REQUEST_LEVEL_KEY, argv[i], 0, NULL, cmd->intention, - cmd->intention_flags, cmd->flags, dbid); + if (argc >= 3) { + long long value; + if (getLongLongFromObject(argv[2], &value) == C_OK && value > 0) { + count = value; + } } + + robj *key = argv[1]; + incrRefCount(key); + + zrangespec *spec = zmalloc(sizeof(zrangespec)); + spec->min = -INFINITY; + spec->max = +INFINITY; + spec->minex = 0; + spec->maxex = 0; + + getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, + 0, /* reverse=0:,min score first */ + spec, + count, /* limit */ + cmd->intention, + cmd->intention_flags, + cmd->flags, dbid); return C_OK; } -int getKeyRequestsZpopMin(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsZpopGeneric(dbid, cmd, argv, argc, result, ZMIN); -} +int getKeyRequestsZpopMax(int dbid, struct redisCommand *cmd, robj **argv, + int argc, struct getKeyRequestsResult *result) { + long count = 1; + + if (argc >= 3) { + long long value; + if (getLongLongFromObject(argv[2], &value) == C_OK && value > 0) { + count = value; + } + } -int getKeyRequestsZpopMax(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsZpopGeneric(dbid, cmd, argv, argc, result, ZMAX); + robj *key = argv[1]; + incrRefCount(key); + + zrangespec *spec = zmalloc(sizeof(zrangespec)); + spec->min = -INFINITY; + spec->max = +INFINITY; + spec->minex = 0; + spec->maxex = 0; + + getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, + 1, /* reverse=1: max score first */ + spec, + count, + cmd->intention, + cmd->intention_flags, + cmd->flags, dbid); + return C_OK; } int getKeyRequestsZrangestore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { @@ -2070,30 +2112,6 @@ int getKeyRequestsZlexCount(int dbid, struct redisCommand *cmd, robj **argv, int int getKeyRequestsZremRangeByLex(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { return getKeyRequestsZrangeGeneric(dbid, cmd, argv, argc, result, ZRANGE_LEX, ZRANGE_DIRECTION_FORWARD); } -/** geo **/ -int getKeyRequestsGeoAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - int first_score = 2; - while(first_score < argc) { - char *opt = argv[first_score]->ptr; - if ( - strcasecmp(opt,"nx") != 0 && - strcasecmp(opt,"xx") != 0 && - strcasecmp(opt,"ch") != 0 - ) { - break; - } - first_score++; - } - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, first_score + 2, -1, 3); -} - -int getKeyRequestsGeoDist(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 2, -2, 1); -} - -int getKeyRequestsGeoHash(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { - return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 2, -1, 1); -} int getKeyRequestsGeoRadius(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { int storekeyIndex = -1; @@ -2247,14 +2265,6 @@ int getKeyRequestsBitop(int dbid, struct redisCommand *cmd, robj **argv, return getKeyRequestsOneDestKeyMultiSrcKeys(dbid, cmd, argv, argc, result, 2, 3, -1); } -int getKeyRequestsBitField(int dbid, struct redisCommand *cmd, robj **argv, - int argc, struct getKeyRequestsResult *result) { - - UNUSED(argc); - getKeyRequestsSingleKey(result,argv[1],cmd->intention,cmd->intention_flags,cmd->flags,dbid); - return 0; -} - #define GET_KEYREQUESTS_MEMORY_MUL 4 @@ -2340,6 +2350,10 @@ int swapCmdTest(int argc, char *argv[], int accurate) { TEST("cmd: init") { initServerConfig(); ACLInit(); + #ifdef ENABLE_CMDPARSE + cmdParseBindToCommands(); + #endif + server.hz = 10; c = createClient(NULL); initTestRedisDb(); diff --git a/src/server.c b/src/server.c index 27bad55a938..54a4922daf4 100644 --- a/src/server.c +++ b/src/server.c @@ -3524,7 +3524,7 @@ void initServer(void) { #ifdef ENABLE_CMDPARSE cmdParseBindToCommands(); #endif - server.gtid_gap_log = createGtidGapLog(); + server.gtid_gap_log = gtidGapLogNew(); server.gtid_xsync_fullresync_indicator = 0; server.gtid_executed_cmd_count = 0; server.gtid_ignored_cmd_count = 0; diff --git a/src/server.h b/src/server.h index d2ee2435d3b..3d42a0ab899 100644 --- a/src/server.h +++ b/src/server.h @@ -1799,8 +1799,6 @@ struct redisCommand { the user associated to the connection has this command bit set in the bitmap of allowed commands. */ #ifdef ENABLE_CMDPARSE - /* 供 swap 和 gtid 共用的命令 key 解析接口(放在末尾以避免破坏命令表的位置初始化) */ - int (*cmdparse_count)(robj **argv, int argc); void (*cmdparse_parse)(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key); #endif }; diff --git a/src/xredis_cmdparse.c b/src/xredis_cmdparse.c deleted file mode 100644 index b4475a444c2..00000000000 --- a/src/xredis_cmdparse.c +++ /dev/null @@ -1,266 +0,0 @@ -#include "xredis_cmdparse.h" -#include "server.h" - -/* ================================================================ - * 辅助函数:sds 转大写(server.commands 的 key 为大写) - * ================================================================ */ -static sds sdsdupupper(sds s) { - sds result = sdsdup(s); - for (size_t i = 0; i < sdslen(result); i++) { - result[i] = toupper((unsigned char)result[i]); - } - return result; -} - -/* ================================================================ - * 各命令的 count / parse 实现 - * ================================================================ */ - -/* --- del / unlink:多个 key --- */ -static int cmdCountDel(robj **argv, int argc) { - UNUSED(argv); - return argc > 1 ? argc - 1 : 0; -} -static void cmdParseDel(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - for (int i = 1; i < argc; i++) { - on_key(ctx, OBJ_UNKNOWN, i, 0, 0, 0, NULL); - } -} - -/* --- 单个 key,无 subkeys(通过命令名推断类型)--- */ -static int cmdCountSingleKey(robj **argv, int argc) { - UNUSED(argv); UNUSED(argc); - return 1; -} -static int cmdKeyTypeFromCommand(struct redisCommand *cmd) { - if (cmd->flags & CMD_CATEGORY_STRING) return OBJ_STRING; - if (cmd->flags & CMD_CATEGORY_LIST) return OBJ_LIST; - if (cmd->flags & CMD_CATEGORY_HASH) return OBJ_HASH; - if (cmd->flags & CMD_CATEGORY_SET) return OBJ_SET; - if (cmd->flags & CMD_CATEGORY_SORTEDSET) return OBJ_ZSET; - if (cmd->flags & CMD_CATEGORY_BITMAP) return OBJ_STRING; - return OBJ_UNKNOWN; -} -static void cmdParseSingleKey(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); UNUSED(argc); - on_key(ctx, cmdKeyTypeFromCommand(cmd), 1, 0, 0, 0, NULL); -} - -/* --- mset / msetnx:多个 key,间隔出现 --- */ -static int cmdCountMset(robj **argv, int argc) { - UNUSED(argv); - return argc > 1 ? (argc - 1) / 2 : 0; -} -static void cmdParseMset(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); UNUSED(argv); - for (int i = 1; i < argc; i += 2) { - on_key(ctx, OBJ_STRING, i, 0, 0, 0, NULL); - } -} - -/* --- hset / hmset:单个 key + subkeys(field), 步长为2, 从argv[2]开始 --- */ -static void cmdParseHset(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - int subkeys_count = (argc - 2) / 2; - on_key(ctx, OBJ_HASH, 1, subkeys_count, 2, 2, NULL); -} - -/* --- hdel:单个 key + 多个 subkeys, 步长为1, 从argv[2]开始 --- */ -static void cmdParseHdel(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - int subkeys_count = argc - 2; - on_key(ctx, OBJ_HASH, 1, subkeys_count, 2, 1, NULL); -} - -/* --- hsetnx / hincrby / hincrbyfloat:单个 key + 1 个 subkey --- */ -static void cmdParseHsetnx(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - if (argc >= 3) { - on_key(ctx, OBJ_HASH, 1, 1, 2, 1, NULL); - } else { - on_key(ctx, OBJ_HASH, 1, 0, 0, 0, NULL); - } -} - -/* --- sadd / srem / spop:单个 key + 多个 subkeys, 步长为1, 从argv[2]开始 --- */ -static void cmdParseSadd(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - int subkeys_count = argc - 2; - on_key(ctx, OBJ_SET, 1, subkeys_count, 2, 1, NULL); -} - -/* --- smove:2 个 key,各带 1 个 subkey(member=argv[3])--- */ -static int cmdCountRename(robj **argv, int argc) { - UNUSED(argv); - return argc >= 3 ? 2 : (argc >= 2 ? 1 : 0); -} -static void cmdParseSmove(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); UNUSED(argv); - if (argc >= 4) { - on_key(ctx, OBJ_SET, 1, 1, 3, 1, NULL); - on_key(ctx, OBJ_SET, 2, 1, 3, 1, NULL); - } else if (argc >= 3) { - on_key(ctx, OBJ_SET, 1, 0, 0, 0, NULL); - on_key(ctx, OBJ_SET, 2, 0, 0, 0, NULL); - } else if (argc >= 2) { - on_key(ctx, OBJ_SET, 1, 0, 0, 0, NULL); - } -} - -/* --- zadd:单个 key + member subkeys, 步长为2 --- */ -static void cmdParseZadd(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - int i = 2; - while (i < argc) { - sds arg = (sds)argv[i]->ptr; - if (!strcasecmp(arg, "nx") || !strcasecmp(arg, "xx") || - !strcasecmp(arg, "ch") || !strcasecmp(arg, "incr") || - !strcasecmp(arg, "gt") || !strcasecmp(arg, "lt")) { - i++; - } else { - break; - } - } - int subkeys_count = (argc - i) / 2; - on_key(ctx, OBJ_ZSET, 1, subkeys_count, i + 1, 2, NULL); -} - -/* --- zrem:单个 key + 多个 subkeys, 步长为1, 从argv[2]开始 --- */ -static void cmdParseZrem(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - int subkeys_count = argc - 2; - on_key(ctx, OBJ_ZSET, 1, subkeys_count, 2, 1, NULL); -} - -/* --- zincrby:单个 key + 1 个 subkey --- */ -static void cmdParseZincrby(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - if (argc >= 4) { - on_key(ctx, OBJ_ZSET, 1, 1, 3, 1, NULL); - } else { - on_key(ctx, OBJ_ZSET, 1, 0, 0, 0, NULL); - } -} - -/* --- rename / renamenx:2 个 key --- */ -static void cmdParseRename(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); - if (argc >= 3) { - on_key(ctx, OBJ_UNKNOWN, 1, 0, 0, 0, NULL); - on_key(ctx, OBJ_UNKNOWN, 2, 0, 0, 0, NULL); - } else if (argc >= 2) { - on_key(ctx, OBJ_UNKNOWN, 1, 0, 0, 0, NULL); - } -} - -/* --- bitop:argv[1]=操作符, argv[2+]=key(参考 server 表 firstkey=2)--- */ -static int cmdCountBitOp(robj **argv, int argc) { - UNUSED(argv); - return argc > 2 ? argc - 2 : 0; -} -static void cmdParseBitOp(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); UNUSED(argv); - for (int i = 2; i < argc; i++) { - on_key(ctx, OBJ_STRING, i, 0, 0, 0, NULL); - } -} - -/* --- zunionstore / zinterstore / zdiffstore:dest + numkeys 个 key --- */ -static int cmdCountZstore(robj **argv, int argc) { - UNUSED(argv); - if (argc < 4) return argc >= 2 ? 1 : 0; - long long numkeys = 0; - if (getLongLongFromObject(argv[2], &numkeys) != C_OK || numkeys < 1) { - return 1; /* dest only */ - } - return 1 + (int)numkeys; -} -static void cmdParseZstore(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - UNUSED(dbid); UNUSED(argv); - if (argc < 2) return; - on_key(ctx, OBJ_ZSET, 1, 0, 0, 0, NULL); /* dest */ - if (argc < 4) return; - long long numkeys = 0; - if (getLongLongFromObject(argv[2], &numkeys) != C_OK || numkeys < 1) return; - int max_keys = argc - 3; - if (numkeys > max_keys) numkeys = max_keys; - for (long long i = 0; i < numkeys; i++) { - on_key(ctx, OBJ_ZSET, 3 + (int)i, 0, 0, 0, NULL); - } -} - -/* ================================================================ - * 引入由 generate_cmdparse_commands.py 自动生成的命令注册表 - * ================================================================ */ -#include "xredis_commands.def" - -/* ================================================================ - * 公共接口:通过 server.commands 查找 - * ================================================================ */ - -/* 计算命令中包含的 key 条目数(通过 lookupCommand 从 server.commands 取) */ -int cmdParseCountKeys(robj **argv, int argc) { - if (argc < 1) return 0; - sds cmd_upper = sdsdupupper((sds)argv[0]->ptr); - struct redisCommand *cmd = lookupCommand(cmd_upper); - sdsfree(cmd_upper); - if (cmd != NULL && cmd->cmdparse_count != NULL) { - return cmd->cmdparse_count(argv, argc); - } - /* unknown command fallback */ - return argc >= 2 ? 1 : 0; -} - -/* 解析命令,通过回调通知每个 key 的位置描述(通过 lookupCommand 从 server.commands 取) */ -void cmdParseKeys(int dbid, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - if (argc < 1) return; - sds cmd_upper = sdsdupupper((sds)argv[0]->ptr); - struct redisCommand *cmd = lookupCommand(cmd_upper); - sdsfree(cmd_upper); - if (cmd != NULL && cmd->cmdparse_parse != NULL) { - cmd->cmdparse_parse(dbid, cmd, argv, argc, ctx, on_key); - } else if (argc >= 2) { - /* unknown command fallback */ - serverLog(LL_WARNING, "Unknown command '%s' for key propagation", (sds)argv[0]->ptr); - // on_key(ctx, OBJ_UNKNOWN, 1, 0, 0, 0, NULL); - serverPanic("unknown command fallback"); - } -} - -/* 绑定 cmdparse 函数到 server.commands 中的所有命令 */ -void cmdParseBindToCommands(void) { - int i; - for (i = 0; cmd_parse_commands[i].name != NULL; i++) { - sds name = sdsnew(cmd_parse_commands[i].name); - for (size_t j = 0; j < sdslen(name); j++) { - name[j] = toupper((unsigned char)name[j]); - } - struct redisCommand *cmd = lookupCommand(name); - sdsfree(name); - if (cmd != NULL) { - cmd->cmdparse_count = cmd_parse_commands[i].count; - cmd->cmdparse_parse = cmd_parse_commands[i].parse; - } - } -} - -/* 通过命令名查找 count 函数(从 server.commands 取) */ -int (*cmdParseGetCountFunc(const char *cmd_name))(robj **argv, int argc) { - if (cmd_name == NULL) return NULL; - struct redisCommand *cmd = lookupCommandByCString(cmd_name); - if (cmd != NULL) { - return cmd->cmdparse_count; - } - return NULL; -} - -/* 通过命令名查找 parse 函数(从 server.commands 取) */ -void (*cmdParseGetParseFunc(const char *cmd_name))(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key) { - if (cmd_name == NULL) return NULL; - struct redisCommand *cmd = lookupCommandByCString(cmd_name); - if (cmd != NULL) { - return cmd->cmdparse_parse; - } - return NULL; -} diff --git a/src/xredis_cmdparse.c b/src/xredis_cmdparse.c new file mode 120000 index 00000000000..65d69ab2695 --- /dev/null +++ b/src/xredis_cmdparse.c @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_cmdparse.c \ No newline at end of file From b5f82ef408a324a212d03faeca91fa0aabbe5e1b Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Thu, 4 Jun 2026 13:35:42 +0800 Subject: [PATCH 4/8] [gtid] submodule: add readBacklogIterator (Init/Deinit/SeekTo/ParseNext) and refactor saveGapLogFromGtidSet - readBacklogIterator stack-allocated with embedded mock client - querybuf reuse across gno iterations (no backlog re-read) - parseGtidCommand/parseMultiCommand take (argv, argc) and readBacklogIterator* respectively - saveGapLogFromGtidSet uses the iterator - parseCmdFromBacklog, cleanMockClient, resetMockClient removed - fix pre-existing typo: redisComamnd -> redisCommand in gtidOnKey --- deps/xredis-gtid | 2 +- src/Makefile | 6 +- ...ver.121736.dong-Virtual-Machine.1781074161 | Bin 0 -> 11210752 bytes src/ctrip_swap.h | 33 +- src/ctrip_swap_cmd.c | 348 +++++++++--------- src/networking.c | 8 +- src/redis_gtid.h | 1 + src/replication.c | 2 +- src/server.c | 5 +- src/server.h | 5 +- src/xredis_adaptation_version.c | 1 - src/xredis_cmdparse.c | 1 - src/xredis_gtid_adaptation_version.c | 1 + src/xredis_gtid_adaptation_version.h | 1 + src/xredis_gtid_aof.c | 1 - src/xredis_gtid_cmdparse.c | 1 + src/xredis_gtid_cmdparse.h | 1 + 17 files changed, 213 insertions(+), 204 deletions(-) create mode 100644 src/core.redis-server.121736.dong-Virtual-Machine.1781074161 create mode 120000 src/redis_gtid.h delete mode 120000 src/xredis_adaptation_version.c delete mode 120000 src/xredis_cmdparse.c create mode 120000 src/xredis_gtid_adaptation_version.c create mode 120000 src/xredis_gtid_adaptation_version.h delete mode 120000 src/xredis_gtid_aof.c create mode 120000 src/xredis_gtid_cmdparse.c create mode 120000 src/xredis_gtid_cmdparse.h diff --git a/deps/xredis-gtid b/deps/xredis-gtid index 7d8aa82656f..d7028ca424d 160000 --- a/deps/xredis-gtid +++ b/deps/xredis-gtid @@ -1 +1 @@ -Subproject commit 7d8aa82656f7ebdbd39a2b84d9a1b7456290c1a9 +Subproject commit d7028ca424ddf86fafdb9ced18711740c573b0ae diff --git a/src/Makefile b/src/Makefile index 946c60deaeb..39a9f8d18b7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -221,7 +221,7 @@ ifdef OPENSSL_PREFIX endif # Include paths to dependencies -FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/xredis-gtid/include -I../deps/xredis-gtid/xredis +FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/xredis-gtid/include ifdef SWAP FINAL_CFLAGS+= -I../deps/tdigest -I../deps/rocksdb/include endif @@ -302,8 +302,6 @@ ifdef SWAP FINAL_CFLAGS+=-DENABLE_SWAP=1 endif -FINAL_CFLAGS+=-DENABLE_CMDPARSE=1 - REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS) REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL) @@ -324,7 +322,7 @@ endif REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX) REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX) REDIS_SWAP_OBJ=ctrip_swap.o ctrip_swap_adlist.o ctrip_lru_cache.o ctrip_swap_async.o ctrip_swap_batch.o ctrip_swap_cmd.o ctrip_swap_data.o ctrip_swap_debug.o ctrip_swap_evict.o ctrip_swap_exec.o ctrip_swap_expire.o ctrip_swap_hash.o ctrip_swap_set.o ctrip_swap_list.o ctrip_swap_iter.o ctrip_swap_zset.o ctrip_swap_meta.o ctrip_swap_object.o ctrip_swap_rdb.o ctrip_swap_repl.o ctrip_swap_rio.o ctrip_swap_rocks.o ctrip_swap_stat.o ctrip_swap_sync.o ctrip_swap_thread.o ctrip_swap_util.o ctrip_swap_lock.o ctrip_swap_string.o ctrip_swap_bitmap.o ctrip_swap_compact.o ctrip_swap_slowlog.o ctrip_swap_blocked.o ctrip_cuckoo_hash.o ctrip_cuckoo_filter.o ctrip_swap_filter.o ctrip_absent_cache.o ctrip_swap_load.o ctrip_swap_dirty.o ctrip_swap_persist.o ctrip_roaring_bitmap.o ctrip_swap_rordb.o ctrip_wtdigest.o ctrip_swap_server.o -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o ctrip.o xredis_gtid.o xredis_gtid_aof.o xredis_gtid_repl.o xredis_gtid_rs.o xredis_gtid_rdb.o xredis_gtid_gap_log.o xredis_adaptation_version.o ctrip_heartbeat.o xredis_cmdparse.o +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o ctrip.o xredis_gtid.o xredis_gtid_repl.o xredis_gtid_rs.o xredis_gtid_rdb.o xredis_gtid_gap_log.o xredis_gtid_adaptation_version.o ctrip_heartbeat.o xredis_gtid_cmdparse.o ifdef SWAP REDIS_SERVER_OBJ+= $(REDIS_SWAP_OBJ) diff --git a/src/core.redis-server.121736.dong-Virtual-Machine.1781074161 b/src/core.redis-server.121736.dong-Virtual-Machine.1781074161 new file mode 100644 index 0000000000000000000000000000000000000000..0ce6be8b0c2cda80eff6356439f65dbd7ce503d1 GIT binary patch literal 11210752 zcmeF)378yJx%mInJv~WJ2+#>3hDE6f0m7P2h!}985(0!xngALVwAsVGEG+~KZd8T= z21IQ@R#`g85tX( z=2X4symdZxYUyRVPv3ptJ<_R^(VvX@ka@dijH@g9m9Cj(9I0odbVv6K==W=B{eBhy zSB(#HG=FzweQ}cqwmnRK4~m@p?n(=(2kqK}Re4!o*(}^n78|pX{jFcSW>0u3!q+LyxYZ+}<3`Xkzl-+Q?`eeIY%V@j&6uZ9o+nCvnDmXTIkCD@Z%

R9t z&3KdSZ_#|(&ir)XcD?VMFU=*|uJ>o*cCIAUvR}TYOIfxv-w3yJq@9-0c2#Lth_tIq zJ1wK_yc=vl{>^YcSK4XWuU&I|HECChw39faM|Hn;&HYfn?Q-i5IbTz%B<=D;Z`brJOS?j(UBpq(r-5fPUq#v#Bkg2(ruxp~wwX`c z<#OTiX}gO25_Qpb)kr%>;xj%p7tc_N z`M)v6w}Kz3^vz>+)H1Lv_zi=$yX`-J_aJ_q;6M54|GO^h`Dx1B+%P66gMa$7x=nwx z<;of1dR(pdvA;COSd((4lyxa9V_$Ese>;=yDKtKR(aCWMWO;3OdoHUzYV$RL{QrKv z%MQ(J)Q%G8-%vDvt4RKBWx2P1|0T|%qfTDr9JA=yBb{b1!~ZYXHqZaJ*PeSFsN1IdT(^Jt zy{=u0V4F&Q^5FQ4VTwZ3kou6cC*yR>5|lJ&CI#=6C-N-%Y(>lTQo>cBJT^*F(bILvBp1Rz~y;}6%tFLbFt)^;Tz1Na^ zr>f@Fdnvg$`iCypdnLK|No!ucHz`H#HMB1G63S!r-a_sbzN*`!_YQI|@UkxNyEj;; zd2L#)%k|nAz1C@6u4TIUMz3vht=dlek-DkdYtc@+TIA@ z(h;H4l%c1Shvsz3$tO)a?%2bpEuOX0r*@jT{c*>haLVHCk2&F#VBrxvoP6RAJDF+! zPBF(1VVnQ8VvZDae-CWG)68J=XRL|*Uq0MF#+%nN{~Q*a#`Ii%x?TYF93Z9VUr74h zHAdF{^Qv1!e%_fmx|QL&^Zv-=)<_5b_1p8W`Cd@^F5XIieeh3z9LF(^UkS>Aes+!g zTo(E9+l79jedJ7K;K!@cz!9nk_eZWj2ZC$czI%Q6;01o2Ir0CmInj5|I>b2?oFDi} zE)X13!M$I5zhkOt>fe~T;3kKqB?1T_fB*srAka_X^vLV~ZSwkmX|a7q*Ol0*r(NII zDtP^WJ>!~f%+}54iGxUuioh^T=Hqx*T1U$L{$O8k*0O)QW()V-HdCZ5KVY9J*Rp@RV9li+Q=09f<<6aU zM5W%%ZNKuQXKW%sFkkZNc%tRPqivv;c5fOJJkhw*%xT9B^t@KkBG@j+{};@~JwJSKusdtP zGT}FA&(^iSU|sOKL9jlE|MZUUEu8YV54^Sap@-kP->aMb{_&d^T_3~*+a0tCVuGI_ zUZ+Y>Z}%Zz3x4qz2Cr?TCVO6LX(~NtQ!~$OJ;9$BH}#mQ?T%+M&M3b&d3`f(NvFx} zk(s=qIWL#XEJ-=z=a~0oGA27WWvVMu{xH%eKPpu^|J`o(VRLD>+kU_(&&%b^WdFq* ze_Q6H{SBMo=G-sswNb`3Q#0jq*_k}mq%U~*;nicNcIWn-d){PYE;QLpXL`$EYECw3 zXL2gzIOEdU_O6t1i<9P74k}M@CtonxtUs!2MRtP!(qg)@jn{2V#ovN)X7jnT%@o({ zmCKr$9meq-CzYAtbmoosm3hWwvnHDg%ro94ZsyvQsT_8e`R*3ij+;AWPB~vlrN=uP zKQuZw$uXI9Zb|xXWclX}{pdJm_(^)`2! zls!SnTt7jVNsn_YQ@l(Q-dp9?bK^I4+IA~V>Bvn<1+(~?p6o$x&$x7EM^nA1ebLvl z8E=n5!MJX2f_Zg(=Z53`L8rX4R{i*~Ry-JGpC8Dwj@8PWvy8Il0VX+0OV~Z)&bt@ku{{zdOd~&TvwuQ1pMzmKU1Z?pb$VGsZC=oS7N5 zUT2pv^FHm2O?%x1lP;R^=9tv{{4Y$(%Z;kK6a6FFcl7t}IM1D(HJS6Ld#Ur5O!N-T zX49r|^r(qGxk+D=YBzJ9IUwujO;5Vp^>fTv)8@L)l6kqdZg-5CH+zzC z+VWG@HaEga(>;r+l&Ic!cglj?k8 zhilqXse{bVXHK}Q;xy*wSD5_h+RD-XWnt>PYfC0~*1lc8nmT>!jGH!TH@m{=DrX$C z%=b*72=QX-huJI>Hv3s&VoEiSCoVBE|^}DW3kFR)}9=+SgMw!_?Oa1kx%ir}>r~5Vkz?`y|89&uHnH{oEZ8!C>R4r?Y z#!WdHGrPE8qupv9ZYJ9?_1ctEPER*WM)}?PyL+l@GGkI^qnx>8;rW@>qq@9|an^Hk z{!!(cdH$5iwPlPyXFcN?f1qBl?>BXK^DiAalYZ2UPfzvdRoXuZO!ay8$c#--3dp;5 z8s*j&OO-3eF`FA_gQITUiKf$dWz&|e`HM|zLZ%~o zo$-Hi9sWES@7tNG`ScurMy6Nh#_YRf{_XP|e~8tdgWuf8*&}BrY@Tx+&p(IxBMHuA zV@LUKd`lILKMmjY@a*E;)KqVF#*8!LGY7e6j!GXk!Se%CZs%0jpJW}|Eis**$@uNe zcxT=8EH9g#o}Or&RF{87$)+-%-*U9s%)21f=FT0nbZL7kJ+8LZjEi!`o>EsPm8m?k zUaEPR?ByTH<5!qm#&xpe{H;Ad{he%^DVXz3>Y}@scIE$TgKN6Gk9>Ih&cmnAF{4vG zZEdq%e;a4Ho4OC1*Sr1v^Cr(v@8oZa+tzt_&NV$_vKQr+WQ)!uXBXEy(qvq-r4JJ9 zt{rZf>>2;C9+S(YgX5!Zu9@s_>0Wcq2HjJQH_Az;vOD#B!!t9UG4oPce@vbkf0j?< zOPN}Bg0p+7JbuY!;~)P1OfX}%Gvgk~rL)`k^SNu>#_p|tl67-#PcGNdRr3#E*DT5T zLfMQ{Hfg_}a*mwTX?7W(O^=<~lks}|)8yQAc8Z&yd{AoaIIo<_?(2D1jLsEJ+AXKX zO{jNH+}GbTW4q3pT$}3n&~0XlH@CgB-EsCRkLlg5(BYa1*H5UrXZ&cd1!c40Y?Ja8 za_azc`1WNL?*DXDJbPVnaD z@9goz-Gbx)bNyu36l>}E1)iG@t$K+@9|HJv!8y?ZgY%}nQ}b8$jtT3q-?kA zWhRvT5gzG`3r@y|O!U^7v7wuu>mMAxv)jGShU3d-BmdXT>?Ourky^2CBe!~X`ka(M zI?s$U;~an6J6>s@aGAgD&tKhbeo{(RE-<+x{ByP0Am?4vKE`p=<{;y3xp(^yoLtTy z&&165?$qe@(&=nwq4CLbX70GL^E&)F>v>bmB-6S3oKfT3+=*F#UwiY+RDWB|*EgP@ zotO2zLe5Od%--8Od91smDa>?JGqT=zvwnwJ;wze?6ja^{Woy3Nvk%{hL!Nssc^+MmvjXS?0D z4V+P9{fXxk9(s84#+y4Ezc-&b+~gd0iZP4)x7wuJJLX-PI{eeK&U8~bcT_OHzPoMP zfvGvid)J!H3x(Nj*-X#8VB@?wxlJ;2KIosxbN=bEC*_%ar$02&j4RIHAnlm*XHGi2 zZTEAtZLa_J4SzmlYpHB%RIaUk_5|afLH!?pvz?NE?#P;Phns@aGiKtFV$r|WdHy}f zGJm>G@aI~$zf-csd}_*>lYcX}$LmUUjN9G6@)-B>oVhmTm@_uW`R)DN4&!BA|FFrq za~*%c-P5)$(T;hpOfwtUf1D!yHh83Y&pKu zpWIU`6Vm?a?V-v^CN(u(oavh#c0c8d9ArM>PfC;ZPXG_4x-xAwu50|?+}W92zAHOwnSVt#bKdW6oa+2l&qk?S$G8L1^LKoDeA?tHHPdeL z#xqN1Z*f5T_?@RN>E5<$lXJK4=*_G+e|*6yEU28>e&#tQhsbZ%$5t*!ozsaa=2Zl`%>^JNd8*Ewn%<9BE}+_o(%XU=uI@)>_}WgpM_uOiOz z=UL8dvufjKo^or(O{daJb4xS+4(=M~v^B<=)W>h=S~jZe82>)cq~>&{x*Ba0Gww0| z&F5Hil9^d>bI!&$W;?s49Pph9?c13NInOozS@YVQ|K_Ds#y{ds*G;LOGwO3EbeU?$ z#M;K~?)=n9m^)x< zI%CZFnJKx=jW^xzF*82BW#-tNIVajxn3(oJsATe>s_GGHzGeWJdX? z5mR!wYqR5I_I{1Q0*~0R;X91ls=vwt-DR009IL zKmY**5I_I{1Q0*~0R#|0009ILKmdV%ZGpnG_H%U#^7%SBsUIYtDf7Tv_OoUdNd1aO zZT%3bA17rszjNsG2s>}IakE^z{6kXiDdqlBE|BtYDgRr_Q>6T&lxIs>mU7Z>Z2uQY z{W2-9lya$**GYM!l($GZX@=dd+oir-%Kw>emw)FEw*Q5)eDoh}{Q;@3l=5LIpOEr- zDW8_|IVoS1@>MDSAmtlUMrX-q=Gg5xUCMK%{Hl}}OZjyvmr8kql($IvEh+Dn@_(fK zu9V-CvM%M1qFrJb-?wuoF{%7_oJWN<==Y5maj(g zoxF$b=h5HU^_T8$>o-e(_ogL}#4V9hx9{$4?fT-$`R2U*t+em$vh}ZI?E1W{-)yX{ zm(Q^4A8NDphO9qN`p-#S^Td_f{{_-y?M+_5acSr2j3XKexxWfAW{Mze=~QuhMu~|DgC57TNXd?rA-8 zlK+&C*?Rp8cK!cKdr#^Osn<@i>tB|7QR*k&E%_JQ^}mz#)z91dpSAsGZM{wMyQkUu zHnP5OmaShU^Q$QRjh6jg{ES^cR@#?j`{VYTE^o+u%+IsqoOz4gUY&p2%5sg5mUn$b ze9p7^b^lbP{yu5%U1ZnqD)szVZGBIvyBFGeLF$b!*}CrEqV#u&tS`!X^)#~n7+GJx z*tS1W>Rpodi)Fnl^)sYixYWi+#~ZCbd7vG?xx&U@BJE4p+4>dQztopXz4uzX z{syVnr2b8fm-_8e&q@7KsW-l1+utqq`qyoJrPQmk{ywP}uCnVdkh&x5YZ`xzUH^#G zi&9@D^}N)dS#IZtk@_pYvi16vw*3#JUeLO<_mUpV0_h+>Jzp}mNX1o1=m-fywTW{aX?*HnI zw!WU!OH$uJ>UD{K>2^E5!VNaQcRkzQyWZ9(NxUodBW|+s#j0H&-M-ZlU%1zP{(if?$4GgYlyhYLUQ!+@%lDP~fl_{4%EP2wDCLP#o+{-Rr0kaROHy7S zm$$a{Iof_JTi;vjQ*3=NsW+s4fYggy+Vu-3+woMku=Rsw zeMPqC6WU(-j|}G>)%QyLy0dM+#}sV&Us9eZWkViE?4GjgPnG5Re9g&HU-^BzzxLM0 z4f=RR^6VnzXC+=AH!qR;neu$cSyDew%HPQ2@>irhPT~%!+k9>EJkA9Yf02~wjqUb+ z^)(xJl`L0?;?Xlg&qwNVErUO`3w}mFZrl5Rh}!&dE{S{}i!6^8+Wzj_Z2Rc?-p(DH z`8CcPZMQcX=jPuVid*r~f#aO#!u@s5A2_b`^KhI#&lY?xOYq}96ds>$-*kDrrJt|v z)dr4xTJq}WqF3%8IPSy+cKh0FTI0mTT`%L+?QrfLxWDTTwf*VmyVvd-IPNuh-bKe- zxpUySS7knrk?k{=4IDQ{p10F5EvugmJ3lDn)%AVlJ>j@xWE_3#<(JCs;kdsYGVuN_ z+!>Dha7=&sYr}D0jOnj&eK_uln7Hzd;W(YAx_>KI4ea;5gX}nhKWhzsDwhu&r~9{W z9$X!cv#sC$U*mK;bbotShTCb}`{X>Kam6bJj*HG8_v-`4-7l|$yuC&CH}{8Or)Of0 z)86iITq7o~a&|cG*D-O0bHj0ej)^m04#$nw=jkE?DXs{|y(=cpJRFYOA||f-RU4;i zbic&;I9G)Gjq|Br6^@JZDSjaw7w41n!f|mvl}EyHaX#*2;kY=TT9=I*s*krc+;5yu z8yEGN5i{T1Z-)E*P)uC!t>L&m zW8$j!hV$!oG)^0Mxz2CBztHa$@di1sX}{Wj_m2;J9Wc+$4Ba=J5EECK7{0$~k2x&sya9rGVuku(pF7CQ# z9u3Fs5|g+1a5(NGF>$pO;kZRHanApQ+2abE!!FE3P#oab=oZctt_Z?Nf5suUIf|lB!Zm-_gIJbt|Y1~3N?lsQb zGH~3-#Yy8DHxC@QFcMe0Y2dh1BXN}*2aY>C66Y-&IPStoT=9m1<8NGU2#5pHeCu7w1!XIUE<~)A&_5F3u7bK$r+pZt2^xHuo@)o@&#Pw|y-T%1quIyP>o^QS&J+;5yu-U-LW z`IO%sj*Ii@-8dW<=Tmtt92e))_~LJ1kJA=@TodP0G~u{7pWYAI zyhHV=9~|D!IG_B1;kY=Tav>ZS=hM4iI4;hok`0eL&Zp5Hj*IiD?ql-~)u()Tcst{K zdOsD8i}NWQ8jg$eseU{h7w6-w7an(IPR{PxcpIJhujdyug}9* zB9Eh@+oAQr#^3l_czff6$g5RQ9J;`$y>-LdA!NxFaH9v_*{h5L^?KRAoSadGDd_mps4 z-1(t+QaCQ|{7~BRy{iWI5_V0xmqNy?|q@V zqok}y;^yi5M zawLD;^Sxdvyq#CYY-jI2;kZx7oX_$f4#zzl(_g6&j_Zi&uRK2-_kS_{6%Go=eOI10 zQ>Vi6@Oe*{PmIZ%KP24WFXTL`?dtao+@JpaN@RMO-Y3KTy%KZ&uD@#I-qC(ixV_$g z==L~~@kQHfUE}-S$D=Ex>^*eg{PmdpeXk?8D1YuF;c+gH*+1@raNPG|;>^bf9#3`g zz{}M~ef9pvi@d)f!q!*S_OHdfzohU&_&oTLnAdfxk@@nmnDap?5*K&gGMk3456{Z` zbNV)1eyPp~$Hl$wRCp%rA9o&dP7TM!orh{Ch2#D&?)Z2q9QTiyIQIn`7agDOpSa_+ zRt)#QL1W3jsxVU*)pAqgaZeI4z49A@qbN$J^ zFC2GzOk5=rw@1u> z-#GP`W5WH#9q%TxeR0RTk?qs&b^q!6J9W91UL=3qYgPof1YidaO#;VPl^}=R$`AOfm^<2T$Pmp?je_Pk*CH46)eO^tUhtub~^!Yn| zoiYh2^?l6lj`sUQ zH?qyl>dq<6-!_rD|3yH}AASGW>#|vr`@ zJ^w+Q@2^rnvTsL7f)*9}eXP;$dYArvRp3`W`YBt^dc0YBQm_1aWwV}t>9J;6KGw!n zF1PK9H@?+e?wn=I!i?`V>$THtyu0^zn{{s!>Gx~TH0z}!9%+{4SL)5O@W~%GOUL^` zv#ji5<7>M=-K?8kZN0w4#^sK&+hI<&_41{rIS(59+4fG)L$=+AY&-KVyWaC`e(zyh z)^^D>+Z7)BzRiD=jN>@lZ}A5=SGXe{tq|X<$q_LYPZ>O7rtQQ^7qMj zK4;_VcYdcCUp`I7d&7^K_1aZ-d&<|@_>#MBbGvFUSfBFEKWWB!_etKBHoo{to40Yl zZCBsgj-zmhY*)M8AJto>{;+LtR@;8fFC_jx+3s^~zx8dbOYf&`yT*Un{=F5pEEwr$ zsa;?G_>;};%gwjzbAPe@BnRmS#+Wl7j zo^`4(v-ygX?Do~}5r@^bpWgdzzn<)e`q%9|%}IVo_Gdx7YLd73S-X9umu>sfrgq-A zGS8jWcDqVHw)sjY*>Spe+W5k6#q)QzUfx;e>rS?;N}PAHU7tVLjw5%8?WeMv-A{$v z?Rw`Has1+{rfa?Mk7n7JZ`e+U=k@HHgjHhwEJ^yPrwRvk_ zwEMYIvD=xe+T{f~FS*y+ytM~yKb6bu{Ha`E=cnc^-DTT36&shY%6Klf$AOXKqH&ka zQ+>CbhwiieIA_@Qr9-9t)pq|oFI$K5;dVRA^QHb9TUM_ThbJZfQro}te|CPJL(ZqFm`m+g5jFUt#ZzAx@-x8D&z_XZoM=b5^=*Sc)pqRbcfSi60_ zZ^?MCmvO$<);y10c&^<~71>YT9yU)w=7sL(@^`JHk#Q7nmhq)+ng5KPkG<aJqjNSul+y3Q; zHnfk6ato}VuG^|=@4JiZg!6A9@x{-F_v^Z{ywMi!XS(#`Z5Cd>i!9I24ZG|r%NqyS z{iWNpw=A!Y+B)P>kmbc+c7~QO(EjfUFF!<>ksm|FJ{6Hp?*H_ymcrnwDZ8f zAAT#?ro*Hk-M+OgKYy3aQ<3?q_t&3}$rJ2f{WqN4J0pJo-A?iWcW-NPl`B(f2F0dOSaqJg&@xk;?Ou z`ut^N-F_hZr~23Md~5ab^TLz%IITWr&s%!@ysG1j9E`y+8eA`fayUPKm;P#zJQ?}? zu4rFL^YqM+wMdJ4SABB-{xz76Kx(nPCus0BXF8yxcjFh z{q;_>d2}AMdOWA;cIU%+T0NdKB~R_w_W05L)9UliS0qpVci}vj=r~)ne{TFI<@t$x zzKZw9@Oh`z*M+K#r#!-Wu8{L+sn?E2=k=P-qe;$hcgc84>u%A!-_q;${Vkpc&K+_e zJHmPXSNG2d=XpW$cq5$WMaff@Jo-HMt1aF?6J`GtCfRwQ^DQIGBd3GDd*0!muXm9C z+!4;Rq2%e^HaxFizj@%-pMr4&V;=5!HjzA)9c&&wUr+jGe4gMQe7JeGlsu*FZ61An zqt$t~mpqLTcH2?%I5|5WeOxk2mbYrY?I-f`iS$y1c$ zN9S9s$5WC#&PL(!oZ4cyF6pnliOr+epH`3OY~5~WWXH3q-N^tpL;+!Pks$~lHAAdDg6~CkIsVwTb$=u>92P9 z$d0EZc^Z;uq{j1k^*Lf>$I~Tw9LY0M6+%7&h6-&UsL^J$y11or`5;L z6_Urh)9#-){evUeu6b*?zfLlqrIM#CdGxx_>O40}o{Hqrd2p*NkDBXPhI`(5So$m7 zZ{2h}KWcHFm!!Yq2NS^Wt=h;B=R3wkOZ6wR}xY07{@w1ckR~%*C^l@;j z?-O^`@r-bu_i7$_|CYLy)^H!6LCy_i%nzX@4KEdGxy7>izSSyPvinD` z+pQkYH1*jveEhUJ&o#1tD)N38b!+weYTm2k`ICLW%Sh$9N$2$l=lOt)Cok{g8maMI ztjEu5)=kf&YkI#}@cM$D2cySVTzxkgk2k`3_Le-wZ@>NgTChzA>HLV>z+o=e?_;iB zY4fN@CFXu~nBDh?385LKB^Q`H8s(Nkc+t?xevcCNNcJ4`= zN9WrYV)jpvBd{Irb$h~Z?enm1WIStne{YgJm&$e*B6-&2mL$)?@^=|^`MVsQ2W!fc zWdB^F`$7I5W~A~wFR$0@-{p){o~1G#cZA2Y@pkflt?R?*gVilQPV+k79?w`YA{(fO7+dfdt=Oj;6@~B&@ z^E{~cCh_9huXiFRkwMOJVWg-F7C`xHjjIcz0T=&w)#AJ z=qq*}m>cci?dtih6f+(*)9s1d{^;`Ub-qa+-9KwOj|R5G&9kHA$&K)MW=S3=lBd;= z7slLg_fKPD_<0vs@<*MM);}iwjdY$PBv1XVw~xbMo69ltE$Mjl^Y`3Y;qyVOk01S> z;_9X0=M!r!9?z-brl04a$It2(=UF6q^z$F|{MPC`3nfopK1X7t#&fdd(a*^ksquJ{ zM?arKA8+Sw3txYd&S!0Rq1?aKb7egG`5>An?*1(}PbWEF_edW7{1ZLj z$K_Fn;g+vWviqU2uKm0W9Zy;E54XR#`0Lee8=FU84_+9ve}eJqzqmdJT7GLxo^p%x zd|UD~X4>)m&ELtv_NRhq7;inEmSZKBUlP_p=^9ae0F62>uN>&)g5% zTZzZ%4<|8=2R`?>XG!i4L1bKX*y5KU1rA zc{n!f-1(d|AfoM(Y9*MUo!G|w^8 zUqi+d=eD-vIZg67G9Jy->hXL<@_3R*&u^{HbFt*9_t^b2QhBbFJmqt39zDOcdOX)j z9#`D_&uebp_qICEZIY)bd0v-1(c?#_cQCJ$+*ds)dGb@NThLGN^O!8RwYC5MIdyw? zcpglRIlm=6PJbbJ3Y&%V6xNU@$#w1($y4bJ=Xov0Ef`OdJh^-Ab*?zo=F#ii;+XLy zy}zC$dCZC1g{}*gm^?}M&$}g0ZIR8R$LSQ!ADL=N^Xw%3)kiqbcjf-NA$bD-;Kyz8 zcs?rq^?p9=c4#C|buAzNoEFKW=eO<_=lQMN$CuBwl`gS)^n8%M&z>hDY5JZ+3i1u-=SA5+ zwX1C&y?@)J#d+Q<{kb=eY#vv)`vIFr=Ubdx((~vZlBaQ7c>nC9`6D|a={)$Py4_*( z=y~+W7Uwxl`txqH&xZupuwV#ZjL8!j-jJ&&tZN_VjC7tWWV?Gm7`|?|`t_e{)#o|8 zf7I<}T|Q))H3!}&{kiY8pTDj1y4B-(P~F<>-yLe6=;uxrZ(qCT-}L(&^>fknytOjs zK4i^0YgqepdH!I3`?y~BPpe-)eMI-qrFQ@5c%EwUJov{E%Cw(z`uy~Xn&Ew7_jr`hLSbY4%rqqWbwbZ8!#2YMgU>iHI(@%8({pVO}E zbMk)7f|R;ES{7p3*YCIe4%U8c^RI3DwcY-Lj5F#|f547EuYOua0td?<34cd*u=eV& zWwe8l_IpRZyTkKssQLRGBOMNP{Mx=A$v;^8NV<1a2Wubat{oK~3VZ1C`nSXLr}vJ5 zp}CqHWAXG#4jD-2lyEIw8-4o_JF5&g| zpoICV7biP^o|G`(EXg<2<6Ps;OPKG?%ae`oriA(KOz8RDg!yU-^F5O=-@^&>y(;-S z%YSYuZn=d@f zZ9~eEJb&h%ZI>^X<8Soi1D`MA@@{{9IDV-4vR_&A{k--Q&AT+=_@0r+)6xF49+&S% z$@lF@-s&Kp`9bzyT)rnGdG!5fgXOz)kjF=H`F;|~ryhgl%MNnBj?4GENIs1lEMHB| zXSX~Oo^QInapfD4d>S`czDbgA)GuwmlI(A7A6Kp~@w$GfV+G|BmF-c{AAj%**rT!#!VrE@8gqa(|thZLin8 z``hbAMM~Z7X!+GhK0Tk6q+Q{o(vQBb(MJ7oG;FZ(m1Y0cK0Ung zJ)JOLK|Cwssq;*?H?Dj>VZN$d-z(y2M#p6cFaKS_d{g!D*f7WUw}kl`GCn6}|2Z+o zefC!aU!Mn?FLgQo8YMeloG@E#ZIgug7V3PFe6a~4E2c;C>G9qx$9rjkJ-%!E*)rE{ z%ZlXF?zEl%KE>06W%}a1uGjKs>)H1sXkE*}@|9vi;z7OYpwN;79w@^5TT~7DVQ!_UET)=G!i22VR>nU++7|*G#GT zV$zuh6XxrQj8FSNLI$dJ9k-T0PMGh!LGo>+2_oh166W(F`Lx^B@;;)t`8oE*fzQ{2 z&Cl*Z^2Hs0+a%2QK+NN5oiB0Y+c{yr+93Jj=F2?E_pwOc!91r7k}uA4VI+_4_rdag zE$04OJ@tExwLdMFB+PebWPIv3SiTDr=35@gr`xZNar33;gUPP92gMu@IzG)GEpL&FG2?sZ6(OAQ*+?GU4};}f9D6@uuMcq_;P+xApKi@y`6k6Y9vf_YZzar^ zOPFuNmy(@7CnU@_En&VpBgeb$cb%X8-WRI7s9AnM@*Nz>JJ|l4J4n8`&IG0)HI{M5Xf&z0AA?~(C65gF%T`Mk*d)P6Oe*0s#b(PMuwo*x37WcJ}i)SRv*SPBK=amM^ zXENb@TH8BezH-0gA-JXP?-|S)z0cM>S}shOFL%M)04+W#D`_C(M`axBr5CGA=Dn z&qa<0&HkN)`IZghsr^UG=M&~zDaU(o99U0#M$!G}FLt82f_iVle2WL!e@70K%wLvW zo$UNskn^>UFM7O3pRsf0e6}p|{vO?|!LCP@exCgwd+CJHezeTX>m?f}9ACBH>s15g zi<_U@C(P&e+kc~U5PsP=Ut*rH-y>nZLc)B9Cd{{BkbGXhG57!V)P(sK%k?Ojmx1Ry z=cn7Bxi97U3G>a9@o7HoQ1_p!3nJyRg!xK?pUK387v2#&vOzy36zzo`FhO<%;}G5M;4 z{5|Eee%FV-&ir2*@_KmxW&M9YF=4*tazC%GMGk@2jEY>$2qW1PbgDJF0R~9J~vj6@zh8T zhr|1g57N5qfWxHTE@!Lbr9Mh-LQj)=MlMo4+P?ouJ-Si->U+Ls_kZP3`#z<^rQR#` z+$Zhwf|SQ!sc+^}D1#>oD8T zqf+;zUXyaQl(P=EaZhTT)L)ePGO52M^}5u@{=vHBjQlBUF#Zp$J?3HqqUW zb|opdljR?jvL?%ON7=Y}QeGgP#)nUs@`vF)l-pC|Qi zOT8@hN2I=7>e+?1-3zjOfz&-Ie&4V%9pb^SaK zPwE@W`l{47m-;hOpDp!0r2M3m*|Ti_^Q1gdmR~CM#Zs?J{amTPE_LT@+x{D}{2nQb zvixzWds1It>Q$+m9@{?Z9j)v0n#}l%x_;XMju)^_Qi5U6#8K+I~DKP0nuL22$2# z`BbTA|HsC=QZAHI=hbql=Ouo%#!Ed{v+WO-x-0eLr0z-m^HN_a_1p^EZkCiKDVIyB z+y`du_IFA7Bl%u{Z%O@EQf?__yL>;}bSd|hQu~XR(eFpq`Pb_2E&Z&1-^5el&*h(g zlb!d`?MhmY`#!1#EzT47eN?T^6Zd^ot*ifz0Z7P+`#W+NIDPn z`w)BIYx4w;JA=M{*y23;y>W#ZHjf@ZFSa;O-1ow~)#5yH-;>+w<23I3pC^rP?L3J4 zo|{(ZiTfPXR=dT0Pe*%;-SoH{>gQv{eGc8)e!kdH+m}CXk3ZdREsNXR&)LQvc3H%j|}>{RAmHrQA-+ou%ALzQ_AT z`Tp&8sqZ1{3sN2;M@6YXO*X#D*F3(@-eeoB!x9j!3IQo3m#j;-Slkb(f-ghsQx+eoW zUh1yY2P^(4`$wPUy!IP_2<5&>%C?@)7Rd+8C^fr{-f=*uHUCrxWeYq@1ev!K}_m9>$mHAe`(#C&I z)@yxfq<&+h{`E+I7fHP+`45cL7e(4X6{(+hM|k@liqspC`ZIE$uj9Ewj;~(Hzf|fa z8Q+_ChWo!!)@%De$$A~Xx@%ckYMr!R`i8BSuCZlNN>@sa)3WsA$5$;`WK8X4DI2yd z?)JUr^1}1BuJO^b(2#sTv!(mIEz2+1vi6)U^HSDjeYBtaeG+%Sw0}hEkJ>UCuXQ~i z>v?+dt#2RiK}>YLw%b8}k#hHxJ+J8N_*&|DJ`&znJzc&Z?JOzv`TXeevC=}<>+|aR ze7(NL7|o~g??1?H-)>Uw7pbT80@b%=_LmLh?;|wN;nMESeQbYvJ=V7mMf*9dVB;3b z_&$E1)TKOC%Cn`sP|61nv2j;RT{mmRNA2?Wy=M2*Yag+7eKj>^7~1?D z^*dG#xZwDSHvYe*Y|Lsj=lOua{=&vwyS}#jn&aZyvm5~g5I_I{1Q0*~0R;YY1+ryt zjgL3}b7lTBMjm~9-*41Tzu);40R;XD0{VA|!+qW<>85GEr0r>k00IagfB*srAbh!GTX3(mkGO5B8N1za>;LJ?hq?VN z-@boVe)1&Sb!+JsZ62-nZB*=vzKGWRLY`!fxZxHEFb|jq%!6ce05{xJ;NRptI^@Tp z|0d&6{y|H|Gbqu30k{pwGU&p86F1HWgYL<-3-~vg2SXkt;~6qlC~z&^qQ|M$Lpg%V zTIP92e{l=Q6DPCg<+!ccCvGinAsIdXlfK>+>Z}zN_D@KW{S)G7eTDg!%oI1=0s-a$ z^MHAfY!0-*EqZ^@7PloFr*SfCUXI(Eed5;Q7ALdj<+z1>qUX0*p4b(k?$*9So@9o& z;T8xm510qcgJg36H{4W!^QcM;uf%P5UK)*?N(`^WZFpW9jhjjguf%P5UK)*?N(`^W zZFpW9jhjjguf%P5UK+iYZafdGkRw#C<^CD+R*`4ORG~oJLL5V`;1;6dJR0I?eTDg! z%oI1=0s-a$^MHAfY!2Xtn+k9qRf*x1xDC%sqj6J-;gz@z&r73mQ;Fe~xDC%sqj6J- z;gz@z&r73mQ;Fe~xDC%sqj6J-;gz_Bym);f#L@Z+^DUVvZvRi*I3FZqA9AaIlX=ZN z81f!X7Wi*64~9HA#xrE9P#|t0jv-fY8!}ZWa4p@qE`&5&Us=ojGvp2WH^~#7Z(4uH zIR9DWc}HRvAb0jP&4kTC5!&^#?6QkKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5J2GnW7mFSqbkAxfWLx(pfSaZ-hAZ>n8HC445k=kIC#K#@dk_Clntb9cDIO8 zU>75Te-cehyp|IjJTzXoXu=H-n2>nTo5X`2G|`KJli2N>ufW))&`31hA4&Ok-h4Cj z-f#BJyl$5O0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly 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 zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0Rn%scW=RV z-3NWg|B4|fGim2^!h;I!bfu<6G>N-y`EqO$vgGSA*wKkEtx7N1p>{_MJnUkpRZ1>P zZqusw4S`nlx^qQ9fWuutD62p_8YT~Ch8GPlU5|R`OgHqVQab68{=a2QvSb|}S+XsC z&W!av|FzbC|M&Wwwa>-c+J^uE0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs 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 literal 0 HcmV?d00001 diff --git a/src/ctrip_swap.h b/src/ctrip_swap.h index feb467e5bf2..e720b74f101 100644 --- a/src/ctrip_swap.h +++ b/src/ctrip_swap.h @@ -244,6 +244,22 @@ int getKeyRequestsMetaScan(int dbid, struct redisCommand *cmd, robj **argv, int int getKeyRequestsSort(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +#define getKeyRequestsHsetnx getKeyRequestsHset +#define getKeyRequestsHget getKeyRequestsHmget +#define getKeyRequestsHdel getKeyRequestsHmget +#define getKeyRequestsHstrlen getKeyRequestsHmget +#define getKeyRequestsHincrby getKeyRequestsHget +#define getKeyRequestsHincrbyfloat getKeyRequestsHmget +#define getKeyRequestsHexists getKeyRequestsHmget +int getKeyRequestsHset(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsHmget(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsHlen(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); + +#define getKeyRequestsSadd getKeyRequestSmembers +#define getKeyRequestsSrem getKeyRequestSmembers +#define getKeyRequestsSdiffstore getKeyRequestsSinterstore +#define getKeyRequestsSunionstore getKeyRequestsSinterstore +int getKeyRequestSmembers(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestSmove(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsSinterstore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); @@ -262,14 +278,18 @@ int getKeyRequestsLindex(int dbid, struct redisCommand *cmd, robj **argv, int ar int getKeyRequestsLrange(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsLtrim(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsZAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsZScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsZMScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsZincrby(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrange(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrangestore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsSinterstore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZpopMin(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZpopMax(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsBzpopMin(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); -int getKeyRequestsBzpopMax(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrangeByScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrevrangeByScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsZremRangeByScore1(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); #define getKeyRequestsZremRangeByScore getKeyRequestsZrangeByScore int getKeyRequestsZrevrangeByLex(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsZrangeByLex(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); @@ -278,10 +298,16 @@ int getKeyRequestsZlexCount(int dbid, struct redisCommand *cmd, robj **argv, int #define getKeyRequestsSdiffstore getKeyRequestsSinterstore #define getKeyRequestsSunionstore getKeyRequestsSinterstore +#define getKeyRequestsZrem getKeyRequestsZScore +int getKeyRequestsGeoAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsGeoRadius(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsGeoHash(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsGeoDist(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsGeoSearch(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsGeoSearchStore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); #define getKeyRequestsGeoRadiusByMember getKeyRequestsGeoRadius +#define getKeyRequestsGeoPos getKeyRequestsGeoHash int getKeyRequestsGtid(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); @@ -291,6 +317,9 @@ int getKeyRequestsGetbit(int dbid, struct redisCommand *cmd, robj **argv, int ar int getKeyRequestsBitcount(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsBitpos(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsBitop(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); +int getKeyRequestsBitField(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); + +int getKeyRequestsMemory(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); int getKeyRequestsMemory(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result); diff --git a/src/ctrip_swap_cmd.c b/src/ctrip_swap_cmd.c index fd84d8c10dc..6d6453a6b23 100644 --- a/src/ctrip_swap_cmd.c +++ b/src/ctrip_swap_cmd.c @@ -29,7 +29,6 @@ #include "ctrip_swap.h" #include #include "slowlog.h" -#include "xredis_cmdparse.h" struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"module",moduleCommand,-2, @@ -96,11 +95,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"bitfield",bitfieldCommand,-2, "write use-memory @bitmap @swap_bitmap", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsBitField,SWAP_IN,0,1,1,1,0,0,0}, {"bitfield_ro",bitfieldroCommand,-2, "read-only fast @bitmap @swap_bitmap", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsBitField,SWAP_IN,0,1,1,1,0,0,0}, {"setrange",setrangeCommand,4, "write use-memory @string @swap_string", @@ -208,11 +207,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"sadd",saddCommand,-3, "write use-memory fast @set @swap_set", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsSadd,SWAP_IN,0,1,1,1,0,0,0}, {"srem",sremCommand,-3, "write fast @set @swap_set", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsSrem,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, /* smove cmd intention flags set by getKeyRequestSmove */ {"smove",smoveCommand,4, @@ -221,11 +220,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"sismember",sismemberCommand,3, "read-only fast @set @swap_set", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestSmembers,SWAP_IN,0,1,1,1,0,0,0}, {"smismember",smismemberCommand,-3, "read-only fast @set @swap_set", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestSmembers,SWAP_IN,0,1,1,1,0,0,0}, {"scard",swap_scardCommand,2, "read-only fast @set @swap_set", @@ -273,15 +272,15 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { /* (zset type) write command flag should be SWAP_IN_DEL, Because the index (score_cf data) needs to be deleted */ {"zadd",zaddCommand,-4, "write use-memory fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZAdd,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zincrby",zincrbyCommand,4, "write use-memory fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZincrby,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zrem",zremCommand,-3, "write fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZrem,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zremrangebyscore",zremrangebyscoreCommand,4, "write @sortedset @swap_zset", @@ -361,11 +360,11 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"zscore",zscoreCommand,3, "read-only fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZScore,SWAP_IN,0,1,1,1,0,0,0}, {"zmscore",zmscoreCommand,-3, "read-only fast @sortedset @swap_zset", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsZMScore,SWAP_IN,0,1,1,1,0,0,0}, {"zrank",zrankCommand,3, "read-only fast @sortedset @swap_zset", @@ -381,19 +380,19 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"zpopmin",zpopminCommand,-2, "write fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZpopMin,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"zpopmax",zpopmaxCommand,-2, "write fast @sortedset @swap_zset", - 0,NULL,getKeyRequestsZpopMax,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"bzpopmin",bzpopminCommand,-3, "write no-script fast @sortedset @blocking @swap_zset", - 0,NULL,getKeyRequestsBzpopMin,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, + 0,NULL,getKeyRequestsZpopMin,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, {"bzpopmax",bzpopmaxCommand,-3, "write no-script fast @sortedset @blocking @swap_zset", - 0,NULL,getKeyRequestsBzpopMax,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, + 0,NULL,getKeyRequestsZpopMax,SWAP_IN,SWAP_IN_DEL,1,-2,1,0,0,0}, {"zrandmember",zrandmemberCommand,-2, "read-only random @sortedset @swap_zset", @@ -401,35 +400,35 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"hset",hsetCommand,-4, "write use-memory fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHset,SWAP_IN,0,1,1,1,0,0,0}, {"hsetnx",hsetnxCommand,4, "write use-memory fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHsetnx,SWAP_IN,0,1,1,1,0,0,0}, {"hget",hgetCommand,3, "read-only fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHget,SWAP_IN,0,1,1,1,0,0,0}, {"hmset",hsetCommand,-4, "write use-memory fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHset,SWAP_IN,0,1,1,1,0,0,0}, {"hmget",hmgetCommand,-3, "read-only fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHmget,SWAP_IN,0,1,1,1,0,0,0}, {"hincrby",hincrbyCommand,4, "write use-memory fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHincrby,SWAP_IN,0,1,1,1,0,0,0}, {"hincrbyfloat",hincrbyfloatCommand,4, "write use-memory fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHincrbyfloat,SWAP_IN,0,1,1,1,0,0,0}, {"hdel",hdelCommand,-3, "write fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHdel,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, {"hlen",hlenCommand,2, "read-only fast @hash @swap_hash", @@ -437,7 +436,7 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"hstrlen",hstrlenCommand,3, "read-only fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHstrlen,SWAP_IN,0,1,1,1,0,0,0}, {"hkeys",hkeysCommand,2, "read-only to-sort @hash @swap_hash", @@ -453,7 +452,7 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"hexists",hexistsCommand,3, "read-only fast @hash @swap_hash", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsHexists,SWAP_IN,0,1,1,1,0,0,0}, {"hrandfield",hrandfieldCommand,-2, "read-only random @hash @swap_hash", @@ -804,7 +803,7 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"geoadd",geoaddCommand,-5, "write use-memory @geo @swap_zset", - 0,NULL,NULL,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsGeoAdd,SWAP_IN,SWAP_IN_DEL,1,1,1,0,0,0}, /* GEORADIUS has store options that may write. */ {"georadius",georadiusCommand,-6, @@ -825,15 +824,15 @@ struct redisCommand redisCommandTable[SWAP_CMD_COUNT] = { {"geohash",geohashCommand,-2, "read-only @geo @swap_zset", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsGeoHash,SWAP_IN,0,1,1,1,0,0,0}, {"geopos",geoposCommand,-2, "read-only @geo @swap_zset", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsGeoPos,SWAP_IN,0,1,1,1,0,0,0}, {"geodist",geodistCommand,-4, "read-only @geo @swap_zset", - 0,NULL,NULL,SWAP_IN,0,1,1,1,0,0,0}, + 0,NULL,getKeyRequestsGeoDist,SWAP_IN,0,1,1,1,0,0,0}, {"geosearch",geosearchCommand,-7, "read-only @geo @swap_zset", @@ -1355,67 +1354,32 @@ void getKeyRequestsFreeResult(getKeyRequestsResult *result) { } } -/* ====== swapOnKey bridge: cmdparse → getKeyRequestsResult ====== */ - -typedef struct { - getKeyRequestsResult *result; -} swapCmdParseCtx; - - -/* cmdparse key/subkey => getKeyRequestsResult */ -static void swapOnKey(void *ctx, int dbid, struct redisCommand* cmd, robj** argv, int argc, int key_arg_idx, - int subkeys_count, int subkeys_start, - int subkeys_step, const int *subkey_arg_idxs, - const cmdParseKeyExtra *extra) { - swapCmdParseCtx *sctx = (swapCmdParseCtx *)ctx; - getKeyRequestsResult *result = sctx->result; - int intention = cmd->intention; - int intention_flags = cmd->intention_flags; - uint64_t cmd_flags = cmd->flags; - - - if (subkeys_count > 0) { - robj *key = argv[key_arg_idx]; - incrRefCount(key); - robj **subkeys = zmalloc(subkeys_count * sizeof(robj *)); - for (int i = 0; i < subkeys_count; i++) { - int idx = subkey_arg_idxs ? subkey_arg_idxs[i] - : subkeys_start + i * subkeys_step; - robj *subkey = argv[idx]; - incrRefCount(subkey); - subkeys[i] = subkey; - } - getKeyRequestsAppendSubkeyResult(result, REQUEST_LEVEL_KEY, key, - subkeys_count, subkeys, - intention, intention_flags, cmd_flags, dbid); - return; - } - - /* only key */ - robj *key = argv[key_arg_idx]; - incrRefCount(key); - getKeyRequestsAppendSubkeyResult(result, REQUEST_LEVEL_KEY, key, - 0, NULL, intention, intention_flags, cmd_flags, dbid); - -} - /* NOTE that result.{key,subkeys} are ONLY REFS to client argv (since client * outlives getKeysResult if no swap action happend. key, subkey will be * copied (using incrRefCount) when async swap acutally proceed. */ static int _getSingleCmdKeyRequests(int dbid, struct redisCommand* cmd, robj** argv, int argc, getKeyRequestsResult *result) { - if (cmd->getkeyrequests_proc != NULL) { - return cmd->getkeyrequests_proc(dbid,cmd,argv,argc,result); + if (cmd->getkeyrequests_proc == NULL) { + int i, numkeys; + getKeysResult keys = GETKEYS_RESULT_INIT; + /* whole key swaping, swaps defined by command arity. */ + numkeys = getKeysFromCommand(cmd,argv,argc,&keys); + getKeyRequestsPrepareResult(result,result->num+numkeys); + for (i = 0; i < numkeys; i++) { + robj *key = argv[keys.keys[i]]; + + incrRefCount(key); + getKeyRequestsAppendSubkeyResult(result,REQUEST_LEVEL_KEY,key,0,NULL, + cmd->intention,cmd->intention_flags,cmd->flags, dbid); + } + getKeysFreeResult(&keys); + return 0; } else if (cmd->flags & CMD_MODULE) { /* TODO support module */ - return 0; } else { - swapCmdParseCtx ctx = { - .result = result, - }; - cmdParseKeys(dbid, cmd, argv, argc, &ctx, swapOnKey); - return 0; + return cmd->getkeyrequests_proc(dbid,cmd,argv,argc,result); } + return 0; } static void getSingleCmdKeyRequests(client *c, getKeyRequestsResult *result) { @@ -1635,6 +1599,56 @@ int getKeyRequestsZdiffstore(int dbid, struct redisCommand *cmd, robj **argv, in return getKeyRequestsZunionInterDiffGeneric(dbid, cmd, argv, argc, result, SET_OP_DIFF); } +#define GETKEYS_RESULT_SUBKEYS_INIT_LEN 8 +#define GETKEYS_RESULT_SUBKEYS_LINER_LEN 1024 + +int getKeyRequestsSingleKeyWithSubkeys(int dbid, struct redisCommand *cmd, robj **argv, + int argc, struct getKeyRequestsResult *result, + int key_index, int first_subkey, int last_subkey, int subkey_step) { + int i, num = 0, capacity = GETKEYS_RESULT_SUBKEYS_INIT_LEN; + robj *key, **subkeys = NULL; + UNUSED(cmd); + + subkeys = zmalloc(capacity*sizeof(robj*)); + getKeyRequestsPrepareResult(result,result->num+1); + + key = argv[key_index]; + incrRefCount(key); + + if (last_subkey < 0) last_subkey += argc; + for (i = first_subkey; i <= last_subkey; i += subkey_step) { + robj *subkey = argv[i]; + if (num >= capacity) { + if (capacity < GETKEYS_RESULT_SUBKEYS_LINER_LEN) + capacity *= 2; + else + capacity += GETKEYS_RESULT_SUBKEYS_LINER_LEN; + + subkeys = zrealloc(subkeys, capacity*sizeof(robj*)); + } + incrRefCount(subkey); + subkeys[num++] = subkey; + } + getKeyRequestsAppendSubkeyResult(result,REQUEST_LEVEL_KEY,key,num,subkeys, + cmd->intention,cmd->intention_flags,cmd->flags, dbid); + + return 0; +} + +int getKeyRequestsHset(int dbid,struct redisCommand *cmd, robj **argv, int argc, + struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid,cmd,argv,argc,result,1,2,-1,2); +} + +int getKeyRequestsHmget(int dbid, struct redisCommand *cmd, robj **argv, int argc, + struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid,cmd,argv,argc,result,1,2,-1,1); +} + +int getKeyRequestSmembers(int dbid, struct redisCommand *cmd, robj **argv, int argc, + struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid,cmd,argv,argc,result,1,2,-1,1); +} int getKeyRequestSmove(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { @@ -1889,113 +1903,57 @@ int getKeyRequestsLtrim(int dbid, struct redisCommand *cmd, robj **argv, result,1,2,3,1/*num_ranges*/,(long)start,(long)stop,(int)1/*reverse*/); return 0; } - -int getKeyRequestsBzpopMin(int dbid, struct redisCommand *cmd, robj **argv, - int argc, struct getKeyRequestsResult *result) { - - for (int i = 1; i < argc - 1; i++) { - robj *key = argv[i]; - incrRefCount(key); - - /* bzpopmin one summber */ - zrangespec *spec = zmalloc(sizeof(zrangespec)); - spec->min = -INFINITY; - spec->max = +INFINITY; - spec->minex = 0; - spec->maxex = 0; - - getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, - 0, /* reverse=0: min score first */ - spec, - 1, /* limit=1 */ - cmd->intention, - cmd->intention_flags, - cmd->flags, dbid); +/** zset **/ +int getKeyRequestsZAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + int first_score = 2; + while(first_score < argc) { + char *opt = argv[first_score]->ptr; + if ( + strcasecmp(opt,"nx") != 0 && + strcasecmp(opt,"xx") != 0 && + strcasecmp(opt,"ch") != 0 && + strcasecmp(opt,"incr") != 0 && + strcasecmp(opt,"gt") != 0 && + strcasecmp(opt,"lt") != 0 + ) { + break; + } + first_score++; } - return C_OK; + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, first_score + 1, -1, 2); } -int getKeyRequestsBzpopMax(int dbid, struct redisCommand *cmd, robj **argv, - int argc, struct getKeyRequestsResult *result) { - for (int i = 1; i < argc - 1; i++) { - robj *key = argv[i]; - incrRefCount(key); +int getKeyRequestsZScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd,argv,argc,result,1,2,-1,1); +} - zrangespec *spec = zmalloc(sizeof(zrangespec)); - spec->min = -INFINITY; - spec->max = +INFINITY; - spec->minex = 0; - spec->maxex = 0; - - getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, - 1, /* reverse=1: max score first */ - spec, - 1, /* limit=1 */ - cmd->intention, - cmd->intention_flags, - cmd->flags, dbid); - } - return C_OK; +int getKeyRequestsZincrby(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 3, -1, 2); } -int getKeyRequestsZpopMin(int dbid, struct redisCommand *cmd, robj **argv, - int argc, struct getKeyRequestsResult *result) { - long count = 1; +int getKeyRequestsZMScore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 2, -1, 1); +} - if (argc >= 3) { - long long value; - if (getLongLongFromObject(argv[2], &value) == C_OK && value > 0) { - count = value; - } +#define ZMIN -1 +#define ZMAX 1 +int getKeyRequestsZpopGeneric(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result, int flags) { + UNUSED(cmd), UNUSED(flags); + getKeyRequestsPrepareResult(result,result->num+ argc - 2); + for(int i = 1; i < argc - 1; i++) { + incrRefCount(argv[i]); + getKeyRequestsAppendSubkeyResult(result, REQUEST_LEVEL_KEY, argv[i], 0, NULL, cmd->intention, + cmd->intention_flags, cmd->flags, dbid); } - - robj *key = argv[1]; - incrRefCount(key); - - zrangespec *spec = zmalloc(sizeof(zrangespec)); - spec->min = -INFINITY; - spec->max = +INFINITY; - spec->minex = 0; - spec->maxex = 0; - - getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, - 0, /* reverse=0:,min score first */ - spec, - count, /* limit */ - cmd->intention, - cmd->intention_flags, - cmd->flags, dbid); return C_OK; } -int getKeyRequestsZpopMax(int dbid, struct redisCommand *cmd, robj **argv, - int argc, struct getKeyRequestsResult *result) { - long count = 1; - - if (argc >= 3) { - long long value; - if (getLongLongFromObject(argv[2], &value) == C_OK && value > 0) { - count = value; - } - } - - robj *key = argv[1]; - incrRefCount(key); +int getKeyRequestsZpopMin(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsZpopGeneric(dbid, cmd, argv, argc, result, ZMIN); +} - zrangespec *spec = zmalloc(sizeof(zrangespec)); - spec->min = -INFINITY; - spec->max = +INFINITY; - spec->minex = 0; - spec->maxex = 0; - - getKeyRequestsAppendScoreResult(result, REQUEST_LEVEL_KEY, key, - 1, /* reverse=1: max score first */ - spec, - count, - cmd->intention, - cmd->intention_flags, - cmd->flags, dbid); - return C_OK; +int getKeyRequestsZpopMax(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsZpopGeneric(dbid, cmd, argv, argc, result, ZMAX); } int getKeyRequestsZrangestore(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { @@ -2112,6 +2070,30 @@ int getKeyRequestsZlexCount(int dbid, struct redisCommand *cmd, robj **argv, int int getKeyRequestsZremRangeByLex(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { return getKeyRequestsZrangeGeneric(dbid, cmd, argv, argc, result, ZRANGE_LEX, ZRANGE_DIRECTION_FORWARD); } +/** geo **/ +int getKeyRequestsGeoAdd(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + int first_score = 2; + while(first_score < argc) { + char *opt = argv[first_score]->ptr; + if ( + strcasecmp(opt,"nx") != 0 && + strcasecmp(opt,"xx") != 0 && + strcasecmp(opt,"ch") != 0 + ) { + break; + } + first_score++; + } + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, first_score + 2, -1, 3); +} + +int getKeyRequestsGeoDist(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 2, -2, 1); +} + +int getKeyRequestsGeoHash(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { + return getKeyRequestsSingleKeyWithSubkeys(dbid, cmd, argv, argc, result, 1, 2, -1, 1); +} int getKeyRequestsGeoRadius(int dbid, struct redisCommand *cmd, robj **argv, int argc, struct getKeyRequestsResult *result) { int storekeyIndex = -1; @@ -2265,6 +2247,14 @@ int getKeyRequestsBitop(int dbid, struct redisCommand *cmd, robj **argv, return getKeyRequestsOneDestKeyMultiSrcKeys(dbid, cmd, argv, argc, result, 2, 3, -1); } +int getKeyRequestsBitField(int dbid, struct redisCommand *cmd, robj **argv, + int argc, struct getKeyRequestsResult *result) { + + UNUSED(argc); + getKeyRequestsSingleKey(result,argv[1],cmd->intention,cmd->intention_flags,cmd->flags,dbid); + return 0; +} + #define GET_KEYREQUESTS_MEMORY_MUL 4 @@ -2350,10 +2340,6 @@ int swapCmdTest(int argc, char *argv[], int accurate) { TEST("cmd: init") { initServerConfig(); ACLInit(); - #ifdef ENABLE_CMDPARSE - cmdParseBindToCommands(); - #endif - server.hz = 10; c = createClient(NULL); initTestRedisDb(); diff --git a/src/networking.c b/src/networking.c index 73ec3449657..3c33926ca79 100644 --- a/src/networking.c +++ b/src/networking.c @@ -501,20 +501,20 @@ void afterErrorReply(client *c, const char *s, size_t len) { * Unlike addReplyErrorSds and others alike which rely on addReplyErrorLength. */ void addReplyErrorObject(client *c, robj *err) { addReply(c, err); - ctrip_afterErrorReply(c, err->ptr, sdslen(err->ptr)-2); /* Ignore trailing \r\n */ + ctrip_afterErrorReply(c, err->ptr, sdslen(err->ptr)-2, -1); /* Ignore trailing \r\n */ } /* See addReplyErrorLength for expectations from the input string. */ void addReplyError(client *c, const char *err) { addReplyErrorLength(c,err,strlen(err)); - ctrip_afterErrorReply(c,err,strlen(err)); + ctrip_afterErrorReply(c,err,strlen(err), -1); } /* See addReplyErrorLength for expectations from the input string. */ /* As a side effect the SDS string is freed. */ void addReplyErrorSds(client *c, sds err) { addReplyErrorLength(c,err,sdslen(err)); - ctrip_afterErrorReply(c,err,sdslen(err)); + ctrip_afterErrorReply(c,err,sdslen(err), -1); sdsfree(err); } @@ -531,7 +531,7 @@ void addReplyErrorFormat(client *c, const char *fmt, ...) { * invalid protocol is emitted. */ s = sdsmapchars(s, "\r\n", " ", 2); addReplyErrorLength(c,s,sdslen(s)); - ctrip_afterErrorReply(c,s,sdslen(s)); + ctrip_afterErrorReply(c,s,sdslen(s), -1); sdsfree(s); } diff --git a/src/redis_gtid.h b/src/redis_gtid.h new file mode 120000 index 00000000000..7a3af4baf56 --- /dev/null +++ b/src/redis_gtid.h @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/redis_gtid_6x.h \ No newline at end of file diff --git a/src/replication.c b/src/replication.c index 98ae6d36523..646c20022cc 100644 --- a/src/replication.c +++ b/src/replication.c @@ -811,7 +811,7 @@ void syncCommand(client *c) { * So the slave knows the new replid and offset to try a PSYNC later * if the connection with the master is lost. */ if (!strcasecmp(c->argv[0]->ptr,"psync") || !strcasecmp(c->argv[0]->ptr,"xsync")) { - if (ctrip_masterTryPartialResynchronization(c) == C_OK) { + if (ctrip_masterTryPartialResynchronization(c, -1) == C_OK) { server.stat_sync_partial_ok++; return; /* No full resync needed, return. */ } else { diff --git a/src/server.c b/src/server.c index 54a4922daf4..bb67f6e6ab0 100644 --- a/src/server.c +++ b/src/server.c @@ -3521,10 +3521,7 @@ void initServer(void) { server.gtid_lost = gtidSetNew(); xsyncUuidInterestedInit(); gtidInitialInfoInit(server.gtid_initial); -#ifdef ENABLE_CMDPARSE - cmdParseBindToCommands(); -#endif - server.gtid_gap_log = gtidGapLogNew(); + server.gtid_gap_log = gtidGaplogNew(); server.gtid_xsync_fullresync_indicator = 0; server.gtid_executed_cmd_count = 0; server.gtid_ignored_cmd_count = 0; diff --git a/src/server.h b/src/server.h index 3d42a0ab899..a400abdd79a 100644 --- a/src/server.h +++ b/src/server.h @@ -1742,7 +1742,7 @@ struct redisServer { long long gtid_executed_cmd_count; long long gtid_sync_stat[GTID_SYNC_TYPES]; int gtid_gaplog_enabled; - gtidGapLog* gtid_gap_log; + gtidGaplog* gtid_gap_log; /* importing mode */ mstime_t importing_end_time; /* in milliseconds */ @@ -1798,9 +1798,6 @@ struct redisCommand { ACLs. A connection is able to execute a given command if the user associated to the connection has this command bit set in the bitmap of allowed commands. */ -#ifdef ENABLE_CMDPARSE - void (*cmdparse_parse)(int dbid, struct redisCommand *cmd, robj **argv, int argc, void *ctx, cmdParseOnKeyFn on_key); -#endif }; struct redisError { diff --git a/src/xredis_adaptation_version.c b/src/xredis_adaptation_version.c deleted file mode 120000 index 324fe6944cd..00000000000 --- a/src/xredis_adaptation_version.c +++ /dev/null @@ -1 +0,0 @@ -../deps/xredis-gtid/xredis/xredis_adaptation_version.c \ No newline at end of file diff --git a/src/xredis_cmdparse.c b/src/xredis_cmdparse.c deleted file mode 120000 index 65d69ab2695..00000000000 --- a/src/xredis_cmdparse.c +++ /dev/null @@ -1 +0,0 @@ -../deps/xredis-gtid/xredis/xredis_cmdparse.c \ No newline at end of file diff --git a/src/xredis_gtid_adaptation_version.c b/src/xredis_gtid_adaptation_version.c new file mode 120000 index 00000000000..f8efd155a90 --- /dev/null +++ b/src/xredis_gtid_adaptation_version.c @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_gtid_adaptation_version_6x.c \ No newline at end of file diff --git a/src/xredis_gtid_adaptation_version.h b/src/xredis_gtid_adaptation_version.h new file mode 120000 index 00000000000..c4fb1a67cfd --- /dev/null +++ b/src/xredis_gtid_adaptation_version.h @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_gtid_adaptation_version.h \ No newline at end of file diff --git a/src/xredis_gtid_aof.c b/src/xredis_gtid_aof.c deleted file mode 120000 index bf1bc4d2348..00000000000 --- a/src/xredis_gtid_aof.c +++ /dev/null @@ -1 +0,0 @@ -../deps/xredis-gtid/xredis/xredis_gtid_aof.c \ No newline at end of file diff --git a/src/xredis_gtid_cmdparse.c b/src/xredis_gtid_cmdparse.c new file mode 120000 index 00000000000..4f35e5bf4d2 --- /dev/null +++ b/src/xredis_gtid_cmdparse.c @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_gtid_cmdparse.c \ No newline at end of file diff --git a/src/xredis_gtid_cmdparse.h b/src/xredis_gtid_cmdparse.h new file mode 120000 index 00000000000..89911fec0cd --- /dev/null +++ b/src/xredis_gtid_cmdparse.h @@ -0,0 +1 @@ +../deps/xredis-gtid/xredis/xredis_gtid_cmdparse.h \ No newline at end of file From 1b6ee7d355abc64333e39f7d6474498629e5f998 Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Thu, 11 Jun 2026 15:27:54 +0800 Subject: [PATCH 5/8] fix: randstring alpha excludes TCL metachars '[' and ']' randstring alpha range 48-122 includes '['(91) and ']'(93) which TCL interprets as command substitution, causing flaky failures when random client name happens to contain these chars. --- deps/xredis-gtid | 2 +- tests/support/util.tcl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps/xredis-gtid b/deps/xredis-gtid index d7028ca424d..450f6f677d5 160000 --- a/deps/xredis-gtid +++ b/deps/xredis-gtid @@ -1 +1 @@ -Subproject commit d7028ca424ddf86fafdb9ced18711740c573b0ae +Subproject commit 450f6f677d50b171f797a826e6383e239cae6c47 diff --git a/tests/support/util.tcl b/tests/support/util.tcl index 3b5ad9c64b9..4b8b483de78 100644 --- a/tests/support/util.tcl +++ b/tests/support/util.tcl @@ -13,8 +13,8 @@ proc randstring {min max {type binary}} { } while {$len} { set rr [expr {$minval+int(rand()*($maxval-$minval+1))}] - if {$type eq {alpha} && $rr eq 92} { - set rr 90; # avoid putting '\' char in the string, it can mess up TCL processing + if {$type eq {alpha} && ($rr eq 92 || $rr eq 91 || $rr eq 93)} { + set rr 94; # avoid '\', '[', ']' — '[' and ']' break TCL command substitution } append output [format "%c" $rr] incr len -1 From dd863698339a485695843dc420062051dfa27cfe Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Sun, 14 Jun 2026 15:23:28 +0800 Subject: [PATCH 6/8] fix: correct server state update and replication unset ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/networking.c: In freeClient() defer-free path, use RS_UPDATE_DOWN instead of RS_UPDATE_NOP — when master mode is enabled via defer-free, the server state must reflect a real down transition, not a no-op. src/replication.c: Move disconnectSlaves() before shiftReplicationId() in replicationUnsetMaster(). Slaves must be disconnected before the replication ID changes so they can attempt partial resync with the new ID. deps/xredis-gtid: Update submodule pointer. --- deps/xredis-gtid | 2 +- src/networking.c | 2 +- src/replication.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/deps/xredis-gtid b/deps/xredis-gtid index 450f6f677d5..86d6bfa3833 160000 --- a/deps/xredis-gtid +++ b/deps/xredis-gtid @@ -1 +1 @@ -Subproject commit 450f6f677d50b171f797a826e6383e239cae6c47 +Subproject commit 86d6bfa3833668b6fa40aa58b4a896a31b677b74 diff --git a/src/networking.c b/src/networking.c index 3c33926ca79..6573b00b9bd 100644 --- a/src/networking.c +++ b/src/networking.c @@ -1479,7 +1479,7 @@ void freeClient(client *c) { //TODO what if master link reset but no master mode enabled? serverReplStreamSwitchIfNeeded( server.gtid_enabled ? REPL_MODE_XSYNC:REPL_MODE_PSYNC, - RS_UPDATE_NOP,"master mode enabled(defer)"); + RS_UPDATE_DOWN,"master mode enabled(defer)"); if (!(c->flags & (CLIENT_PROTOCOL_ERROR|CLIENT_BLOCKED|CLIENT_SWAP_DISCARD_CACHED_MASTER)) && server.repl_mode->mode != REPL_MODE_XSYNC) { diff --git a/src/replication.c b/src/replication.c index 646c20022cc..9277ad67f75 100644 --- a/src/replication.c +++ b/src/replication.c @@ -2808,6 +2808,11 @@ void replicationUnsetMaster(void) { if (server.master) freeClient(server.master); replicationDiscardCachedMaster(); cancelReplicationHandshake(0); + /* Disconnecting all the slaves is required: we need to inform slaves + * of the replication ID change (see shiftReplicationId() call). However + * the slaves will be able to partially resync with us, so it will be + * a very fast reconnection. */ + disconnectSlaves(); /* When a slave is turned into a master, the current replication ID * (that was inherited from the master at synchronization time) is * used as secondary ID up to the current offset, and a new replication @@ -2832,11 +2837,6 @@ void replicationUnsetMaster(void) { server.gtid_enabled ? REPL_MODE_XSYNC:REPL_MODE_PSYNC, RS_UPDATE_NOP,"master mode enabled"); } - /* Disconnecting all the slaves is required: we need to inform slaves - * of the replication ID change (see shiftReplicationId() call). However - * the slaves will be able to partially resync with us, so it will be - * a very fast reconnection. */ - disconnectSlaves(); server.repl_state = REPL_STATE_NONE; /* We need to make sure the new master will start the replication stream From 1f4dc6967484edc03848cd916894ad7e5e7103d1 Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Mon, 15 Jun 2026 18:30:55 +0800 Subject: [PATCH 7/8] [gtid] submodule: move readBacklogIterator to gap_log and split tests per file --- deps/xredis-gtid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/xredis-gtid b/deps/xredis-gtid index 86d6bfa3833..c7e028b79b1 160000 --- a/deps/xredis-gtid +++ b/deps/xredis-gtid @@ -1 +1 @@ -Subproject commit 86d6bfa3833668b6fa40aa58b4a896a31b677b74 +Subproject commit c7e028b79b1682e6659e620f0f449f63f98a0151 From 48a0b9072c219bf0087438d477a99fe7dc19ddf3 Mon Sep 17 00:00:00 2001 From: "gd.zhou" Date: Mon, 15 Jun 2026 19:23:38 +0800 Subject: [PATCH 8/8] [gtid] submodule: thread psync_offset through masterParseSyncRequest --- deps/xredis-gtid | 2 +- src/replication.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/xredis-gtid b/deps/xredis-gtid index c7e028b79b1..d61e9fb3a69 160000 --- a/deps/xredis-gtid +++ b/deps/xredis-gtid @@ -1 +1 @@ -Subproject commit c7e028b79b1682e6659e620f0f449f63f98a0151 +Subproject commit d61e9fb3a69263c1ea69e093767faedeaaa81375 diff --git a/src/replication.c b/src/replication.c index 9277ad67f75..ed8e6fdfc50 100644 --- a/src/replication.c +++ b/src/replication.c @@ -811,7 +811,7 @@ void syncCommand(client *c) { * So the slave knows the new replid and offset to try a PSYNC later * if the connection with the master is lost. */ if (!strcasecmp(c->argv[0]->ptr,"psync") || !strcasecmp(c->argv[0]->ptr,"xsync")) { - if (ctrip_masterTryPartialResynchronization(c, -1) == C_OK) { + if (ctrip_masterTryPartialResynchronization(c, PSYNC_OFFSET_UNSET) == C_OK) { server.stat_sync_partial_ok++; return; /* No full resync needed, return. */ } else {