nutcracker 0.2.4.12 → 0.3.0.12

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 (47) hide show
  1. checksums.yaml +8 -8
  2. data/Rakefile +1 -1
  3. data/ext/nutcracker/ChangeLog +10 -0
  4. data/ext/nutcracker/Makefile.am +2 -0
  5. data/ext/nutcracker/Makefile.in +101 -14
  6. data/ext/nutcracker/README.md +18 -1
  7. data/ext/nutcracker/config.h.in +18 -0
  8. data/ext/nutcracker/configure +196 -25
  9. data/ext/nutcracker/configure.ac +64 -6
  10. data/ext/nutcracker/extconf.rb +1 -1
  11. data/ext/nutcracker/man/nutcracker.8 +76 -0
  12. data/ext/nutcracker/notes/debug.txt +116 -16
  13. data/ext/nutcracker/notes/kqueue.pdf +0 -0
  14. data/ext/nutcracker/notes/recommendation.md +20 -0
  15. data/ext/nutcracker/notes/redis.md +2 -2
  16. data/ext/nutcracker/scripts/nutcracker.spec +1 -1
  17. data/ext/nutcracker/scripts/redis-check.sh +3 -1
  18. data/ext/nutcracker/src/Makefile.am +15 -6
  19. data/ext/nutcracker/src/Makefile.in +39 -36
  20. data/ext/nutcracker/src/event/Makefile.am +16 -0
  21. data/ext/nutcracker/src/event/Makefile.in +492 -0
  22. data/ext/nutcracker/src/event/nc_epoll.c +344 -0
  23. data/ext/nutcracker/src/event/nc_event.h +88 -0
  24. data/ext/nutcracker/src/event/nc_evport.c +420 -0
  25. data/ext/nutcracker/src/event/nc_kqueue.c +412 -0
  26. data/ext/nutcracker/src/hashkit/nc_crc32.c +19 -1
  27. data/ext/nutcracker/src/hashkit/nc_hashkit.h +3 -1
  28. data/ext/nutcracker/src/hashkit/nc_md5.c +257 -315
  29. data/ext/nutcracker/src/nc.c +12 -1
  30. data/ext/nutcracker/src/nc_connection.c +18 -1
  31. data/ext/nutcracker/src/nc_connection.h +1 -0
  32. data/ext/nutcracker/src/nc_core.c +22 -30
  33. data/ext/nutcracker/src/nc_core.h +22 -7
  34. data/ext/nutcracker/src/nc_proxy.c +8 -9
  35. data/ext/nutcracker/src/nc_queue.h +2 -0
  36. data/ext/nutcracker/src/nc_request.c +3 -4
  37. data/ext/nutcracker/src/nc_response.c +25 -8
  38. data/ext/nutcracker/src/nc_server.c +8 -6
  39. data/ext/nutcracker/src/nc_stats.c +46 -43
  40. data/ext/nutcracker/src/nc_stats.h +37 -30
  41. data/ext/nutcracker/src/nc_util.c +6 -1
  42. data/ext/nutcracker/src/proto/nc_redis.c +19 -5
  43. data/lib/nutcracker/version.rb +1 -1
  44. data/lib/nutcracker.rb +1 -1
  45. metadata +10 -4
  46. data/ext/nutcracker/src/nc_event.c +0 -214
  47. data/ext/nutcracker/src/nc_event.h +0 -39
@@ -0,0 +1,412 @@
1
+ /*
2
+ * twemproxy - A fast and lightweight proxy for memcached protocol.
3
+ * Copyright (C) 2011 Twitter, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include <nc_core.h>
19
+
20
+ #ifdef NC_HAVE_KQUEUE
21
+
22
+ #include <sys/event.h>
23
+
24
+ struct event_base *
25
+ event_base_create(int nevent, event_cb_t cb)
26
+ {
27
+ struct event_base *evb;
28
+ int status, kq;
29
+ struct kevent *change, *event;
30
+
31
+ ASSERT(nevent > 0);
32
+
33
+ kq = kqueue();
34
+ if (kq < 0) {
35
+ log_error("kqueue failed: %s", strerror(errno));
36
+ return NULL;
37
+ }
38
+
39
+ change = nc_calloc(nevent, sizeof(*change));
40
+ if (change == NULL) {
41
+ status = close(kq);
42
+ if (status < 0) {
43
+ log_error("close kq %d failed, ignored: %s", kq, strerror(errno));
44
+ }
45
+ return NULL;
46
+ }
47
+
48
+ event = nc_calloc(nevent, sizeof(*event));
49
+ if (event == NULL) {
50
+ nc_free(change);
51
+ status = close(kq);
52
+ if (status < 0) {
53
+ log_error("close kq %d failed, ignored: %s", kq, strerror(errno));
54
+ }
55
+ return NULL;
56
+ }
57
+
58
+ evb = nc_alloc(sizeof(*evb));
59
+ if (evb == NULL) {
60
+ nc_free(change);
61
+ nc_free(event);
62
+ status = close(kq);
63
+ if (status < 0) {
64
+ log_error("close kq %d failed, ignored: %s", kq, strerror(errno));
65
+ }
66
+ return NULL;
67
+ }
68
+
69
+ evb->kq = kq;
70
+ evb->change = change;
71
+ evb->nchange = 0;
72
+ evb->event = event;
73
+ evb->nevent = nevent;
74
+ evb->nreturned = 0;
75
+ evb->nprocessed = 0;
76
+ evb->cb = cb;
77
+
78
+ log_debug(LOG_INFO, "kq %d with nevent %d", evb->kq, evb->nevent);
79
+
80
+ return evb;
81
+ }
82
+
83
+ void
84
+ event_base_destroy(struct event_base *evb)
85
+ {
86
+ int status;
87
+
88
+ if (evb == NULL) {
89
+ return;
90
+ }
91
+
92
+ ASSERT(evb->kq > 0);
93
+
94
+ nc_free(evb->change);
95
+ nc_free(evb->event);
96
+
97
+ status = close(evb->kq);
98
+ if (status < 0) {
99
+ log_error("close kq %d failed, ignored: %s", evb->kq, strerror(errno));
100
+ }
101
+ evb->kq = -1;
102
+
103
+ nc_free(evb);
104
+ }
105
+
106
+ int
107
+ event_add_in(struct event_base *evb, struct conn *c)
108
+ {
109
+ struct kevent *event;
110
+
111
+ ASSERT(evb->kq > 0);
112
+ ASSERT(c != NULL);
113
+ ASSERT(c->sd > 0);
114
+ ASSERT(evb->nchange < evb->nevent);
115
+
116
+ if (c->recv_active) {
117
+ return 0;
118
+ }
119
+
120
+ event = &evb->change[evb->nchange++];
121
+ EV_SET(event, c->sd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, c);
122
+
123
+ c->recv_active = 1;
124
+
125
+ return 0;
126
+ }
127
+
128
+ int
129
+ event_del_in(struct event_base *evb, struct conn *c)
130
+ {
131
+ struct kevent *event;
132
+
133
+ ASSERT(evb->kq > 0);
134
+ ASSERT(c != NULL);
135
+ ASSERT(c->sd > 0);
136
+ ASSERT(evb->nchange < evb->nevent);
137
+
138
+ if (!c->recv_active) {
139
+ return 0;
140
+ }
141
+
142
+ event = &evb->change[evb->nchange++];
143
+ EV_SET(event, c->sd, EVFILT_READ, EV_DELETE, 0, 0, c);
144
+
145
+ c->recv_active = 0;
146
+
147
+ return 0;
148
+ }
149
+
150
+ int
151
+ event_add_out(struct event_base *evb, struct conn *c)
152
+ {
153
+ struct kevent *event;
154
+
155
+ ASSERT(evb->kq > 0);
156
+ ASSERT(c != NULL);
157
+ ASSERT(c->sd > 0);
158
+ ASSERT(c->recv_active);
159
+ ASSERT(evb->nchange < evb->nevent);
160
+
161
+ if (c->send_active) {
162
+ return 0;
163
+ }
164
+
165
+ event = &evb->change[evb->nchange++];
166
+ EV_SET(event, c->sd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, c);
167
+
168
+ c->send_active = 1;
169
+
170
+ return 0;
171
+ }
172
+
173
+ int
174
+ event_del_out(struct event_base *evb, struct conn *c)
175
+ {
176
+ struct kevent *event;
177
+
178
+ ASSERT(evb->kq > 0);
179
+ ASSERT(c != NULL);
180
+ ASSERT(c->sd > 0);
181
+ ASSERT(c->recv_active);
182
+ ASSERT(evb->nchange < evb->nevent);
183
+
184
+ if (!c->send_active) {
185
+ return 0;
186
+ }
187
+
188
+ event = &evb->change[evb->nchange++];
189
+ EV_SET(event, c->sd, EVFILT_WRITE, EV_DELETE, 0, 0, c);
190
+
191
+ c->send_active = 0;
192
+
193
+ return 0;
194
+ }
195
+
196
+ int
197
+ event_add_conn(struct event_base *evb, struct conn *c)
198
+ {
199
+ ASSERT(evb->kq > 0);
200
+ ASSERT(c != NULL);
201
+ ASSERT(c->sd > 0);
202
+ ASSERT(!c->recv_active);
203
+ ASSERT(!c->send_active);
204
+ ASSERT(evb->nchange < evb->nevent);
205
+
206
+ event_add_in(evb, c);
207
+ event_add_out(evb, c);
208
+
209
+ return 0;
210
+ }
211
+
212
+ int
213
+ event_del_conn(struct event_base *evb, struct conn *c)
214
+ {
215
+ int i;
216
+
217
+ ASSERT(evb->kq > 0);
218
+ ASSERT(c != NULL);
219
+ ASSERT(c->sd > 0);
220
+ ASSERT(evb->nchange < evb->nevent);
221
+
222
+ event_del_out(evb, c);
223
+ event_del_in(evb, c);
224
+
225
+ /*
226
+ * Now, eliminate pending events for c->sd (there should be at most one
227
+ * other event). This is important because we will close c->sd and free
228
+ * c when we return.
229
+ */
230
+ for (i = evb->nprocessed + 1; i < evb->nreturned; i++) {
231
+ struct kevent *ev = &evb->event[i];
232
+ if (ev->ident == (uintptr_t)c->sd) {
233
+ ev->flags = 0;
234
+ ev->filter = 0;
235
+ break;
236
+ }
237
+ }
238
+
239
+ return 0;
240
+ }
241
+
242
+ int
243
+ event_wait(struct event_base *evb, int timeout)
244
+ {
245
+ int kq = evb->kq;
246
+ struct timespec ts, *tsp;
247
+
248
+ ASSERT(kq > 0);
249
+
250
+ /* kevent should block indefinitely if timeout < 0 */
251
+ if (timeout < 0) {
252
+ tsp = NULL;
253
+ } else {
254
+ tsp = &ts;
255
+ tsp->tv_sec = timeout / 1000LL;
256
+ tsp->tv_nsec = (timeout % 1000LL) * 1000000LL;
257
+ }
258
+
259
+ for (;;) {
260
+ /*
261
+ * kevent() is used both to register new events with kqueue, and to
262
+ * retrieve any pending events. Changes that should be applied to the
263
+ * kqueue are given in the change[] and any returned events are placed
264
+ * in event[], up to the maximum sized allowed by nevent. The number
265
+ * of entries actually placed in event[] is returned by the kevent()
266
+ * call and saved in nreturned.
267
+ *
268
+ * Events are registered with the system by the application via a
269
+ * struct kevent, and an event is uniquely identified with the system
270
+ * by a (kq, ident, filter) tuple. This means that there can be only
271
+ * one (ident, filter) pair for a given kqueue.
272
+ */
273
+ evb->nreturned = kevent(kq, evb->change, evb->nchange, evb->event,
274
+ evb->nevent, tsp);
275
+ evb->nchange = 0;
276
+ if (evb->nreturned > 0) {
277
+ for (evb->nprocessed = 0; evb->nprocessed < evb->nreturned;
278
+ evb->nprocessed++) {
279
+ struct kevent *ev = &evb->event[evb->nprocessed];
280
+ uint32_t events = 0;
281
+
282
+ log_debug(LOG_VVERB, "kevent %04"PRIX32" with filter %d "
283
+ "triggered on sd %d", ev->flags, ev->filter,
284
+ ev->ident);
285
+
286
+ /*
287
+ * If an error occurs while processing an element of the
288
+ * change[] and there is enough room in the event[], then the
289
+ * event event will be placed in the eventlist with EV_ERROR
290
+ * set in flags and the system error(errno) in data.
291
+ */
292
+ if (ev->flags & EV_ERROR) {
293
+ /*
294
+ * Error messages that can happen, when a delete fails.
295
+ * EBADF happens when the file descriptor has been closed
296
+ * ENOENT when the file descriptor was closed and then
297
+ * reopened.
298
+ * EINVAL for some reasons not understood; EINVAL
299
+ * should not be returned ever; but FreeBSD does :-\
300
+ * An error is also indicated when a callback deletes an
301
+ * event we are still processing. In that case the data
302
+ * field is set to ENOENT.
303
+ */
304
+ if (ev->data == EBADF || ev->data == EINVAL ||
305
+ ev->data == ENOENT || ev->data == EINTR) {
306
+ continue;
307
+ }
308
+ events |= EVENT_ERR;
309
+ }
310
+
311
+ if (ev->filter == EVFILT_READ) {
312
+ events |= EVENT_READ;
313
+ }
314
+
315
+ if (ev->filter == EVFILT_WRITE) {
316
+ events |= EVENT_WRITE;
317
+ }
318
+
319
+ if (evb->cb != NULL && events != 0) {
320
+ evb->cb(ev->udata, events);
321
+ }
322
+ }
323
+ return evb->nreturned;
324
+ }
325
+
326
+ if (evb->nreturned == 0) {
327
+ if (timeout == -1) {
328
+ log_error("kevent on kq %d with %d events and %d timeout "
329
+ "returned no events", kq, evb->nevent, timeout);
330
+ return -1;
331
+ }
332
+
333
+ return 0;
334
+ }
335
+
336
+ if (errno == EINTR) {
337
+ continue;
338
+ }
339
+
340
+ log_error("kevent on kq %d with %d events failed: %s", kq, evb->nevent,
341
+ strerror(errno));
342
+ return -1;
343
+ }
344
+
345
+ NOT_REACHED();
346
+ }
347
+
348
+ void
349
+ event_loop_stats(event_stats_cb_t cb, void *arg)
350
+ {
351
+ struct stats *st = arg;
352
+ int status, kq;
353
+ struct kevent change, event;
354
+ struct timespec ts, *tsp;
355
+
356
+ kq = kqueue();
357
+ if (kq < 0) {
358
+ log_error("kqueue failed: %s", strerror(errno));
359
+ return;
360
+ }
361
+
362
+ EV_SET(&change, st->sd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
363
+
364
+ /* kevent should block indefinitely if st->interval < 0 */
365
+ if (st->interval < 0) {
366
+ tsp = NULL;
367
+ } else {
368
+ tsp = &ts;
369
+ tsp->tv_sec = st->interval / 1000LL;
370
+ tsp->tv_nsec = (st->interval % 1000LL) * 1000000LL;
371
+ }
372
+
373
+ for (;;) {
374
+ int nreturned;
375
+
376
+ nreturned = kevent(kq, &change, 1, &event, 1, tsp);
377
+ if (nreturned < 0) {
378
+ if (errno == EINTR) {
379
+ continue;
380
+ }
381
+ log_error("kevent on kq %d with m %d failed: %s", kq, st->sd,
382
+ strerror(errno));
383
+ goto error;
384
+ }
385
+
386
+ ASSERT(nreturned <= 1);
387
+
388
+ if (nreturned == 1) {
389
+ struct kevent *ev = &event;
390
+
391
+ if (ev->flags & EV_ERROR) {
392
+ if (ev->data == EINTR) {
393
+ continue;
394
+ }
395
+ log_error("kevent on kq %d with m %d failed: %s", kq, st->sd,
396
+ strerror(ev->data));
397
+ goto error;
398
+ }
399
+ }
400
+
401
+ cb(st, &nreturned);
402
+ }
403
+
404
+ error:
405
+ status = close(kq);
406
+ if (status < 0) {
407
+ log_error("close kq %d failed, ignored: %s", kq, strerror(errno));
408
+ }
409
+ kq = -1;
410
+ }
411
+
412
+ #endif /* NC_HAVE_KQUEUE */
@@ -91,15 +91,33 @@ static const uint32_t crc32tab[256] = {
91
91
  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
92
92
  };
93
93
 
94
+ /*
95
+ * CRC-32 implementation compatible with libmemcached library. Unfortunately
96
+ * this implementation does not return CRC-32 as per spec.
97
+ */
94
98
  uint32_t
95
99
  hash_crc32(const char *key, size_t key_length)
96
100
  {
97
101
  uint64_t x;
98
102
  uint32_t crc = UINT32_MAX;
99
103
 
100
- for (x= 0; x < key_length; x++) {
104
+ for (x = 0; x < key_length; x++) {
101
105
  crc = (crc >> 8) ^ crc32tab[(crc ^ (uint64_t)key[x]) & 0xff];
102
106
  }
103
107
 
104
108
  return ((~crc) >> 16) & 0x7fff;
105
109
  }
110
+
111
+ uint32_t
112
+ hash_crc32a(const char *key, size_t key_length)
113
+ {
114
+ const uint8_t *p = key;
115
+ uint32_t crc;
116
+
117
+ crc = ~0U;
118
+ while (key_length--) {
119
+ crc = crc32tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
120
+ }
121
+
122
+ return crc ^ ~0U;
123
+ }
@@ -26,6 +26,7 @@
26
26
  ACTION( HASH_MD5, md5 ) \
27
27
  ACTION( HASH_CRC16, crc16 ) \
28
28
  ACTION( HASH_CRC32, crc32 ) \
29
+ ACTION( HASH_CRC32A, crc32a ) \
29
30
  ACTION( HASH_FNV1_64, fnv1_64 ) \
30
31
  ACTION( HASH_FNV1A_64, fnv1a_64 ) \
31
32
  ACTION( HASH_FNV1_32, fnv1_32 ) \
@@ -56,8 +57,9 @@ typedef enum dist_type {
56
57
  uint32_t hash_one_at_a_time(const char *key, size_t key_length);
57
58
  void md5_signature(const unsigned char *key, unsigned int length, unsigned char *result);
58
59
  uint32_t hash_md5(const char *key, size_t key_length);
59
- uint32_t hash_crc32(const char *key, size_t key_length);
60
60
  uint32_t hash_crc16(const char *key, size_t key_length);
61
+ uint32_t hash_crc32(const char *key, size_t key_length);
62
+ uint32_t hash_crc32a(const char *key, size_t key_length);
61
63
  uint32_t hash_fnv1_64(const char *key, size_t key_length);
62
64
  uint32_t hash_fnv1a_64(const char *key, size_t key_length);
63
65
  uint32_t hash_fnv1_32(const char *key, size_t key_length);