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.
- checksums.yaml +8 -8
- data/Rakefile +1 -1
- data/ext/nutcracker/ChangeLog +10 -0
- data/ext/nutcracker/Makefile.am +2 -0
- data/ext/nutcracker/Makefile.in +101 -14
- data/ext/nutcracker/README.md +18 -1
- data/ext/nutcracker/config.h.in +18 -0
- data/ext/nutcracker/configure +196 -25
- data/ext/nutcracker/configure.ac +64 -6
- data/ext/nutcracker/extconf.rb +1 -1
- data/ext/nutcracker/man/nutcracker.8 +76 -0
- data/ext/nutcracker/notes/debug.txt +116 -16
- data/ext/nutcracker/notes/kqueue.pdf +0 -0
- data/ext/nutcracker/notes/recommendation.md +20 -0
- data/ext/nutcracker/notes/redis.md +2 -2
- data/ext/nutcracker/scripts/nutcracker.spec +1 -1
- data/ext/nutcracker/scripts/redis-check.sh +3 -1
- data/ext/nutcracker/src/Makefile.am +15 -6
- data/ext/nutcracker/src/Makefile.in +39 -36
- data/ext/nutcracker/src/event/Makefile.am +16 -0
- data/ext/nutcracker/src/event/Makefile.in +492 -0
- data/ext/nutcracker/src/event/nc_epoll.c +344 -0
- data/ext/nutcracker/src/event/nc_event.h +88 -0
- data/ext/nutcracker/src/event/nc_evport.c +420 -0
- data/ext/nutcracker/src/event/nc_kqueue.c +412 -0
- data/ext/nutcracker/src/hashkit/nc_crc32.c +19 -1
- data/ext/nutcracker/src/hashkit/nc_hashkit.h +3 -1
- data/ext/nutcracker/src/hashkit/nc_md5.c +257 -315
- data/ext/nutcracker/src/nc.c +12 -1
- data/ext/nutcracker/src/nc_connection.c +18 -1
- data/ext/nutcracker/src/nc_connection.h +1 -0
- data/ext/nutcracker/src/nc_core.c +22 -30
- data/ext/nutcracker/src/nc_core.h +22 -7
- data/ext/nutcracker/src/nc_proxy.c +8 -9
- data/ext/nutcracker/src/nc_queue.h +2 -0
- data/ext/nutcracker/src/nc_request.c +3 -4
- data/ext/nutcracker/src/nc_response.c +25 -8
- data/ext/nutcracker/src/nc_server.c +8 -6
- data/ext/nutcracker/src/nc_stats.c +46 -43
- data/ext/nutcracker/src/nc_stats.h +37 -30
- data/ext/nutcracker/src/nc_util.c +6 -1
- data/ext/nutcracker/src/proto/nc_redis.c +19 -5
- data/lib/nutcracker/version.rb +1 -1
- data/lib/nutcracker.rb +1 -1
- metadata +10 -4
- data/ext/nutcracker/src/nc_event.c +0 -214
- 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);
|