nutcracker 0.2.4.12 → 0.3.0.12

Sign up to get free protection for your applications and to get access to all the features.
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);