nutcracker 0.3.0.12 → 0.4.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +5 -13
  2. data/README.md +3 -3
  3. data/Rakefile +12 -10
  4. data/ext/nutcracker/Makefile.in +215 -162
  5. data/ext/nutcracker/README.md +16 -4
  6. data/ext/nutcracker/aclocal.m4 +432 -254
  7. data/ext/nutcracker/{contrib/yaml-0.1.4/configure → autom4te.cache/output.0} +11367 -4545
  8. data/ext/nutcracker/autom4te.cache/output.1 +19907 -0
  9. data/ext/nutcracker/autom4te.cache/output.2 +19907 -0
  10. data/ext/nutcracker/autom4te.cache/requests +518 -0
  11. data/ext/nutcracker/autom4te.cache/traces.0 +2715 -0
  12. data/ext/nutcracker/autom4te.cache/traces.1 +967 -0
  13. data/ext/nutcracker/autom4te.cache/traces.2 +2715 -0
  14. data/ext/nutcracker/config/compile +347 -0
  15. data/ext/nutcracker/config/config.guess +116 -78
  16. data/ext/nutcracker/config/config.sub +65 -45
  17. data/ext/nutcracker/config/depcomp +295 -192
  18. data/ext/nutcracker/config/install-sh +7 -7
  19. data/ext/nutcracker/config/ltmain.sh +15 -20
  20. data/ext/nutcracker/config/missing +149 -265
  21. data/ext/nutcracker/configure +493 -367
  22. data/ext/nutcracker/contrib/Makefile.in +158 -116
  23. data/ext/nutcracker/extconf.rb +0 -1
  24. data/ext/nutcracker/m4/libtool.m4 +4 -23
  25. data/ext/nutcracker/m4/ltoptions.m4 +0 -0
  26. data/ext/nutcracker/m4/ltsugar.m4 +0 -0
  27. data/ext/nutcracker/m4/ltversion.m4 +0 -0
  28. data/ext/nutcracker/m4/lt~obsolete.m4 +0 -0
  29. data/ext/nutcracker/notes/recommendation.md +1 -1
  30. data/ext/nutcracker/notes/redis.md +35 -3
  31. data/ext/nutcracker/scripts/benchmark-mget.py +43 -0
  32. data/ext/nutcracker/scripts/nutcracker.spec +61 -3
  33. data/ext/nutcracker/scripts/redis-check.sh +43 -0
  34. data/ext/nutcracker/src/Makefile.in +205 -142
  35. data/ext/nutcracker/src/event/Makefile.in +164 -66
  36. data/ext/nutcracker/src/hashkit/Makefile.in +164 -66
  37. data/ext/nutcracker/src/nc_conf.c +2 -0
  38. data/ext/nutcracker/src/nc_connection.c +31 -0
  39. data/ext/nutcracker/src/nc_connection.h +3 -0
  40. data/ext/nutcracker/src/nc_core.c +38 -2
  41. data/ext/nutcracker/src/nc_core.h +11 -0
  42. data/ext/nutcracker/src/nc_log.c +90 -12
  43. data/ext/nutcracker/src/nc_log.h +11 -0
  44. data/ext/nutcracker/src/nc_mbuf.h +1 -1
  45. data/ext/nutcracker/src/nc_message.c +162 -116
  46. data/ext/nutcracker/src/nc_message.h +161 -129
  47. data/ext/nutcracker/src/nc_proxy.c +34 -4
  48. data/ext/nutcracker/src/nc_request.c +158 -32
  49. data/ext/nutcracker/src/nc_server.c +59 -5
  50. data/ext/nutcracker/src/nc_server.h +1 -0
  51. data/ext/nutcracker/src/nc_signal.c +2 -2
  52. data/ext/nutcracker/src/nc_stats.c +21 -0
  53. data/ext/nutcracker/src/nc_stats.h +28 -26
  54. data/ext/nutcracker/src/nc_string.c +176 -1
  55. data/ext/nutcracker/src/nc_string.h +26 -0
  56. data/ext/nutcracker/src/nc_util.c +12 -0
  57. data/ext/nutcracker/src/nc_util.h +1 -0
  58. data/ext/nutcracker/src/proto/Makefile.in +164 -66
  59. data/ext/nutcracker/src/proto/nc_memcache.c +279 -88
  60. data/ext/nutcracker/src/proto/nc_proto.h +3 -4
  61. data/ext/nutcracker/src/proto/nc_redis.c +561 -134
  62. data/lib/nutcracker/version.rb +1 -1
  63. metadata +31 -67
  64. data/ext/nutcracker/contrib/yaml-0.1.4/LICENSE +0 -19
  65. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.am +0 -20
  66. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.in +0 -736
  67. data/ext/nutcracker/contrib/yaml-0.1.4/README +0 -27
  68. data/ext/nutcracker/contrib/yaml-0.1.4/aclocal.m4 +0 -956
  69. data/ext/nutcracker/contrib/yaml-0.1.4/config.h.in +0 -80
  70. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.guess +0 -1561
  71. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.sub +0 -1686
  72. data/ext/nutcracker/contrib/yaml-0.1.4/config/depcomp +0 -630
  73. data/ext/nutcracker/contrib/yaml-0.1.4/config/install-sh +0 -520
  74. data/ext/nutcracker/contrib/yaml-0.1.4/config/ltmain.sh +0 -8406
  75. data/ext/nutcracker/contrib/yaml-0.1.4/config/missing +0 -376
  76. data/ext/nutcracker/contrib/yaml-0.1.4/configure.ac +0 -75
  77. data/ext/nutcracker/contrib/yaml-0.1.4/doc/doxygen.cfg +0 -222
  78. data/ext/nutcracker/contrib/yaml-0.1.4/include/yaml.h +0 -1971
  79. data/ext/nutcracker/contrib/yaml-0.1.4/m4/libtool.m4 +0 -7357
  80. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltoptions.m4 +0 -368
  81. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltsugar.m4 +0 -123
  82. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltversion.m4 +0 -23
  83. data/ext/nutcracker/contrib/yaml-0.1.4/m4/lt~obsolete.m4 +0 -92
  84. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.am +0 -4
  85. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.in +0 -484
  86. data/ext/nutcracker/contrib/yaml-0.1.4/src/api.c +0 -1392
  87. data/ext/nutcracker/contrib/yaml-0.1.4/src/dumper.c +0 -394
  88. data/ext/nutcracker/contrib/yaml-0.1.4/src/emitter.c +0 -2329
  89. data/ext/nutcracker/contrib/yaml-0.1.4/src/loader.c +0 -432
  90. data/ext/nutcracker/contrib/yaml-0.1.4/src/parser.c +0 -1374
  91. data/ext/nutcracker/contrib/yaml-0.1.4/src/reader.c +0 -465
  92. data/ext/nutcracker/contrib/yaml-0.1.4/src/scanner.c +0 -3570
  93. data/ext/nutcracker/contrib/yaml-0.1.4/src/writer.c +0 -141
  94. data/ext/nutcracker/contrib/yaml-0.1.4/src/yaml_private.h +0 -640
  95. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.am +0 -8
  96. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.in +0 -675
  97. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor-alt.c +0 -800
  98. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor.c +0 -1130
  99. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter-alt.c +0 -217
  100. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter.c +0 -202
  101. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-dumper.c +0 -311
  102. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-emitter.c +0 -327
  103. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-loader.c +0 -63
  104. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-parser.c +0 -63
  105. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-scanner.c +0 -63
  106. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-reader.c +0 -354
  107. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-version.c +0 -29
@@ -140,16 +140,15 @@
140
140
 
141
141
  void memcache_parse_req(struct msg *r);
142
142
  void memcache_parse_rsp(struct msg *r);
143
- void memcache_pre_splitcopy(struct mbuf *mbuf, void *arg);
144
- rstatus_t memcache_post_splitcopy(struct msg *r);
145
143
  void memcache_pre_coalesce(struct msg *r);
146
144
  void memcache_post_coalesce(struct msg *r);
145
+ rstatus_t memcache_fragment(struct msg *r, uint32_t ncontinuum, struct msg_tqh *frag_msgq);
147
146
 
148
147
  void redis_parse_req(struct msg *r);
149
148
  void redis_parse_rsp(struct msg *r);
150
- void redis_pre_splitcopy(struct mbuf *mbuf, void *arg);
151
- rstatus_t redis_post_splitcopy(struct msg *r);
152
149
  void redis_pre_coalesce(struct msg *r);
153
150
  void redis_post_coalesce(struct msg *r);
151
+ rstatus_t redis_fragment(struct msg *r, uint32_t ncontinuum, struct msg_tqh *frag_msgq);
152
+ rstatus_t redis_reply(struct msg *r);
154
153
 
155
154
  #endif
@@ -21,6 +21,25 @@
21
21
  #include <nc_core.h>
22
22
  #include <nc_proto.h>
23
23
 
24
+ /*
25
+ * Return true, if the redis command take no key, otherwise
26
+ * return false
27
+ */
28
+ static bool
29
+ redis_argz(struct msg *r)
30
+ {
31
+ switch (r->type) {
32
+ case MSG_REQ_REDIS_PING:
33
+ case MSG_REQ_REDIS_QUIT:
34
+ return true;
35
+
36
+ default:
37
+ break;
38
+ }
39
+
40
+ return false;
41
+ }
42
+
24
43
  /*
25
44
  * Return true, if the redis command accepts no arguments, otherwise
26
45
  * return false
@@ -32,6 +51,7 @@ redis_arg0(struct msg *r)
32
51
  case MSG_REQ_REDIS_EXISTS:
33
52
  case MSG_REQ_REDIS_PERSIST:
34
53
  case MSG_REQ_REDIS_PTTL:
54
+ case MSG_REQ_REDIS_SORT:
35
55
  case MSG_REQ_REDIS_TTL:
36
56
  case MSG_REQ_REDIS_TYPE:
37
57
  case MSG_REQ_REDIS_DUMP:
@@ -55,6 +75,8 @@ redis_arg0(struct msg *r)
55
75
  case MSG_REQ_REDIS_SPOP:
56
76
 
57
77
  case MSG_REQ_REDIS_ZCARD:
78
+
79
+ case MSG_REQ_REDIS_PFCOUNT:
58
80
  return true;
59
81
 
60
82
  default:
@@ -134,7 +156,9 @@ redis_arg2(struct msg *r)
134
156
  case MSG_REQ_REDIS_SMOVE:
135
157
 
136
158
  case MSG_REQ_REDIS_ZCOUNT:
159
+ case MSG_REQ_REDIS_ZLEXCOUNT:
137
160
  case MSG_REQ_REDIS_ZINCRBY:
161
+ case MSG_REQ_REDIS_ZREMRANGEBYLEX:
138
162
  case MSG_REQ_REDIS_ZREMRANGEBYRANK:
139
163
  case MSG_REQ_REDIS_ZREMRANGEBYSCORE:
140
164
 
@@ -180,6 +204,7 @@ redis_argn(struct msg *r)
180
204
  case MSG_REQ_REDIS_HDEL:
181
205
  case MSG_REQ_REDIS_HMGET:
182
206
  case MSG_REQ_REDIS_HMSET:
207
+ case MSG_REQ_REDIS_HSCAN:
183
208
 
184
209
  case MSG_REQ_REDIS_LPUSH:
185
210
  case MSG_REQ_REDIS_RPUSH:
@@ -193,6 +218,10 @@ redis_argn(struct msg *r)
193
218
  case MSG_REQ_REDIS_SUNION:
194
219
  case MSG_REQ_REDIS_SUNIONSTORE:
195
220
  case MSG_REQ_REDIS_SRANDMEMBER:
221
+ case MSG_REQ_REDIS_SSCAN:
222
+
223
+ case MSG_REQ_REDIS_PFADD:
224
+ case MSG_REQ_REDIS_PFMERGE:
196
225
 
197
226
  case MSG_REQ_REDIS_ZADD:
198
227
  case MSG_REQ_REDIS_ZINTERSTORE:
@@ -200,8 +229,10 @@ redis_argn(struct msg *r)
200
229
  case MSG_REQ_REDIS_ZRANGEBYSCORE:
201
230
  case MSG_REQ_REDIS_ZREM:
202
231
  case MSG_REQ_REDIS_ZREVRANGE:
232
+ case MSG_REQ_REDIS_ZRANGEBYLEX:
203
233
  case MSG_REQ_REDIS_ZREVRANGEBYSCORE:
204
234
  case MSG_REQ_REDIS_ZUNIONSTORE:
235
+ case MSG_REQ_REDIS_ZSCAN:
205
236
  return true;
206
237
 
207
238
  default:
@@ -230,6 +261,24 @@ redis_argx(struct msg *r)
230
261
  return false;
231
262
  }
232
263
 
264
+ /*
265
+ * Return true, if the redis command is a vector command accepting one or
266
+ * more key-value pairs, otherwise return false
267
+ */
268
+ static bool
269
+ redis_argkvx(struct msg *r)
270
+ {
271
+ switch (r->type) {
272
+ case MSG_REQ_REDIS_MSET:
273
+ return true;
274
+
275
+ default:
276
+ break;
277
+ }
278
+
279
+ return false;
280
+ }
281
+
233
282
  /*
234
283
  * Return true, if the redis command is either EVAL or EVALSHA. These commands
235
284
  * have a special format with exactly 2 arguments, followed by one or more keys,
@@ -309,7 +358,6 @@ redis_parse_req(struct msg *r)
309
358
  SW_ARGN_LEN_LF,
310
359
  SW_ARGN,
311
360
  SW_ARGN_LF,
312
- SW_FRAGMENT,
313
361
  SW_SENTINEL
314
362
  } state;
315
363
 
@@ -540,6 +588,10 @@ redis_parse_req(struct msg *r)
540
588
  r->type = MSG_REQ_REDIS_MGET;
541
589
  break;
542
590
  }
591
+ if (str4icmp(m, 'm', 's', 'e', 't')) {
592
+ r->type = MSG_REQ_REDIS_MSET;
593
+ break;
594
+ }
543
595
 
544
596
  if (str4icmp(m, 'z', 'a', 'd', 'd')) {
545
597
  r->type = MSG_REQ_REDIS_ZADD;
@@ -556,6 +608,23 @@ redis_parse_req(struct msg *r)
556
608
  break;
557
609
  }
558
610
 
611
+ if (str4icmp(m, 's', 'o', 'r', 't')) {
612
+ r->type = MSG_REQ_REDIS_SORT;
613
+ break;
614
+ }
615
+
616
+ if (str4icmp(m, 'p', 'i', 'n', 'g')) {
617
+ r->type = MSG_REQ_REDIS_PING;
618
+ r->noforward = 1;
619
+ break;
620
+ }
621
+
622
+ if (str4icmp(m, 'q', 'u', 'i', 't')) {
623
+ r->type = MSG_REQ_REDIS_QUIT;
624
+ r->quit = 1;
625
+ break;
626
+ }
627
+
559
628
  break;
560
629
 
561
630
  case 5:
@@ -579,6 +648,11 @@ redis_parse_req(struct msg *r)
579
648
  break;
580
649
  }
581
650
 
651
+ if (str5icmp(m, 'h', 's', 'c', 'a', 'n')) {
652
+ r->type = MSG_REQ_REDIS_HSCAN;
653
+ break;
654
+ }
655
+
582
656
  if (str5icmp(m, 'l', 'p', 'u', 's', 'h')) {
583
657
  r->type = MSG_REQ_REDIS_LPUSH;
584
658
  break;
@@ -619,6 +693,11 @@ redis_parse_req(struct msg *r)
619
693
  break;
620
694
  }
621
695
 
696
+ if (str5icmp(m, 's', 's', 'c', 'a', 'n')) {
697
+ r->type = MSG_REQ_REDIS_SSCAN;
698
+ break;
699
+ }
700
+
622
701
  if (str5icmp(m, 'z', 'c', 'a', 'r', 'd')) {
623
702
  r->type = MSG_REQ_REDIS_ZCARD;
624
703
  break;
@@ -629,6 +708,16 @@ redis_parse_req(struct msg *r)
629
708
  break;
630
709
  }
631
710
 
711
+ if (str5icmp(m, 'z', 's', 'c', 'a', 'n')) {
712
+ r->type = MSG_REQ_REDIS_ZSCAN;
713
+ break;
714
+ }
715
+
716
+ if (str5icmp(m, 'p', 'f', 'a', 'd', 'd')) {
717
+ r->type = MSG_REQ_REDIS_PFADD;
718
+ break;
719
+ }
720
+
632
721
  break;
633
722
 
634
723
  case 6:
@@ -780,6 +869,16 @@ redis_parse_req(struct msg *r)
780
869
  break;
781
870
  }
782
871
 
872
+ if (str7icmp(m, 'p', 'f', 'c', 'o', 'u', 'n', 't')) {
873
+ r->type = MSG_REQ_REDIS_PFCOUNT;
874
+ break;
875
+ }
876
+
877
+ if (str7icmp(m, 'p', 'f', 'm', 'e', 'r', 'g', 'e')) {
878
+ r->type = MSG_REQ_REDIS_PFMERGE;
879
+ break;
880
+ }
881
+
783
882
  break;
784
883
 
785
884
  case 8:
@@ -836,6 +935,11 @@ redis_parse_req(struct msg *r)
836
935
  break;
837
936
  }
838
937
 
938
+ if (str9icmp(m, 'z', 'l', 'e', 'x', 'c', 'o', 'u', 'n', 't')) {
939
+ r->type = MSG_REQ_REDIS_ZLEXCOUNT;
940
+ break;
941
+ }
942
+
839
943
  break;
840
944
 
841
945
  case 10:
@@ -875,6 +979,11 @@ redis_parse_req(struct msg *r)
875
979
  break;
876
980
  }
877
981
 
982
+ if (str11icmp(m, 'z', 'r', 'a', 'n', 'g', 'e', 'b', 'y', 'l', 'e', 'x')) {
983
+ r->type = MSG_REQ_REDIS_ZRANGEBYLEX;
984
+ break;
985
+ }
986
+
878
987
  break;
879
988
 
880
989
  case 12:
@@ -894,6 +1003,14 @@ redis_parse_req(struct msg *r)
894
1003
 
895
1004
  break;
896
1005
 
1006
+ case 14:
1007
+ if (str14icmp(m, 'z', 'r', 'e', 'm', 'r', 'a', 'n', 'g', 'e', 'b', 'y', 'l', 'e', 'x')) {
1008
+ r->type = MSG_REQ_REDIS_ZREMRANGEBYLEX;
1009
+ break;
1010
+ }
1011
+
1012
+ break;
1013
+
897
1014
  case 15:
898
1015
  if (str15icmp(m, 'z', 'r', 'e', 'm', 'r', 'a', 'n', 'g', 'e', 'b', 'y', 'r', 'a', 'n', 'k')) {
899
1016
  r->type = MSG_REQ_REDIS_ZREMRANGEBYRANK;
@@ -932,7 +1049,9 @@ redis_parse_req(struct msg *r)
932
1049
  case SW_REQ_TYPE_LF:
933
1050
  switch (ch) {
934
1051
  case LF:
935
- if (redis_argeval(r)) {
1052
+ if (redis_argz(r)) {
1053
+ goto done;
1054
+ } else if (redis_argeval(r)) {
936
1055
  state = SW_ARG1_LEN;
937
1056
  } else {
938
1057
  state = SW_KEY_LEN;
@@ -1005,17 +1124,23 @@ redis_parse_req(struct msg *r)
1005
1124
 
1006
1125
  if (*m != CR) {
1007
1126
  goto error;
1008
- }
1127
+ } else { /* got a key */
1128
+ struct keypos *kpos;
1009
1129
 
1010
- p = m; /* move forward by rlen bytes */
1011
- r->rlen = 0;
1012
- m = r->token;
1013
- r->token = NULL;
1130
+ p = m; /* move forward by rlen bytes */
1131
+ r->rlen = 0;
1132
+ m = r->token;
1133
+ r->token = NULL;
1014
1134
 
1015
- r->key_start = m;
1016
- r->key_end = p;
1135
+ kpos = array_push(r->keys);
1136
+ if (kpos == NULL) {
1137
+ goto enomem;
1138
+ }
1139
+ kpos->start = m;
1140
+ kpos->end = p;
1017
1141
 
1018
- state = SW_KEY_LF;
1142
+ state = SW_KEY_LF;
1143
+ }
1019
1144
 
1020
1145
  break;
1021
1146
 
@@ -1051,7 +1176,12 @@ redis_parse_req(struct msg *r)
1051
1176
  if (r->rnarg == 0) {
1052
1177
  goto done;
1053
1178
  }
1054
- state = SW_FRAGMENT;
1179
+ state = SW_KEY_LEN;
1180
+ } else if (redis_argkvx(r)) {
1181
+ if (r->rnarg == 0) {
1182
+ goto done;
1183
+ }
1184
+ state = SW_ARG1_LEN;
1055
1185
  } else if (redis_argeval(r)) {
1056
1186
  if (r->rnarg == 0) {
1057
1187
  goto done;
@@ -1069,10 +1199,6 @@ redis_parse_req(struct msg *r)
1069
1199
 
1070
1200
  break;
1071
1201
 
1072
- case SW_FRAGMENT:
1073
- r->token = p;
1074
- goto fragment;
1075
-
1076
1202
  case SW_ARG1_LEN:
1077
1203
  if (r->token == NULL) {
1078
1204
  if (ch != '$') {
@@ -1155,6 +1281,11 @@ redis_parse_req(struct msg *r)
1155
1281
  goto error;
1156
1282
  }
1157
1283
  state = SW_ARG2_LEN;
1284
+ } else if (redis_argkvx(r)) {
1285
+ if (r->rnarg == 0) {
1286
+ goto done;
1287
+ }
1288
+ state = SW_KEY_LEN;
1158
1289
  } else {
1159
1290
  goto error;
1160
1291
  }
@@ -1387,7 +1518,7 @@ redis_parse_req(struct msg *r)
1387
1518
  r->rnarg--;
1388
1519
  r->token = NULL;
1389
1520
  state = SW_ARGN_LEN_LF;
1390
- } else {
1521
+ } else {
1391
1522
  goto error;
1392
1523
  }
1393
1524
 
@@ -1468,19 +1599,6 @@ redis_parse_req(struct msg *r)
1468
1599
  r->state, r->pos - b->pos, b->last - b->pos);
1469
1600
  return;
1470
1601
 
1471
- fragment:
1472
- ASSERT(p != b->last);
1473
- ASSERT(r->token != NULL);
1474
- r->pos = r->token;
1475
- r->token = NULL;
1476
- r->state = state;
1477
- r->result = MSG_PARSE_FRAGMENT;
1478
-
1479
- log_hexdump(LOG_VERB, b->pos, mbuf_length(b), "parsed req %"PRIu64" res %d "
1480
- "type %d state %d rpos %d of %d", r->id, r->result, r->type,
1481
- r->state, r->pos - b->pos, b->last - b->pos);
1482
- return;
1483
-
1484
1602
  done:
1485
1603
  ASSERT(r->type > MSG_UNKNOWN && r->type < MSG_SENTINEL);
1486
1604
  r->pos = p + 1;
@@ -1494,6 +1612,15 @@ done:
1494
1612
  r->state, r->pos - b->pos, b->last - b->pos);
1495
1613
  return;
1496
1614
 
1615
+ enomem:
1616
+ r->result = MSG_PARSE_ERROR;
1617
+ r->state = state;
1618
+
1619
+ log_hexdump(LOG_INFO, b->pos, mbuf_length(b), "out of memory on parse req %"PRIu64" "
1620
+ "res %d type %d state %d", r->id, r->result, r->type, r->state);
1621
+
1622
+ return;
1623
+
1497
1624
  error:
1498
1625
  r->result = MSG_PARSE_ERROR;
1499
1626
  r->state = state;
@@ -1789,7 +1916,28 @@ redis_parse_rsp(struct msg *r)
1789
1916
  *
1790
1917
  * Here, we only handle a multi bulk reply element that
1791
1918
  * are either integer reply or bulk reply.
1919
+ *
1920
+ * there is a special case for sscan/hscan/zscan, these command
1921
+ * replay a nested multi-bulk with a number and a multi bulk like this:
1922
+ *
1923
+ * - mulit-bulk
1924
+ * - cursor
1925
+ * - mulit-bulk
1926
+ * - val1
1927
+ * - val2
1928
+ * - val3
1929
+ *
1930
+ * in this case, there is only one sub-multi-bulk,
1931
+ * and it's the last element of parent,
1932
+ * we can handle it like tail-recursive.
1933
+ *
1792
1934
  */
1935
+ if (ch == '*') { /* for sscan/hscan/zscan only */
1936
+ p = p - 1; /* go back by 1 byte */
1937
+ state = SW_MULTIBULK;
1938
+ break;
1939
+ }
1940
+
1793
1941
  if (ch != '$' && ch != ':') {
1794
1942
  goto error;
1795
1943
  }
@@ -1813,7 +1961,6 @@ redis_parse_rsp(struct msg *r)
1813
1961
  }
1814
1962
  r->rnarg--;
1815
1963
  r->token = NULL;
1816
-
1817
1964
  } else {
1818
1965
  goto error;
1819
1966
  }
@@ -1916,77 +2063,77 @@ error:
1916
2063
  }
1917
2064
 
1918
2065
  /*
1919
- * Pre-split copy handler invoked when the request is a multi vector -
1920
- * 'mget' or 'del' request and is about to be split into two requests
1921
- */
1922
- void
1923
- redis_pre_splitcopy(struct mbuf *mbuf, void *arg)
2066
+ * copy one bulk from src to dst
2067
+ *
2068
+ * if dst == NULL, we just eat the bulk
2069
+ *
2070
+ * */
2071
+ static rstatus_t
2072
+ redis_copy_bulk(struct msg *dst, struct msg *src)
1924
2073
  {
1925
- struct msg *r = arg;
1926
- int n;
1927
-
1928
- ASSERT(r->request);
1929
- ASSERT(r->narg > 1);
1930
- ASSERT(mbuf_empty(mbuf));
1931
-
1932
- switch (r->type) {
1933
- case MSG_REQ_REDIS_MGET:
1934
- n = nc_snprintf(mbuf->last, mbuf_size(mbuf), "*%d\r\n$4\r\nmget\r\n",
1935
- r->narg - 1);
1936
- break;
1937
-
1938
- case MSG_REQ_REDIS_DEL:
1939
- n = nc_snprintf(mbuf->last, mbuf_size(mbuf), "*%d\r\n$3\r\ndel\r\n",
1940
- r->narg - 1);
1941
- break;
1942
-
1943
- default:
1944
- n = 0;
1945
- NOT_REACHED();
2074
+ struct mbuf *mbuf, *nbuf;
2075
+ uint8_t *p;
2076
+ uint32_t len = 0;
2077
+ uint32_t bytes = 0;
2078
+ rstatus_t status;
2079
+
2080
+ for (mbuf = STAILQ_FIRST(&src->mhdr);
2081
+ mbuf && mbuf_empty(mbuf);
2082
+ mbuf = STAILQ_FIRST(&src->mhdr)) {
2083
+
2084
+ mbuf_remove(&src->mhdr, mbuf);
2085
+ mbuf_put(mbuf);
1946
2086
  }
1947
2087
 
1948
- mbuf->last += n;
1949
- }
1950
-
1951
- /*
1952
- * Post-split copy handler invoked when the request is a multi vector -
1953
- * 'mget' or 'del' request and has already been split into two requests
1954
- */
1955
- rstatus_t
1956
- redis_post_splitcopy(struct msg *r)
1957
- {
1958
- struct mbuf *hbuf, *nhbuf; /* head mbuf and new head mbuf */
1959
- struct string hstr = string("*2"); /* header string */
1960
-
1961
- ASSERT(r->request);
1962
- ASSERT(r->type == MSG_REQ_REDIS_MGET || r->type == MSG_REQ_REDIS_DEL);
1963
- ASSERT(!STAILQ_EMPTY(&r->mhdr));
1964
-
1965
- nhbuf = mbuf_get();
1966
- if (nhbuf == NULL) {
1967
- return NC_ENOMEM;
2088
+ mbuf = STAILQ_FIRST(&src->mhdr);
2089
+ if (mbuf == NULL) {
2090
+ return NC_ERROR;
1968
2091
  }
1969
2092
 
1970
- /*
1971
- * Fix the head mbuf in the head (A) msg. The fix is straightforward
1972
- * as we just need to skip over the narg token
1973
- */
1974
- hbuf = STAILQ_FIRST(&r->mhdr);
1975
- ASSERT(hbuf->pos == r->narg_start);
1976
- ASSERT(hbuf->pos < r->narg_end && r->narg_end <= hbuf->last);
1977
- hbuf->pos = r->narg_end;
1978
-
1979
- /*
1980
- * Add a new head mbuf in the head (A) msg that just contains '*2'
1981
- * token
1982
- */
1983
- STAILQ_INSERT_HEAD(&r->mhdr, nhbuf, next);
1984
- mbuf_copy(nhbuf, hstr.data, hstr.len);
2093
+ p = mbuf->pos;
2094
+ ASSERT(*p == '$');
2095
+ p++;
1985
2096
 
1986
- /* fix up the narg_start and narg_end */
1987
- r->narg_start = nhbuf->pos;
1988
- r->narg_end = nhbuf->last;
2097
+ if (p[0] == '-' && p[1] == '1') {
2098
+ len = 1 + 2 + CRLF_LEN; /* $-1\r\n */
2099
+ p = mbuf->pos + len;
2100
+ } else {
2101
+ len = 0;
2102
+ for (; p < mbuf->last && isdigit(*p); p++) {
2103
+ len = len * 10 + (uint32_t)(*p - '0');
2104
+ }
2105
+ len += CRLF_LEN * 2;
2106
+ len += (p - mbuf->pos);
2107
+ }
2108
+ bytes = len;
2109
+
2110
+ /* copy len bytes to dst */
2111
+ for (; mbuf;) {
2112
+ if (mbuf_length(mbuf) <= len) { /* steal this buf from src to dst */
2113
+ nbuf = STAILQ_NEXT(mbuf, next);
2114
+ mbuf_remove(&src->mhdr, mbuf);
2115
+ if (dst != NULL) {
2116
+ mbuf_insert(&dst->mhdr, mbuf);
2117
+ }
2118
+ len -= mbuf_length(mbuf);
2119
+ mbuf = nbuf;
2120
+ } else { /* split it */
2121
+ if (dst != NULL) {
2122
+ status = msg_append(dst, mbuf->pos, len);
2123
+ if (status != NC_OK) {
2124
+ return status;
2125
+ }
2126
+ }
2127
+ mbuf->pos += len;
2128
+ break;
2129
+ }
2130
+ }
1989
2131
 
2132
+ if (dst != NULL) {
2133
+ dst->mlen += bytes;
2134
+ }
2135
+ src->mlen -= bytes;
2136
+ log_debug(LOG_VVERB, "redis_copy_bulk copy bytes: %d", bytes);
1990
2137
  return NC_OK;
1991
2138
  }
1992
2139
 
@@ -2008,6 +2155,7 @@ redis_pre_coalesce(struct msg *r)
2008
2155
  /* do nothing, if not a response to a fragmented request */
2009
2156
  return;
2010
2157
  }
2158
+ pr->frag_owner->nfrag_done++;
2011
2159
 
2012
2160
  switch (r->type) {
2013
2161
  case MSG_RSP_REDIS_INTEGER:
@@ -2049,14 +2197,13 @@ redis_pre_coalesce(struct msg *r)
2049
2197
  r->mlen -= (uint32_t)(r->narg_end - r->narg_start);
2050
2198
  mbuf->pos = r->narg_end;
2051
2199
 
2052
- if (pr->first_fragment) {
2053
- mbuf = mbuf_get();
2054
- if (mbuf == NULL) {
2055
- pr->error = 1;
2056
- pr->err = EINVAL;
2057
- return;
2058
- }
2059
- STAILQ_INSERT_HEAD(&r->mhdr, mbuf, next);
2200
+ break;
2201
+
2202
+ case MSG_RSP_REDIS_STATUS:
2203
+ if (pr->type == MSG_REQ_REDIS_MSET) { /* MSET segments */
2204
+ mbuf = STAILQ_FIRST(&r->mhdr);
2205
+ r->mlen -= mbuf_length(mbuf);
2206
+ mbuf_rewind(mbuf);
2060
2207
  }
2061
2208
  break;
2062
2209
 
@@ -2075,6 +2222,309 @@ redis_pre_coalesce(struct msg *r)
2075
2222
  }
2076
2223
  }
2077
2224
 
2225
+ static rstatus_t
2226
+ redis_append_key(struct msg *r, uint8_t *key, uint32_t keylen)
2227
+ {
2228
+ uint32_t len;
2229
+ struct mbuf *mbuf;
2230
+ uint8_t printbuf[32];
2231
+ struct keypos *kpos;
2232
+
2233
+ /* 1. keylen */
2234
+ len = (uint32_t)nc_snprintf(printbuf, sizeof(printbuf), "$%d\r\n", keylen);
2235
+ mbuf = msg_ensure_mbuf(r, len);
2236
+ if (mbuf == NULL) {
2237
+ return NC_ENOMEM;
2238
+ }
2239
+ mbuf_copy(mbuf, printbuf, len);
2240
+ r->mlen += len;
2241
+
2242
+ /* 2. key */
2243
+ mbuf = msg_ensure_mbuf(r, keylen);
2244
+ if (mbuf == NULL) {
2245
+ return NC_ENOMEM;
2246
+ }
2247
+
2248
+ kpos = array_push(r->keys);
2249
+ if (kpos == NULL) {
2250
+ return NC_ENOMEM;
2251
+ }
2252
+
2253
+ kpos->start = mbuf->last;
2254
+ kpos->end = mbuf->last + keylen;
2255
+ mbuf_copy(mbuf, key, keylen);
2256
+ r->mlen += keylen;
2257
+
2258
+ /* 3. CRLF */
2259
+ mbuf = msg_ensure_mbuf(r, CRLF_LEN);
2260
+ if (mbuf == NULL) {
2261
+ return NC_ENOMEM;
2262
+ }
2263
+ mbuf_copy(mbuf, (uint8_t *)CRLF, CRLF_LEN);
2264
+ r->mlen += (uint32_t)CRLF_LEN;
2265
+
2266
+ return NC_OK;
2267
+ }
2268
+
2269
+ /*
2270
+ * input a msg, return a msg chain.
2271
+ * ncontinuum is the number of backend redis/memcache server
2272
+ *
2273
+ * the original msg will be fragment into at most ncontinuum fragments.
2274
+ * all the keys map to the same backend will group into one fragment.
2275
+ *
2276
+ * frag_id:
2277
+ * a unique fragment id for all fragments of the message vector. including the orig msg.
2278
+ *
2279
+ * frag_owner:
2280
+ * All fragments of the message use frag_owner point to the orig msg
2281
+ *
2282
+ * frag_seq:
2283
+ * the map from each key to it's fragment, (only in the orig msg)
2284
+ *
2285
+ * For example, a message vector with 3 keys:
2286
+ *
2287
+ * get key1 key2 key3
2288
+ *
2289
+ * suppose we have 2 backend server, and the map is:
2290
+ *
2291
+ * key1 => backend 0
2292
+ * key2 => backend 1
2293
+ * key3 => backend 0
2294
+ *
2295
+ * it will fragment like this:
2296
+ *
2297
+ * +-----------------+
2298
+ * | msg vector |
2299
+ * |(original msg) |
2300
+ * |key1, key2, key3 |
2301
+ * +-----------------+
2302
+ *
2303
+ * frag_owner
2304
+ * /--------------------------------------+
2305
+ * frag_owner / |
2306
+ * /-----------+ | /------------+ frag_owner |
2307
+ * | | | | | |
2308
+ * | v v v | |
2309
+ * +--------------------+ +---------------------+ +----+----------------+
2310
+ * | frag_id = 10 | | frag_id = 10 | | frag_id = 10 |
2311
+ * | nfrag = 3 | | nfrag = 0 | | nfrag = 0 |
2312
+ * | frag_seq = x x x | | key1, key3 | | key2 |
2313
+ * +------------|-|-|---+ +---------------------+ +---------------------+
2314
+ * | | | ^ ^ ^
2315
+ * | \ \ | | |
2316
+ * | \ ----------+ | |
2317
+ * +---\---------------+ |
2318
+ * ------------------------------------------+
2319
+ *
2320
+ */
2321
+ static rstatus_t
2322
+ redis_fragment_argx(struct msg *r, uint32_t ncontinuum, struct msg_tqh *frag_msgq,
2323
+ uint32_t key_step)
2324
+ {
2325
+ struct mbuf *mbuf;
2326
+ struct msg **sub_msgs;
2327
+ uint32_t i;
2328
+ rstatus_t status;
2329
+
2330
+ ASSERT(array_n(r->keys) == (r->narg - 1) / key_step);
2331
+
2332
+ sub_msgs = nc_zalloc(ncontinuum * sizeof(*sub_msgs));
2333
+ if (sub_msgs == NULL) {
2334
+ return NC_ENOMEM;
2335
+ }
2336
+
2337
+ ASSERT(r->frag_seq == NULL);
2338
+ r->frag_seq = nc_alloc(array_n(r->keys) * sizeof(*r->frag_seq));
2339
+ if (r->frag_seq == NULL) {
2340
+ nc_free(sub_msgs);
2341
+ return NC_ENOMEM;
2342
+ }
2343
+
2344
+ mbuf = STAILQ_FIRST(&r->mhdr);
2345
+ mbuf->pos = mbuf->start;
2346
+
2347
+ /*
2348
+ * This code is based on the assumption that '*narg\r\n$4\r\nMGET\r\n' is located
2349
+ * in a contiguous location.
2350
+ * This is always true because we have capped our MBUF_MIN_SIZE at 512 and
2351
+ * whenever we have multiple messages, we copy the tail message into a new mbuf
2352
+ */
2353
+ for (i = 0; i < 3; i++) { /* eat *narg\r\n$4\r\nMGET\r\n */
2354
+ for (; *(mbuf->pos) != '\n';) {
2355
+ mbuf->pos++;
2356
+ }
2357
+ mbuf->pos++;
2358
+ }
2359
+
2360
+ r->frag_id = msg_gen_frag_id();
2361
+ r->nfrag = 0;
2362
+ r->frag_owner = r;
2363
+
2364
+ for (i = 0; i < array_n(r->keys); i++) { /* for each key */
2365
+ struct msg *sub_msg;
2366
+ struct keypos *kpos = array_get(r->keys, i);
2367
+ uint32_t idx = msg_backend_idx(r, kpos->start, kpos->end - kpos->start);
2368
+
2369
+ if (sub_msgs[idx] == NULL) {
2370
+ sub_msgs[idx] = msg_get(r->owner, r->request, r->redis);
2371
+ if (sub_msgs[idx] == NULL) {
2372
+ nc_free(sub_msgs);
2373
+ return NC_ENOMEM;
2374
+ }
2375
+ }
2376
+ r->frag_seq[i] = sub_msg = sub_msgs[idx];
2377
+
2378
+ sub_msg->narg++;
2379
+ status = redis_append_key(sub_msg, kpos->start, kpos->end - kpos->start);
2380
+ if (status != NC_OK) {
2381
+ nc_free(sub_msgs);
2382
+ return status;
2383
+ }
2384
+
2385
+ if (key_step == 1) { /* mget,del */
2386
+ continue;
2387
+ } else { /* mset */
2388
+ status = redis_copy_bulk(NULL, r); /* eat key */
2389
+ if (status != NC_OK) {
2390
+ nc_free(sub_msgs);
2391
+ return status;
2392
+ }
2393
+
2394
+ status = redis_copy_bulk(sub_msg, r);
2395
+ if (status != NC_OK) {
2396
+ nc_free(sub_msgs);
2397
+ return status;
2398
+ }
2399
+
2400
+ sub_msg->narg++;
2401
+ }
2402
+ }
2403
+
2404
+ for (i = 0; i < ncontinuum; i++) { /* prepend mget header, and forward it */
2405
+ struct msg *sub_msg = sub_msgs[i];
2406
+ if (sub_msg == NULL) {
2407
+ continue;
2408
+ }
2409
+
2410
+ if (r->type == MSG_REQ_REDIS_MGET) {
2411
+ status = msg_prepend_format(sub_msg, "*%d\r\n$4\r\nmget\r\n",
2412
+ sub_msg->narg + 1);
2413
+ } else if (r->type == MSG_REQ_REDIS_DEL) {
2414
+ status = msg_prepend_format(sub_msg, "*%d\r\n$3\r\ndel\r\n",
2415
+ sub_msg->narg + 1);
2416
+ } else if (r->type == MSG_REQ_REDIS_MSET) {
2417
+ status = msg_prepend_format(sub_msg, "*%d\r\n$4\r\nmset\r\n",
2418
+ sub_msg->narg + 1);
2419
+ } else {
2420
+ NOT_REACHED();
2421
+ }
2422
+ if (status != NC_OK) {
2423
+ nc_free(sub_msgs);
2424
+ return status;
2425
+ }
2426
+
2427
+ sub_msg->type = r->type;
2428
+ sub_msg->frag_id = r->frag_id;
2429
+ sub_msg->frag_owner = r->frag_owner;
2430
+
2431
+ TAILQ_INSERT_TAIL(frag_msgq, sub_msg, m_tqe);
2432
+ r->nfrag++;
2433
+ }
2434
+
2435
+ nc_free(sub_msgs);
2436
+ return NC_OK;
2437
+ }
2438
+
2439
+ rstatus_t
2440
+ redis_fragment(struct msg *r, uint32_t ncontinuum, struct msg_tqh *frag_msgq)
2441
+ {
2442
+ switch (r->type) {
2443
+ case MSG_REQ_REDIS_MGET:
2444
+ case MSG_REQ_REDIS_DEL:
2445
+ return redis_fragment_argx(r, ncontinuum, frag_msgq, 1);
2446
+ case MSG_REQ_REDIS_MSET:
2447
+ return redis_fragment_argx(r, ncontinuum, frag_msgq, 2);
2448
+ default:
2449
+ return NC_OK;
2450
+ }
2451
+ }
2452
+
2453
+ rstatus_t
2454
+ redis_reply(struct msg *r)
2455
+ {
2456
+ struct msg *response = r->peer;
2457
+
2458
+ ASSERT(response != NULL);
2459
+
2460
+ switch (r->type) {
2461
+ case MSG_REQ_REDIS_PING:
2462
+ return msg_append(response, (uint8_t *)"+PONG\r\n", 7);
2463
+
2464
+ default:
2465
+ NOT_REACHED();
2466
+ return NC_ERROR;
2467
+ }
2468
+ }
2469
+
2470
+ void
2471
+ redis_post_coalesce_mset(struct msg *request)
2472
+ {
2473
+ struct msg *response = request->peer;
2474
+ rstatus_t status;
2475
+
2476
+ status = msg_append(response, (uint8_t *)"+OK\r\n", 5);
2477
+ if (status != NC_OK) {
2478
+ response->error = 1; /* mark this msg as err */
2479
+ response->err = errno;
2480
+ }
2481
+ }
2482
+
2483
+ void
2484
+ redis_post_coalesce_del(struct msg *request)
2485
+ {
2486
+ struct msg *response = request->peer;
2487
+ rstatus_t status;
2488
+
2489
+ status = msg_prepend_format(response, ":%d\r\n", request->integer);
2490
+ if (status != NC_OK) {
2491
+ response->error = 1;
2492
+ response->err = errno;
2493
+ }
2494
+ }
2495
+
2496
+ static void
2497
+ redis_post_coalesce_mget(struct msg *request)
2498
+ {
2499
+ struct msg *response = request->peer;
2500
+ struct msg *sub_msg;
2501
+ rstatus_t status;
2502
+ uint32_t i;
2503
+
2504
+ status = msg_prepend_format(response, "*%d\r\n", request->narg - 1);
2505
+ if (status != NC_OK) {
2506
+ /*
2507
+ * the fragments is still in c_conn->omsg_q, we have to discard all of them,
2508
+ * we just close the conn here
2509
+ */
2510
+ response->owner->err = 1;
2511
+ return;
2512
+ }
2513
+
2514
+ for (i = 0; i < array_n(request->keys); i++) { /* for each key */
2515
+ sub_msg = request->frag_seq[i]->peer; /* get it's peer response */
2516
+ if (sub_msg == NULL) {
2517
+ response->owner->err = 1;
2518
+ return;
2519
+ }
2520
+ status = redis_copy_bulk(response, sub_msg);
2521
+ if (status != NC_OK) {
2522
+ response->owner->err = 1;
2523
+ return;
2524
+ }
2525
+ }
2526
+ }
2527
+
2078
2528
  /*
2079
2529
  * Post-coalesce handler is invoked when the message is a response to
2080
2530
  * the fragmented multi vector request - 'mget' or 'del' and all the
@@ -2085,44 +2535,21 @@ void
2085
2535
  redis_post_coalesce(struct msg *r)
2086
2536
  {
2087
2537
  struct msg *pr = r->peer; /* peer response */
2088
- struct mbuf *mbuf;
2089
- int n;
2090
2538
 
2091
- ASSERT(r->request && r->first_fragment);
2539
+ ASSERT(!pr->request);
2540
+ ASSERT(r->request && (r->frag_owner == r));
2092
2541
  if (r->error || r->ferror) {
2093
2542
  /* do nothing, if msg is in error */
2094
2543
  return;
2095
2544
  }
2096
2545
 
2097
- ASSERT(!pr->request);
2098
-
2099
- switch (pr->type) {
2100
- case MSG_RSP_REDIS_INTEGER:
2101
- /* only redis 'del' fragmented request sends back integer reply */
2102
- ASSERT(r->type == MSG_REQ_REDIS_DEL);
2103
-
2104
- mbuf = STAILQ_FIRST(&pr->mhdr);
2105
-
2106
- ASSERT(pr->mlen == 0);
2107
- ASSERT(mbuf_empty(mbuf));
2108
-
2109
- n = nc_scnprintf(mbuf->last, mbuf_size(mbuf), ":%d\r\n", r->integer);
2110
- mbuf->last += n;
2111
- pr->mlen += (uint32_t)n;
2112
- break;
2113
-
2114
- case MSG_RSP_REDIS_MULTIBULK:
2115
- /* only redis 'mget' fragmented request sends back multi-bulk reply */
2116
- ASSERT(r->type == MSG_REQ_REDIS_MGET);
2117
-
2118
- mbuf = STAILQ_FIRST(&pr->mhdr);
2119
- ASSERT(mbuf_empty(mbuf));
2120
-
2121
- n = nc_scnprintf(mbuf->last, mbuf_size(mbuf), "*%d\r\n", r->nfrag);
2122
- mbuf->last += n;
2123
- pr->mlen += (uint32_t)n;
2124
- break;
2125
-
2546
+ switch (r->type) {
2547
+ case MSG_REQ_REDIS_MGET:
2548
+ return redis_post_coalesce_mget(r);
2549
+ case MSG_REQ_REDIS_DEL:
2550
+ return redis_post_coalesce_del(r);
2551
+ case MSG_REQ_REDIS_MSET:
2552
+ return redis_post_coalesce_mset(r);
2126
2553
  default:
2127
2554
  NOT_REACHED();
2128
2555
  }