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.
- 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);
|