1 /**************************************************************************
2 Copyright (c) 2017 sewenew
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 *************************************************************************/
16
17 #ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP
18 #define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP
19
20 #include <utility>
21 #include "command.h"
22 #include "reply.h"
23 #include "utils.h"
24 #include "errors.h"
25 #include "shards_pool.h"
26
27 namespace sw {
28
29 namespace redis {
30
31 template <typename Cmd, typename Key, typename ...Args>
command(Cmd cmd,Key && key,Args &&...args)32 auto RedisCluster::command(Cmd cmd, Key &&key, Args &&...args)
33 -> typename std::enable_if<!std::is_convertible<Cmd, StringView>::value, ReplyUPtr>::type {
34 return _command(cmd,
35 std::is_convertible<typename std::decay<Key>::type, StringView>(),
36 std::forward<Key>(key),
37 std::forward<Args>(args)...);
38 }
39
40 template <typename Key, typename ...Args>
command(const StringView & cmd_name,Key && key,Args &&...args)41 auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args)
42 -> typename std::enable_if<(std::is_convertible<Key, StringView>::value
43 || std::is_arithmetic<typename std::decay<Key>::type>::value)
44 && !IsIter<typename LastType<Key, Args...>::type>::value, ReplyUPtr>::type {
45 auto cmd = Command(cmd_name);
46
47 return _generic_command(cmd, std::forward<Key>(key), std::forward<Args>(args)...);
48 }
49
50 template <typename Result, typename Key, typename ...Args>
command(const StringView & cmd_name,Key && key,Args &&...args)51 auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args)
52 -> typename std::enable_if<std::is_convertible<Key, StringView>::value
53 || std::is_arithmetic<typename std::decay<Key>::type>::value, Result>::type {
54 auto r = command(cmd_name, std::forward<Key>(key), std::forward<Args>(args)...);
55
56 assert(r);
57
58 return reply::parse<Result>(*r);
59 }
60
61 template <typename Key, typename ...Args>
command(const StringView & cmd_name,Key && key,Args &&...args)62 auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args)
63 -> typename std::enable_if<(std::is_convertible<Key, StringView>::value
64 || std::is_arithmetic<typename std::decay<Key>::type>::value)
65 && IsIter<typename LastType<Key, Args...>::type>::value, void>::type {
66 auto r = _command(cmd_name,
67 MakeIndexSequence<sizeof...(Args)>(),
68 std::forward<Key>(key),
69 std::forward<Args>(args)...);
70
71 assert(r);
72
73 reply::to_array(*r, LastValue(std::forward<Args>(args)...));
74 }
75
76 template <typename Input>
command(Input first,Input last)77 auto RedisCluster::command(Input first, Input last)
78 -> typename std::enable_if<IsIter<Input>::value, ReplyUPtr>::type {
79 if (first == last || std::next(first) == last) {
80 throw Error("command: invalid range");
81 }
82
83 const auto &key = *first;
84 ++first;
85
86 auto cmd = [&key](Connection &connection, Input first, Input last) {
87 CmdArgs cmd_args;
88 cmd_args.append(key);
89 while (first != last) {
90 cmd_args.append(*first);
91 ++first;
92 }
93 connection.send(cmd_args);
94 };
95
96 return command(cmd, first, last);
97 }
98
99 template <typename Result, typename Input>
command(Input first,Input last)100 auto RedisCluster::command(Input first, Input last)
101 -> typename std::enable_if<IsIter<Input>::value, Result>::type {
102 auto r = command(first, last);
103
104 assert(r);
105
106 return reply::parse<Result>(*r);
107 }
108
109 template <typename Input, typename Output>
command(Input first,Input last,Output output)110 auto RedisCluster::command(Input first, Input last, Output output)
111 -> typename std::enable_if<IsIter<Input>::value, void>::type {
112 auto r = command(first, last);
113
114 assert(r);
115
116 reply::to_array(*r, output);
117 }
118
119 // KEY commands.
120
121 template <typename Input>
del(Input first,Input last)122 long long RedisCluster::del(Input first, Input last) {
123 if (first == last) {
124 throw Error("DEL: no key specified");
125 }
126
127 auto reply = command(cmd::del_range<Input>, first, last);
128
129 return reply::parse<long long>(*reply);
130 }
131
132 template <typename Input>
exists(Input first,Input last)133 long long RedisCluster::exists(Input first, Input last) {
134 if (first == last) {
135 throw Error("EXISTS: no key specified");
136 }
137
138 auto reply = command(cmd::exists_range<Input>, first, last);
139
140 return reply::parse<long long>(*reply);
141 }
142
expire(const StringView & key,const std::chrono::seconds & timeout)143 inline bool RedisCluster::expire(const StringView &key, const std::chrono::seconds &timeout) {
144 return expire(key, timeout.count());
145 }
146
expireat(const StringView & key,const std::chrono::time_point<std::chrono::system_clock,std::chrono::seconds> & tp)147 inline bool RedisCluster::expireat(const StringView &key,
148 const std::chrono::time_point<std::chrono::system_clock,
149 std::chrono::seconds> &tp) {
150 return expireat(key, tp.time_since_epoch().count());
151 }
152
pexpire(const StringView & key,const std::chrono::milliseconds & timeout)153 inline bool RedisCluster::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) {
154 return pexpire(key, timeout.count());
155 }
156
pexpireat(const StringView & key,const std::chrono::time_point<std::chrono::system_clock,std::chrono::milliseconds> & tp)157 inline bool RedisCluster::pexpireat(const StringView &key,
158 const std::chrono::time_point<std::chrono::system_clock,
159 std::chrono::milliseconds> &tp) {
160 return pexpireat(key, tp.time_since_epoch().count());
161 }
162
restore(const StringView & key,const StringView & val,const std::chrono::milliseconds & ttl,bool replace)163 inline void RedisCluster::restore(const StringView &key,
164 const StringView &val,
165 const std::chrono::milliseconds &ttl,
166 bool replace) {
167 return restore(key, val, ttl.count(), replace);
168 }
169
170 template <typename Input>
touch(Input first,Input last)171 long long RedisCluster::touch(Input first, Input last) {
172 if (first == last) {
173 throw Error("TOUCH: no key specified");
174 }
175
176 auto reply = command(cmd::touch_range<Input>, first, last);
177
178 return reply::parse<long long>(*reply);
179 }
180
181 template <typename Input>
unlink(Input first,Input last)182 long long RedisCluster::unlink(Input first, Input last) {
183 if (first == last) {
184 throw Error("UNLINK: no key specified");
185 }
186
187 auto reply = command(cmd::unlink_range<Input>, first, last);
188
189 return reply::parse<long long>(*reply);
190 }
191
192 // STRING commands.
193
194 template <typename Input>
bitop(BitOp op,const StringView & destination,Input first,Input last)195 long long RedisCluster::bitop(BitOp op, const StringView &destination, Input first, Input last) {
196 if (first == last) {
197 throw Error("BITOP: no key specified");
198 }
199
200 auto reply = _command(cmd::bitop_range<Input>, destination, op, destination, first, last);
201
202 return reply::parse<long long>(*reply);
203 }
204
205 template <typename Input, typename Output>
mget(Input first,Input last,Output output)206 void RedisCluster::mget(Input first, Input last, Output output) {
207 if (first == last) {
208 throw Error("MGET: no key specified");
209 }
210
211 auto reply = command(cmd::mget<Input>, first, last);
212
213 reply::to_array(*reply, output);
214 }
215
216 template <typename Input>
mset(Input first,Input last)217 void RedisCluster::mset(Input first, Input last) {
218 if (first == last) {
219 throw Error("MSET: no key specified");
220 }
221
222 auto reply = command(cmd::mset<Input>, first, last);
223
224 reply::parse<void>(*reply);
225 }
226
227 template <typename Input>
msetnx(Input first,Input last)228 bool RedisCluster::msetnx(Input first, Input last) {
229 if (first == last) {
230 throw Error("MSETNX: no key specified");
231 }
232
233 auto reply = command(cmd::msetnx<Input>, first, last);
234
235 return reply::parse<bool>(*reply);
236 }
237
psetex(const StringView & key,const std::chrono::milliseconds & ttl,const StringView & val)238 inline void RedisCluster::psetex(const StringView &key,
239 const std::chrono::milliseconds &ttl,
240 const StringView &val) {
241 return psetex(key, ttl.count(), val);
242 }
243
setex(const StringView & key,const std::chrono::seconds & ttl,const StringView & val)244 inline void RedisCluster::setex(const StringView &key,
245 const std::chrono::seconds &ttl,
246 const StringView &val) {
247 setex(key, ttl.count(), val);
248 }
249
250 // LIST commands.
251
252 template <typename Input>
blpop(Input first,Input last,long long timeout)253 OptionalStringPair RedisCluster::blpop(Input first, Input last, long long timeout) {
254 if (first == last) {
255 throw Error("BLPOP: no key specified");
256 }
257
258 auto reply = command(cmd::blpop_range<Input>, first, last, timeout);
259
260 return reply::parse<OptionalStringPair>(*reply);
261 }
262
263 template <typename Input>
blpop(Input first,Input last,const std::chrono::seconds & timeout)264 OptionalStringPair RedisCluster::blpop(Input first,
265 Input last,
266 const std::chrono::seconds &timeout) {
267 return blpop(first, last, timeout.count());
268 }
269
270 template <typename Input>
brpop(Input first,Input last,long long timeout)271 OptionalStringPair RedisCluster::brpop(Input first, Input last, long long timeout) {
272 if (first == last) {
273 throw Error("BRPOP: no key specified");
274 }
275
276 auto reply = command(cmd::brpop_range<Input>, first, last, timeout);
277
278 return reply::parse<OptionalStringPair>(*reply);
279 }
280
281 template <typename Input>
brpop(Input first,Input last,const std::chrono::seconds & timeout)282 OptionalStringPair RedisCluster::brpop(Input first,
283 Input last,
284 const std::chrono::seconds &timeout) {
285 return brpop(first, last, timeout.count());
286 }
287
brpoplpush(const StringView & source,const StringView & destination,const std::chrono::seconds & timeout)288 inline OptionalString RedisCluster::brpoplpush(const StringView &source,
289 const StringView &destination,
290 const std::chrono::seconds &timeout) {
291 return brpoplpush(source, destination, timeout.count());
292 }
293
294 template <typename Input>
lpush(const StringView & key,Input first,Input last)295 inline long long RedisCluster::lpush(const StringView &key, Input first, Input last) {
296 if (first == last) {
297 throw Error("LPUSH: no key specified");
298 }
299
300 auto reply = command(cmd::lpush_range<Input>, key, first, last);
301
302 return reply::parse<long long>(*reply);
303 }
304
305 template <typename Output>
lrange(const StringView & key,long long start,long long stop,Output output)306 inline void RedisCluster::lrange(const StringView &key, long long start, long long stop, Output output) {
307 auto reply = command(cmd::lrange, key, start, stop);
308
309 reply::to_array(*reply, output);
310 }
311
312 template <typename Input>
rpush(const StringView & key,Input first,Input last)313 inline long long RedisCluster::rpush(const StringView &key, Input first, Input last) {
314 if (first == last) {
315 throw Error("RPUSH: no key specified");
316 }
317
318 auto reply = command(cmd::rpush_range<Input>, key, first, last);
319
320 return reply::parse<long long>(*reply);
321 }
322
323 // HASH commands.
324
325 template <typename Input>
hdel(const StringView & key,Input first,Input last)326 inline long long RedisCluster::hdel(const StringView &key, Input first, Input last) {
327 if (first == last) {
328 throw Error("HDEL: no key specified");
329 }
330
331 auto reply = command(cmd::hdel_range<Input>, key, first, last);
332
333 return reply::parse<long long>(*reply);
334 }
335
336 template <typename Output>
hgetall(const StringView & key,Output output)337 inline void RedisCluster::hgetall(const StringView &key, Output output) {
338 auto reply = command(cmd::hgetall, key);
339
340 reply::to_array(*reply, output);
341 }
342
343 template <typename Output>
hkeys(const StringView & key,Output output)344 inline void RedisCluster::hkeys(const StringView &key, Output output) {
345 auto reply = command(cmd::hkeys, key);
346
347 reply::to_array(*reply, output);
348 }
349
350 template <typename Input, typename Output>
hmget(const StringView & key,Input first,Input last,Output output)351 inline void RedisCluster::hmget(const StringView &key, Input first, Input last, Output output) {
352 if (first == last) {
353 throw Error("HMGET: no key specified");
354 }
355
356 auto reply = command(cmd::hmget<Input>, key, first, last);
357
358 reply::to_array(*reply, output);
359 }
360
361 template <typename Input>
hmset(const StringView & key,Input first,Input last)362 inline void RedisCluster::hmset(const StringView &key, Input first, Input last) {
363 if (first == last) {
364 throw Error("HMSET: no key specified");
365 }
366
367 auto reply = command(cmd::hmset<Input>, key, first, last);
368
369 reply::parse<void>(*reply);
370 }
371
372 template <typename Output>
hscan(const StringView & key,long long cursor,const StringView & pattern,long long count,Output output)373 long long RedisCluster::hscan(const StringView &key,
374 long long cursor,
375 const StringView &pattern,
376 long long count,
377 Output output) {
378 auto reply = command(cmd::hscan, key, cursor, pattern, count);
379
380 return reply::parse_scan_reply(*reply, output);
381 }
382
383 template <typename Output>
hscan(const StringView & key,long long cursor,const StringView & pattern,Output output)384 inline long long RedisCluster::hscan(const StringView &key,
385 long long cursor,
386 const StringView &pattern,
387 Output output) {
388 return hscan(key, cursor, pattern, 10, output);
389 }
390
391 template <typename Output>
hscan(const StringView & key,long long cursor,long long count,Output output)392 inline long long RedisCluster::hscan(const StringView &key,
393 long long cursor,
394 long long count,
395 Output output) {
396 return hscan(key, cursor, "*", count, output);
397 }
398
399 template <typename Output>
hscan(const StringView & key,long long cursor,Output output)400 inline long long RedisCluster::hscan(const StringView &key,
401 long long cursor,
402 Output output) {
403 return hscan(key, cursor, "*", 10, output);
404 }
405
406 template <typename Output>
hvals(const StringView & key,Output output)407 inline void RedisCluster::hvals(const StringView &key, Output output) {
408 auto reply = command(cmd::hvals, key);
409
410 reply::to_array(*reply, output);
411 }
412
413 // SET commands.
414
415 template <typename Input>
sadd(const StringView & key,Input first,Input last)416 long long RedisCluster::sadd(const StringView &key, Input first, Input last) {
417 if (first == last) {
418 throw Error("SADD: no key specified");
419 }
420
421 auto reply = command(cmd::sadd_range<Input>, key, first, last);
422
423 return reply::parse<long long>(*reply);
424 }
425
426 template <typename Input, typename Output>
sdiff(Input first,Input last,Output output)427 void RedisCluster::sdiff(Input first, Input last, Output output) {
428 if (first == last) {
429 throw Error("SDIFF: no key specified");
430 }
431
432 auto reply = command(cmd::sdiff<Input>, first, last);
433
434 reply::to_array(*reply, output);
435 }
436
437 template <typename Input>
sdiffstore(const StringView & destination,Input first,Input last)438 long long RedisCluster::sdiffstore(const StringView &destination,
439 Input first,
440 Input last) {
441 if (first == last) {
442 throw Error("SDIFFSTORE: no key specified");
443 }
444
445 auto reply = command(cmd::sdiffstore_range<Input>, destination, first, last);
446
447 return reply::parse<long long>(*reply);
448 }
449
450 template <typename Input, typename Output>
sinter(Input first,Input last,Output output)451 void RedisCluster::sinter(Input first, Input last, Output output) {
452 if (first == last) {
453 throw Error("SINTER: no key specified");
454 }
455
456 auto reply = command(cmd::sinter<Input>, first, last);
457
458 reply::to_array(*reply, output);
459 }
460
461 template <typename Input>
sinterstore(const StringView & destination,Input first,Input last)462 long long RedisCluster::sinterstore(const StringView &destination,
463 Input first,
464 Input last) {
465 if (first == last) {
466 throw Error("SINTERSTORE: no key specified");
467 }
468
469 auto reply = command(cmd::sinterstore_range<Input>, destination, first, last);
470
471 return reply::parse<long long>(*reply);
472 }
473
474 template <typename Output>
smembers(const StringView & key,Output output)475 void RedisCluster::smembers(const StringView &key, Output output) {
476 auto reply = command(cmd::smembers, key);
477
478 reply::to_array(*reply, output);
479 }
480
481 template <typename Output>
spop(const StringView & key,long long count,Output output)482 void RedisCluster::spop(const StringView &key, long long count, Output output) {
483 auto reply = command(cmd::spop_range, key, count);
484
485 reply::to_array(*reply, output);
486 }
487
488 template <typename Output>
srandmember(const StringView & key,long long count,Output output)489 void RedisCluster::srandmember(const StringView &key, long long count, Output output) {
490 auto reply = command(cmd::srandmember_range, key, count);
491
492 reply::to_array(*reply, output);
493 }
494
495 template <typename Input>
srem(const StringView & key,Input first,Input last)496 long long RedisCluster::srem(const StringView &key, Input first, Input last) {
497 if (first == last) {
498 throw Error("SREM: no key specified");
499 }
500
501 auto reply = command(cmd::srem_range<Input>, key, first, last);
502
503 return reply::parse<long long>(*reply);
504 }
505
506 template <typename Output>
sscan(const StringView & key,long long cursor,const StringView & pattern,long long count,Output output)507 long long RedisCluster::sscan(const StringView &key,
508 long long cursor,
509 const StringView &pattern,
510 long long count,
511 Output output) {
512 auto reply = command(cmd::sscan, key, cursor, pattern, count);
513
514 return reply::parse_scan_reply(*reply, output);
515 }
516
517 template <typename Output>
sscan(const StringView & key,long long cursor,const StringView & pattern,Output output)518 inline long long RedisCluster::sscan(const StringView &key,
519 long long cursor,
520 const StringView &pattern,
521 Output output) {
522 return sscan(key, cursor, pattern, 10, output);
523 }
524
525 template <typename Output>
sscan(const StringView & key,long long cursor,long long count,Output output)526 inline long long RedisCluster::sscan(const StringView &key,
527 long long cursor,
528 long long count,
529 Output output) {
530 return sscan(key, cursor, "*", count, output);
531 }
532
533 template <typename Output>
sscan(const StringView & key,long long cursor,Output output)534 inline long long RedisCluster::sscan(const StringView &key,
535 long long cursor,
536 Output output) {
537 return sscan(key, cursor, "*", 10, output);
538 }
539
540 template <typename Input, typename Output>
sunion(Input first,Input last,Output output)541 void RedisCluster::sunion(Input first, Input last, Output output) {
542 if (first == last) {
543 throw Error("SUNION: no key specified");
544 }
545
546 auto reply = command(cmd::sunion<Input>, first, last);
547
548 reply::to_array(*reply, output);
549 }
550
551 template <typename Input>
sunionstore(const StringView & destination,Input first,Input last)552 long long RedisCluster::sunionstore(const StringView &destination, Input first, Input last) {
553 if (first == last) {
554 throw Error("SUNIONSTORE: no key specified");
555 }
556
557 auto reply = command(cmd::sunionstore_range<Input>, destination, first, last);
558
559 return reply::parse<long long>(*reply);
560 }
561
562 // SORTED SET commands.
563
bzpopmax(const StringView & key,const std::chrono::seconds & timeout)564 inline auto RedisCluster::bzpopmax(const StringView &key, const std::chrono::seconds &timeout)
565 -> Optional<std::tuple<std::string, std::string, double>> {
566 return bzpopmax(key, timeout.count());
567 }
568
569 template <typename Input>
bzpopmax(Input first,Input last,long long timeout)570 auto RedisCluster::bzpopmax(Input first, Input last, long long timeout)
571 -> Optional<std::tuple<std::string, std::string, double>> {
572 auto reply = command(cmd::bzpopmax_range<Input>, first, last, timeout);
573
574 return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
575 }
576
577 template <typename Input>
bzpopmax(Input first,Input last,const std::chrono::seconds & timeout)578 inline auto RedisCluster::bzpopmax(Input first,
579 Input last,
580 const std::chrono::seconds &timeout)
581 -> Optional<std::tuple<std::string, std::string, double>> {
582 return bzpopmax(first, last, timeout.count());
583 }
584
bzpopmin(const StringView & key,const std::chrono::seconds & timeout)585 inline auto RedisCluster::bzpopmin(const StringView &key, const std::chrono::seconds &timeout)
586 -> Optional<std::tuple<std::string, std::string, double>> {
587 return bzpopmin(key, timeout.count());
588 }
589
590 template <typename Input>
bzpopmin(Input first,Input last,long long timeout)591 auto RedisCluster::bzpopmin(Input first, Input last, long long timeout)
592 -> Optional<std::tuple<std::string, std::string, double>> {
593 auto reply = command(cmd::bzpopmin_range<Input>, first, last, timeout);
594
595 return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
596 }
597
598 template <typename Input>
bzpopmin(Input first,Input last,const std::chrono::seconds & timeout)599 inline auto RedisCluster::bzpopmin(Input first,
600 Input last,
601 const std::chrono::seconds &timeout)
602 -> Optional<std::tuple<std::string, std::string, double>> {
603 return bzpopmin(first, last, timeout.count());
604 }
605
606 template <typename Input>
zadd(const StringView & key,Input first,Input last,UpdateType type,bool changed)607 long long RedisCluster::zadd(const StringView &key,
608 Input first,
609 Input last,
610 UpdateType type,
611 bool changed) {
612 if (first == last) {
613 throw Error("ZADD: no key specified");
614 }
615
616 auto reply = command(cmd::zadd_range<Input>, key, first, last, type, changed);
617
618 return reply::parse<long long>(*reply);
619 }
620
621 template <typename Interval>
zcount(const StringView & key,const Interval & interval)622 long long RedisCluster::zcount(const StringView &key, const Interval &interval) {
623 auto reply = command(cmd::zcount<Interval>, key, interval);
624
625 return reply::parse<long long>(*reply);
626 }
627
628 template <typename Input>
zinterstore(const StringView & destination,Input first,Input last,Aggregation type)629 long long RedisCluster::zinterstore(const StringView &destination,
630 Input first,
631 Input last,
632 Aggregation type) {
633 if (first == last) {
634 throw Error("ZINTERSTORE: no key specified");
635 }
636
637 auto reply = command(cmd::zinterstore_range<Input>,
638 destination,
639 first,
640 last,
641 type);
642
643 return reply::parse<long long>(*reply);
644 }
645
646 template <typename Interval>
zlexcount(const StringView & key,const Interval & interval)647 long long RedisCluster::zlexcount(const StringView &key, const Interval &interval) {
648 auto reply = command(cmd::zlexcount<Interval>, key, interval);
649
650 return reply::parse<long long>(*reply);
651 }
652
653 template <typename Output>
zpopmax(const StringView & key,long long count,Output output)654 void RedisCluster::zpopmax(const StringView &key, long long count, Output output) {
655 auto reply = command(cmd::zpopmax, key, count);
656
657 reply::to_array(*reply, output);
658 }
659
660 template <typename Output>
zpopmin(const StringView & key,long long count,Output output)661 void RedisCluster::zpopmin(const StringView &key, long long count, Output output) {
662 auto reply = command(cmd::zpopmin, key, count);
663
664 reply::to_array(*reply, output);
665 }
666
667 template <typename Output>
zrange(const StringView & key,long long start,long long stop,Output output)668 void RedisCluster::zrange(const StringView &key, long long start, long long stop, Output output) {
669 auto reply = _score_command<Output>(cmd::zrange, key, start, stop);
670
671 reply::to_array(*reply, output);
672 }
673
674 template <typename Interval, typename Output>
zrangebylex(const StringView & key,const Interval & interval,Output output)675 void RedisCluster::zrangebylex(const StringView &key, const Interval &interval, Output output) {
676 zrangebylex(key, interval, {}, output);
677 }
678
679 template <typename Interval, typename Output>
zrangebylex(const StringView & key,const Interval & interval,const LimitOptions & opts,Output output)680 void RedisCluster::zrangebylex(const StringView &key,
681 const Interval &interval,
682 const LimitOptions &opts,
683 Output output) {
684 auto reply = command(cmd::zrangebylex<Interval>, key, interval, opts);
685
686 reply::to_array(*reply, output);
687 }
688
689 template <typename Interval, typename Output>
zrangebyscore(const StringView & key,const Interval & interval,Output output)690 void RedisCluster::zrangebyscore(const StringView &key,
691 const Interval &interval,
692 Output output) {
693 zrangebyscore(key, interval, {}, output);
694 }
695
696 template <typename Interval, typename Output>
zrangebyscore(const StringView & key,const Interval & interval,const LimitOptions & opts,Output output)697 void RedisCluster::zrangebyscore(const StringView &key,
698 const Interval &interval,
699 const LimitOptions &opts,
700 Output output) {
701 auto reply = _score_command<Output>(cmd::zrangebyscore<Interval>,
702 key,
703 interval,
704 opts);
705
706 reply::to_array(*reply, output);
707 }
708
709 template <typename Input>
zrem(const StringView & key,Input first,Input last)710 long long RedisCluster::zrem(const StringView &key, Input first, Input last) {
711 if (first == last) {
712 throw Error("ZREM: no key specified");
713 }
714
715 auto reply = command(cmd::zrem_range<Input>, key, first, last);
716
717 return reply::parse<long long>(*reply);
718 }
719
720 template <typename Interval>
zremrangebylex(const StringView & key,const Interval & interval)721 long long RedisCluster::zremrangebylex(const StringView &key, const Interval &interval) {
722 auto reply = command(cmd::zremrangebylex<Interval>, key, interval);
723
724 return reply::parse<long long>(*reply);
725 }
726
727 template <typename Interval>
zremrangebyscore(const StringView & key,const Interval & interval)728 long long RedisCluster::zremrangebyscore(const StringView &key, const Interval &interval) {
729 auto reply = command(cmd::zremrangebyscore<Interval>, key, interval);
730
731 return reply::parse<long long>(*reply);
732 }
733
734 template <typename Output>
zrevrange(const StringView & key,long long start,long long stop,Output output)735 void RedisCluster::zrevrange(const StringView &key, long long start, long long stop, Output output) {
736 auto reply = _score_command<Output>(cmd::zrevrange, key, start, stop);
737
738 reply::to_array(*reply, output);
739 }
740
741 template <typename Interval, typename Output>
zrevrangebylex(const StringView & key,const Interval & interval,Output output)742 inline void RedisCluster::zrevrangebylex(const StringView &key,
743 const Interval &interval,
744 Output output) {
745 zrevrangebylex(key, interval, {}, output);
746 }
747
748 template <typename Interval, typename Output>
zrevrangebylex(const StringView & key,const Interval & interval,const LimitOptions & opts,Output output)749 void RedisCluster::zrevrangebylex(const StringView &key,
750 const Interval &interval,
751 const LimitOptions &opts,
752 Output output) {
753 auto reply = command(cmd::zrevrangebylex<Interval>, key, interval, opts);
754
755 reply::to_array(*reply, output);
756 }
757
758 template <typename Interval, typename Output>
zrevrangebyscore(const StringView & key,const Interval & interval,Output output)759 void RedisCluster::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) {
760 zrevrangebyscore(key, interval, {}, output);
761 }
762
763 template <typename Interval, typename Output>
zrevrangebyscore(const StringView & key,const Interval & interval,const LimitOptions & opts,Output output)764 void RedisCluster::zrevrangebyscore(const StringView &key,
765 const Interval &interval,
766 const LimitOptions &opts,
767 Output output) {
768 auto reply = _score_command<Output>(cmd::zrevrangebyscore<Interval>, key, interval, opts);
769
770 reply::to_array(*reply, output);
771 }
772
773 template <typename Output>
zscan(const StringView & key,long long cursor,const StringView & pattern,long long count,Output output)774 long long RedisCluster::zscan(const StringView &key,
775 long long cursor,
776 const StringView &pattern,
777 long long count,
778 Output output) {
779 auto reply = command(cmd::zscan, key, cursor, pattern, count);
780
781 return reply::parse_scan_reply(*reply, output);
782 }
783
784 template <typename Output>
zscan(const StringView & key,long long cursor,const StringView & pattern,Output output)785 inline long long RedisCluster::zscan(const StringView &key,
786 long long cursor,
787 const StringView &pattern,
788 Output output) {
789 return zscan(key, cursor, pattern, 10, output);
790 }
791
792 template <typename Output>
zscan(const StringView & key,long long cursor,long long count,Output output)793 inline long long RedisCluster::zscan(const StringView &key,
794 long long cursor,
795 long long count,
796 Output output) {
797 return zscan(key, cursor, "*", count, output);
798 }
799
800 template <typename Output>
zscan(const StringView & key,long long cursor,Output output)801 inline long long RedisCluster::zscan(const StringView &key,
802 long long cursor,
803 Output output) {
804 return zscan(key, cursor, "*", 10, output);
805 }
806
807 template <typename Input>
zunionstore(const StringView & destination,Input first,Input last,Aggregation type)808 long long RedisCluster::zunionstore(const StringView &destination,
809 Input first,
810 Input last,
811 Aggregation type) {
812 if (first == last) {
813 throw Error("ZUNIONSTORE: no key specified");
814 }
815
816 auto reply = command(cmd::zunionstore_range<Input>,
817 destination,
818 first,
819 last,
820 type);
821
822 return reply::parse<long long>(*reply);
823 }
824
825 // HYPERLOGLOG commands.
826
827 template <typename Input>
pfadd(const StringView & key,Input first,Input last)828 bool RedisCluster::pfadd(const StringView &key, Input first, Input last) {
829 if (first == last) {
830 throw Error("PFADD: no key specified");
831 }
832
833 auto reply = command(cmd::pfadd_range<Input>, key, first, last);
834
835 return reply::parse<bool>(*reply);
836 }
837
838 template <typename Input>
pfcount(Input first,Input last)839 long long RedisCluster::pfcount(Input first, Input last) {
840 if (first == last) {
841 throw Error("PFCOUNT: no key specified");
842 }
843
844 auto reply = command(cmd::pfcount_range<Input>, first, last);
845
846 return reply::parse<long long>(*reply);
847 }
848
849 template <typename Input>
pfmerge(const StringView & destination,Input first,Input last)850 void RedisCluster::pfmerge(const StringView &destination,
851 Input first,
852 Input last) {
853 if (first == last) {
854 throw Error("PFMERGE: no key specified");
855 }
856
857 auto reply = command(cmd::pfmerge_range<Input>, destination, first, last);
858
859 reply::parse<void>(*reply);
860 }
861
862 // GEO commands.
863
864 template <typename Input>
geoadd(const StringView & key,Input first,Input last)865 inline long long RedisCluster::geoadd(const StringView &key,
866 Input first,
867 Input last) {
868 if (first == last) {
869 throw Error("GEOADD: no key specified");
870 }
871
872 auto reply = command(cmd::geoadd_range<Input>, key, first, last);
873
874 return reply::parse<long long>(*reply);
875 }
876
877 template <typename Input, typename Output>
geohash(const StringView & key,Input first,Input last,Output output)878 void RedisCluster::geohash(const StringView &key, Input first, Input last, Output output) {
879 if (first == last) {
880 throw Error("GEOHASH: no key specified");
881 }
882
883 auto reply = command(cmd::geohash_range<Input>, key, first, last);
884
885 reply::to_array(*reply, output);
886 }
887
888 template <typename Input, typename Output>
geopos(const StringView & key,Input first,Input last,Output output)889 void RedisCluster::geopos(const StringView &key, Input first, Input last, Output output) {
890 if (first == last) {
891 throw Error("GEOPOS: no key specified");
892 }
893
894 auto reply = command(cmd::geopos_range<Input>, key, first, last);
895
896 reply::to_array(*reply, output);
897 }
898
899 template <typename Output>
georadius(const StringView & key,const std::pair<double,double> & loc,double radius,GeoUnit unit,long long count,bool asc,Output output)900 void RedisCluster::georadius(const StringView &key,
901 const std::pair<double, double> &loc,
902 double radius,
903 GeoUnit unit,
904 long long count,
905 bool asc,
906 Output output) {
907 auto reply = command(cmd::georadius,
908 key,
909 loc,
910 radius,
911 unit,
912 count,
913 asc,
914 WithCoord<typename IterType<Output>::type>::value,
915 WithDist<typename IterType<Output>::type>::value,
916 WithHash<typename IterType<Output>::type>::value);
917
918 reply::to_array(*reply, output);
919 }
920
921 template <typename Output>
georadiusbymember(const StringView & key,const StringView & member,double radius,GeoUnit unit,long long count,bool asc,Output output)922 void RedisCluster::georadiusbymember(const StringView &key,
923 const StringView &member,
924 double radius,
925 GeoUnit unit,
926 long long count,
927 bool asc,
928 Output output) {
929 auto reply = command(cmd::georadiusbymember,
930 key,
931 member,
932 radius,
933 unit,
934 count,
935 asc,
936 WithCoord<typename IterType<Output>::type>::value,
937 WithDist<typename IterType<Output>::type>::value,
938 WithHash<typename IterType<Output>::type>::value);
939
940 reply::to_array(*reply, output);
941 }
942
943 // SCRIPTING commands.
944
945 template <typename Result>
eval(const StringView & script,std::initializer_list<StringView> keys,std::initializer_list<StringView> args)946 Result RedisCluster::eval(const StringView &script,
947 std::initializer_list<StringView> keys,
948 std::initializer_list<StringView> args) {
949 if (keys.size() == 0) {
950 throw Error("DO NOT support Lua script without key");
951 }
952
953 auto reply = _command(cmd::eval, *keys.begin(), script, keys, args);
954
955 return reply::parse<Result>(*reply);
956 }
957
958 template <typename Output>
eval(const StringView & script,std::initializer_list<StringView> keys,std::initializer_list<StringView> args,Output output)959 void RedisCluster::eval(const StringView &script,
960 std::initializer_list<StringView> keys,
961 std::initializer_list<StringView> args,
962 Output output) {
963 if (keys.size() == 0) {
964 throw Error("DO NOT support Lua script without key");
965 }
966
967 auto reply = _command(cmd::eval, *keys.begin(), script, keys, args);
968
969 reply::to_array(*reply, output);
970 }
971
972 template <typename Result>
evalsha(const StringView & script,std::initializer_list<StringView> keys,std::initializer_list<StringView> args)973 Result RedisCluster::evalsha(const StringView &script,
974 std::initializer_list<StringView> keys,
975 std::initializer_list<StringView> args) {
976 if (keys.size() == 0) {
977 throw Error("DO NOT support Lua script without key");
978 }
979
980 auto reply = _command(cmd::evalsha, *keys.begin(), script, keys, args);
981
982 return reply::parse<Result>(*reply);
983 }
984
985 template <typename Output>
evalsha(const StringView & script,std::initializer_list<StringView> keys,std::initializer_list<StringView> args,Output output)986 void RedisCluster::evalsha(const StringView &script,
987 std::initializer_list<StringView> keys,
988 std::initializer_list<StringView> args,
989 Output output) {
990 if (keys.size() == 0) {
991 throw Error("DO NOT support Lua script without key");
992 }
993
994 auto reply = command(cmd::evalsha, *keys.begin(), script, keys, args);
995
996 reply::to_array(*reply, output);
997 }
998
999 // Stream commands.
1000
1001 template <typename Input>
xack(const StringView & key,const StringView & group,Input first,Input last)1002 long long RedisCluster::xack(const StringView &key,
1003 const StringView &group,
1004 Input first,
1005 Input last) {
1006 auto reply = command(cmd::xack_range<Input>, key, group, first, last);
1007
1008 return reply::parse<long long>(*reply);
1009 }
1010
1011 template <typename Input>
xadd(const StringView & key,const StringView & id,Input first,Input last)1012 std::string RedisCluster::xadd(const StringView &key,
1013 const StringView &id,
1014 Input first,
1015 Input last) {
1016 auto reply = command(cmd::xadd_range<Input>, key, id, first, last);
1017
1018 return reply::parse<std::string>(*reply);
1019 }
1020
1021 template <typename Input>
xadd(const StringView & key,const StringView & id,Input first,Input last,long long count,bool approx)1022 std::string RedisCluster::xadd(const StringView &key,
1023 const StringView &id,
1024 Input first,
1025 Input last,
1026 long long count,
1027 bool approx) {
1028 auto reply = command(cmd::xadd_maxlen_range<Input>, key, id, first, last, count, approx);
1029
1030 return reply::parse<std::string>(*reply);
1031 }
1032
1033 template <typename Output>
xclaim(const StringView & key,const StringView & group,const StringView & consumer,const std::chrono::milliseconds & min_idle_time,const StringView & id,Output output)1034 void RedisCluster::xclaim(const StringView &key,
1035 const StringView &group,
1036 const StringView &consumer,
1037 const std::chrono::milliseconds &min_idle_time,
1038 const StringView &id,
1039 Output output) {
1040 auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id);
1041
1042 reply::to_array(*reply, output);
1043 }
1044
1045 template <typename Input, typename Output>
xclaim(const StringView & key,const StringView & group,const StringView & consumer,const std::chrono::milliseconds & min_idle_time,Input first,Input last,Output output)1046 void RedisCluster::xclaim(const StringView &key,
1047 const StringView &group,
1048 const StringView &consumer,
1049 const std::chrono::milliseconds &min_idle_time,
1050 Input first,
1051 Input last,
1052 Output output) {
1053 auto reply = command(cmd::xclaim_range<Input>,
1054 key,
1055 group,
1056 consumer,
1057 min_idle_time.count(),
1058 first,
1059 last);
1060
1061 reply::to_array(*reply, output);
1062 }
1063
1064 template <typename Input>
xdel(const StringView & key,Input first,Input last)1065 long long RedisCluster::xdel(const StringView &key, Input first, Input last) {
1066 auto reply = command(cmd::xdel_range<Input>, key, first, last);
1067
1068 return reply::parse<long long>(*reply);
1069 }
1070
1071 template <typename Output>
xpending(const StringView & key,const StringView & group,Output output)1072 auto RedisCluster::xpending(const StringView &key, const StringView &group, Output output)
1073 -> std::tuple<long long, OptionalString, OptionalString> {
1074 auto reply = command(cmd::xpending, key, group);
1075
1076 return reply::parse_xpending_reply(*reply, output);
1077 }
1078
1079 template <typename Output>
xpending(const StringView & key,const StringView & group,const StringView & start,const StringView & end,long long count,Output output)1080 void RedisCluster::xpending(const StringView &key,
1081 const StringView &group,
1082 const StringView &start,
1083 const StringView &end,
1084 long long count,
1085 Output output) {
1086 auto reply = command(cmd::xpending_detail, key, group, start, end, count);
1087
1088 reply::to_array(*reply, output);
1089 }
1090
1091 template <typename Output>
xpending(const StringView & key,const StringView & group,const StringView & start,const StringView & end,long long count,const StringView & consumer,Output output)1092 void RedisCluster::xpending(const StringView &key,
1093 const StringView &group,
1094 const StringView &start,
1095 const StringView &end,
1096 long long count,
1097 const StringView &consumer,
1098 Output output) {
1099 auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer);
1100
1101 reply::to_array(*reply, output);
1102 }
1103
1104 template <typename Output>
xrange(const StringView & key,const StringView & start,const StringView & end,Output output)1105 void RedisCluster::xrange(const StringView &key,
1106 const StringView &start,
1107 const StringView &end,
1108 Output output) {
1109 auto reply = command(cmd::xrange, key, start, end);
1110
1111 reply::to_array(*reply, output);
1112 }
1113
1114 template <typename Output>
xrange(const StringView & key,const StringView & start,const StringView & end,long long count,Output output)1115 void RedisCluster::xrange(const StringView &key,
1116 const StringView &start,
1117 const StringView &end,
1118 long long count,
1119 Output output) {
1120 auto reply = command(cmd::xrange_count, key, start, end, count);
1121
1122 reply::to_array(*reply, output);
1123 }
1124
1125 template <typename Output>
xread(const StringView & key,const StringView & id,long long count,Output output)1126 void RedisCluster::xread(const StringView &key,
1127 const StringView &id,
1128 long long count,
1129 Output output) {
1130 auto reply = command(cmd::xread, key, id, count);
1131
1132 if (!reply::is_nil(*reply)) {
1133 reply::to_array(*reply, output);
1134 }
1135 }
1136
1137 template <typename Input, typename Output>
xread(Input first,Input last,long long count,Output output)1138 auto RedisCluster::xread(Input first, Input last, long long count, Output output)
1139 -> typename std::enable_if<!std::is_convertible<Input, StringView>::value>::type {
1140 if (first == last) {
1141 throw Error("XREAD: no key specified");
1142 }
1143
1144 auto reply = command(cmd::xread_range<Input>, first, last, count);
1145
1146 if (!reply::is_nil(*reply)) {
1147 reply::to_array(*reply, output);
1148 }
1149 }
1150
1151 template <typename Output>
xread(const StringView & key,const StringView & id,const std::chrono::milliseconds & timeout,long long count,Output output)1152 void RedisCluster::xread(const StringView &key,
1153 const StringView &id,
1154 const std::chrono::milliseconds &timeout,
1155 long long count,
1156 Output output) {
1157 auto reply = command(cmd::xread_block, key, id, timeout.count(), count);
1158
1159 if (!reply::is_nil(*reply)) {
1160 reply::to_array(*reply, output);
1161 }
1162 }
1163
1164 template <typename Input, typename Output>
xread(Input first,Input last,const std::chrono::milliseconds & timeout,long long count,Output output)1165 auto RedisCluster::xread(Input first,
1166 Input last,
1167 const std::chrono::milliseconds &timeout,
1168 long long count,
1169 Output output)
1170 -> typename std::enable_if<!std::is_convertible<Input, StringView>::value>::type {
1171 if (first == last) {
1172 throw Error("XREAD: no key specified");
1173 }
1174
1175 auto reply = command(cmd::xread_block_range<Input>, first, last, timeout.count(), count);
1176
1177 if (!reply::is_nil(*reply)) {
1178 reply::to_array(*reply, output);
1179 }
1180 }
1181
1182 template <typename Output>
xreadgroup(const StringView & group,const StringView & consumer,const StringView & key,const StringView & id,long long count,bool noack,Output output)1183 void RedisCluster::xreadgroup(const StringView &group,
1184 const StringView &consumer,
1185 const StringView &key,
1186 const StringView &id,
1187 long long count,
1188 bool noack,
1189 Output output) {
1190 auto reply = _command(cmd::xreadgroup, key, group, consumer, key, id, count, noack);
1191
1192 if (!reply::is_nil(*reply)) {
1193 reply::to_array(*reply, output);
1194 }
1195 }
1196
1197 template <typename Input, typename Output>
xreadgroup(const StringView & group,const StringView & consumer,Input first,Input last,long long count,bool noack,Output output)1198 auto RedisCluster::xreadgroup(const StringView &group,
1199 const StringView &consumer,
1200 Input first,
1201 Input last,
1202 long long count,
1203 bool noack,
1204 Output output)
1205 -> typename std::enable_if<!std::is_convertible<Input, StringView>::value>::type {
1206 if (first == last) {
1207 throw Error("XREADGROUP: no key specified");
1208 }
1209
1210 auto reply = _command(cmd::xreadgroup_range<Input>,
1211 first->first,
1212 group,
1213 consumer,
1214 first,
1215 last,
1216 count,
1217 noack);
1218
1219 if (!reply::is_nil(*reply)) {
1220 reply::to_array(*reply, output);
1221 }
1222 }
1223
1224 template <typename Output>
xreadgroup(const StringView & group,const StringView & consumer,const StringView & key,const StringView & id,const std::chrono::milliseconds & timeout,long long count,bool noack,Output output)1225 void RedisCluster::xreadgroup(const StringView &group,
1226 const StringView &consumer,
1227 const StringView &key,
1228 const StringView &id,
1229 const std::chrono::milliseconds &timeout,
1230 long long count,
1231 bool noack,
1232 Output output) {
1233 auto reply = _command(cmd::xreadgroup_block,
1234 key,
1235 group,
1236 consumer,
1237 key,
1238 id,
1239 timeout.count(),
1240 count,
1241 noack);
1242
1243 if (!reply::is_nil(*reply)) {
1244 reply::to_array(*reply, output);
1245 }
1246 }
1247
1248 template <typename Input, typename Output>
xreadgroup(const StringView & group,const StringView & consumer,Input first,Input last,const std::chrono::milliseconds & timeout,long long count,bool noack,Output output)1249 auto RedisCluster::xreadgroup(const StringView &group,
1250 const StringView &consumer,
1251 Input first,
1252 Input last,
1253 const std::chrono::milliseconds &timeout,
1254 long long count,
1255 bool noack,
1256 Output output)
1257 -> typename std::enable_if<!std::is_convertible<Input, StringView>::value>::type {
1258 if (first == last) {
1259 throw Error("XREADGROUP: no key specified");
1260 }
1261
1262 auto reply = _command(cmd::xreadgroup_block_range<Input>,
1263 first->first,
1264 group,
1265 consumer,
1266 first,
1267 last,
1268 timeout.count(),
1269 count,
1270 noack);
1271
1272 if (!reply::is_nil(*reply)) {
1273 reply::to_array(*reply, output);
1274 }
1275 }
1276
1277 template <typename Output>
xrevrange(const StringView & key,const StringView & end,const StringView & start,Output output)1278 void RedisCluster::xrevrange(const StringView &key,
1279 const StringView &end,
1280 const StringView &start,
1281 Output output) {
1282 auto reply = command(cmd::xrevrange, key, end, start);
1283
1284 reply::to_array(*reply, output);
1285 }
1286
1287 template <typename Output>
xrevrange(const StringView & key,const StringView & end,const StringView & start,long long count,Output output)1288 void RedisCluster::xrevrange(const StringView &key,
1289 const StringView &end,
1290 const StringView &start,
1291 long long count,
1292 Output output) {
1293 auto reply = command(cmd::xrevrange_count, key, end, start, count);
1294
1295 reply::to_array(*reply, output);
1296 }
1297
1298 template <typename Cmd, typename Key, typename ...Args>
_generic_command(Cmd cmd,Key && key,Args &&...args)1299 auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args)
1300 -> typename std::enable_if<std::is_convertible<Key, StringView>::value,
1301 ReplyUPtr>::type {
1302 return command(cmd, std::forward<Key>(key), std::forward<Args>(args)...);
1303 }
1304
1305 template <typename Cmd, typename Key, typename ...Args>
_generic_command(Cmd cmd,Key && key,Args &&...args)1306 auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args)
1307 -> typename std::enable_if<std::is_arithmetic<typename std::decay<Key>::type>::value,
1308 ReplyUPtr>::type {
1309 auto k = std::to_string(std::forward<Key>(key));
1310 return command(cmd, k, std::forward<Args>(args)...);
1311 }
1312
1313 template <typename Cmd, typename ...Args>
_command(Cmd cmd,std::true_type,const StringView & key,Args &&...args)1314 ReplyUPtr RedisCluster::_command(Cmd cmd, std::true_type, const StringView &key, Args &&...args) {
1315 return _command(cmd, key, key, std::forward<Args>(args)...);
1316 }
1317
1318 template <typename Cmd, typename Input, typename ...Args>
_command(Cmd cmd,std::false_type,Input && first,Args &&...args)1319 ReplyUPtr RedisCluster::_command(Cmd cmd, std::false_type, Input &&first, Args &&...args) {
1320 return _range_command(cmd,
1321 std::is_convertible<
1322 typename std::decay<
1323 decltype(*std::declval<Input>())>::type, StringView>(),
1324 std::forward<Input>(first),
1325 std::forward<Args>(args)...);
1326 }
1327
1328 template <typename Cmd, typename Input, typename ...Args>
_range_command(Cmd cmd,std::true_type,Input input,Args &&...args)1329 ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::true_type, Input input, Args &&...args) {
1330 return _command(cmd, *input, input, std::forward<Args>(args)...);
1331 }
1332
1333 template <typename Cmd, typename Input, typename ...Args>
_range_command(Cmd cmd,std::false_type,Input input,Args &&...args)1334 ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::false_type, Input input, Args &&...args) {
1335 return _command(cmd, std::get<0>(*input), input, std::forward<Args>(args)...);
1336 }
1337
1338 template <typename Cmd, typename ...Args>
_command(Cmd cmd,Connection & connection,Args &&...args)1339 ReplyUPtr RedisCluster::_command(Cmd cmd, Connection &connection, Args &&...args) {
1340 assert(!connection.broken());
1341
1342 cmd(connection, std::forward<Args>(args)...);
1343
1344 return connection.recv();
1345 }
1346
1347 template <typename Cmd, typename ...Args>
_command(Cmd cmd,const StringView & key,Args &&...args)1348 ReplyUPtr RedisCluster::_command(Cmd cmd, const StringView &key, Args &&...args) {
1349 for (auto idx = 0; idx < 2; ++idx) {
1350 try {
1351 auto guarded_connection = _pool.fetch(key);
1352
1353 return _command(cmd, guarded_connection.connection(), std::forward<Args>(args)...);
1354 } catch (const IoError &err) {
1355 // When master is down, one of its replicas will be promoted to be the new master.
1356 // If we try to send command to the old master, we'll get an *IoError*.
1357 // In this case, we need to update the slots mapping.
1358 _pool.update();
1359 } catch (const ClosedError &err) {
1360 // Node might be removed.
1361 // 1. Get up-to-date slot mapping to check if the node still exists.
1362 _pool.update();
1363
1364 // TODO:
1365 // 2. If it's NOT exist, update slot mapping, and retry.
1366 // 3. If it's still exist, that means the node is down, NOT removed, throw exception.
1367 } catch (const MovedError &err) {
1368 // Slot mapping has been changed, update it and try again.
1369 _pool.update();
1370 } catch (const AskError &err) {
1371 auto guarded_connection = _pool.fetch(err.node());
1372 auto &connection = guarded_connection.connection();
1373
1374 // 1. send ASKING command.
1375 _asking(connection);
1376
1377 // 2. resend last command.
1378 try {
1379 return _command(cmd, connection, std::forward<Args>(args)...);
1380 } catch (const MovedError &err) {
1381 throw Error("Slot migrating... ASKING node hasn't been set to IMPORTING state");
1382 }
1383 } // For other exceptions, just throw it.
1384 }
1385
1386 // Possible failures:
1387 // 1. Source node has already run 'CLUSTER SETSLOT xxx NODE xxx',
1388 // while the destination node has NOT run it.
1389 // In this case, client will be redirected by both nodes with MovedError.
1390 // 2. Other failures...
1391 throw Error("Failed to send command with key: " + std::string(key.data(), key.size()));
1392 }
1393
1394 template <typename Cmd, typename ...Args>
_score_command(std::true_type,Cmd cmd,Args &&...args)1395 inline ReplyUPtr RedisCluster::_score_command(std::true_type, Cmd cmd, Args &&... args) {
1396 return command(cmd, std::forward<Args>(args)..., true);
1397 }
1398
1399 template <typename Cmd, typename ...Args>
_score_command(std::false_type,Cmd cmd,Args &&...args)1400 inline ReplyUPtr RedisCluster::_score_command(std::false_type, Cmd cmd, Args &&... args) {
1401 return command(cmd, std::forward<Args>(args)..., false);
1402 }
1403
1404 template <typename Output, typename Cmd, typename ...Args>
_score_command(Cmd cmd,Args &&...args)1405 inline ReplyUPtr RedisCluster::_score_command(Cmd cmd, Args &&... args) {
1406 return _score_command(typename IsKvPairIter<Output>::type(),
1407 cmd,
1408 std::forward<Args>(args)...);
1409 }
1410
1411 }
1412
1413 }
1414
1415 #endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP
1416