nutcracker 0.2.3
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.
- data/README.md +22 -0
- data/Rakefile +55 -0
- data/bin/nutcracker +2 -0
- data/ext/nutcracker/ChangeLog +66 -0
- data/ext/nutcracker/LICENSE +177 -0
- data/ext/nutcracker/Makefile.am +7 -0
- data/ext/nutcracker/Makefile.in +726 -0
- data/ext/nutcracker/NOTICE +124 -0
- data/ext/nutcracker/README.md +240 -0
- data/ext/nutcracker/aclocal.m4 +956 -0
- data/ext/nutcracker/conf/nutcracker.leaf.yml +10 -0
- data/ext/nutcracker/conf/nutcracker.root.yml +8 -0
- data/ext/nutcracker/conf/nutcracker.yml +67 -0
- data/ext/nutcracker/config.h.in +316 -0
- data/ext/nutcracker/config/config.guess +1561 -0
- data/ext/nutcracker/config/config.sub +1686 -0
- data/ext/nutcracker/config/depcomp +630 -0
- data/ext/nutcracker/config/install-sh +520 -0
- data/ext/nutcracker/config/ltmain.sh +8413 -0
- data/ext/nutcracker/config/missing +376 -0
- data/ext/nutcracker/configure +18862 -0
- data/ext/nutcracker/configure.ac +155 -0
- data/ext/nutcracker/contrib/Makefile.am +3 -0
- data/ext/nutcracker/contrib/Makefile.in +560 -0
- data/ext/nutcracker/contrib/yaml-0.1.4.tar.gz +0 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/LICENSE +19 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.am +20 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.in +736 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/README +27 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/aclocal.m4 +956 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config.h.in +80 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/config.guess +1561 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/config.sub +1686 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/depcomp +630 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/install-sh +520 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/ltmain.sh +8406 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/config/missing +376 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/configure +13085 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/configure.ac +75 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/doc/doxygen.cfg +222 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/include/yaml.h +1971 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/libtool.m4 +7357 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltoptions.m4 +368 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltsugar.m4 +123 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltversion.m4 +23 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/m4/lt~obsolete.m4 +92 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.am +4 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.in +484 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/api.c +1392 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/dumper.c +394 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/emitter.c +2329 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/loader.c +432 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/parser.c +1374 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/reader.c +465 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/scanner.c +3570 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/writer.c +141 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/src/yaml_private.h +640 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.am +8 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.in +675 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor-alt.c +800 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor.c +1130 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter-alt.c +217 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter.c +202 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-dumper.c +311 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-emitter.c +327 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-loader.c +63 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-parser.c +63 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-scanner.c +63 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-reader.c +354 -0
- data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-version.c +29 -0
- data/ext/nutcracker/extconf.rb +5 -0
- data/ext/nutcracker/m4/libtool.m4 +7376 -0
- data/ext/nutcracker/m4/ltoptions.m4 +368 -0
- data/ext/nutcracker/m4/ltsugar.m4 +123 -0
- data/ext/nutcracker/m4/ltversion.m4 +23 -0
- data/ext/nutcracker/m4/lt~obsolete.m4 +92 -0
- data/ext/nutcracker/notes/c-styleguide.txt +425 -0
- data/ext/nutcracker/notes/debug.txt +96 -0
- data/ext/nutcracker/notes/memcache.txt +123 -0
- data/ext/nutcracker/notes/recommendation.md +118 -0
- data/ext/nutcracker/notes/redis.md +415 -0
- data/ext/nutcracker/notes/socket.txt +131 -0
- data/ext/nutcracker/scripts/multi_get.sh +26 -0
- data/ext/nutcracker/scripts/nutcracker.init +73 -0
- data/ext/nutcracker/scripts/nutcracker.spec +52 -0
- data/ext/nutcracker/scripts/pipelined_read.sh +23 -0
- data/ext/nutcracker/scripts/pipelined_write.sh +29 -0
- data/ext/nutcracker/scripts/populate_memcached.sh +24 -0
- data/ext/nutcracker/scripts/redis-check.py +23 -0
- data/ext/nutcracker/scripts/redis-check.sh +564 -0
- data/ext/nutcracker/src/Makefile.am +46 -0
- data/ext/nutcracker/src/Makefile.in +726 -0
- data/ext/nutcracker/src/hashkit/Makefile.am +22 -0
- data/ext/nutcracker/src/hashkit/Makefile.in +501 -0
- data/ext/nutcracker/src/hashkit/nc_crc32.c +105 -0
- data/ext/nutcracker/src/hashkit/nc_fnv.c +82 -0
- data/ext/nutcracker/src/hashkit/nc_hashkit.h +74 -0
- data/ext/nutcracker/src/hashkit/nc_hsieh.c +93 -0
- data/ext/nutcracker/src/hashkit/nc_jenkins.c +230 -0
- data/ext/nutcracker/src/hashkit/nc_ketama.c +240 -0
- data/ext/nutcracker/src/hashkit/nc_md5.c +379 -0
- data/ext/nutcracker/src/hashkit/nc_modula.c +144 -0
- data/ext/nutcracker/src/hashkit/nc_murmur.c +99 -0
- data/ext/nutcracker/src/hashkit/nc_one_at_a_time.c +51 -0
- data/ext/nutcracker/src/hashkit/nc_random.c +146 -0
- data/ext/nutcracker/src/nc.c +573 -0
- data/ext/nutcracker/src/nc_array.c +204 -0
- data/ext/nutcracker/src/nc_array.h +73 -0
- data/ext/nutcracker/src/nc_client.c +189 -0
- data/ext/nutcracker/src/nc_client.h +28 -0
- data/ext/nutcracker/src/nc_conf.c +1766 -0
- data/ext/nutcracker/src/nc_conf.h +134 -0
- data/ext/nutcracker/src/nc_connection.c +392 -0
- data/ext/nutcracker/src/nc_connection.h +99 -0
- data/ext/nutcracker/src/nc_core.c +334 -0
- data/ext/nutcracker/src/nc_core.h +131 -0
- data/ext/nutcracker/src/nc_event.c +214 -0
- data/ext/nutcracker/src/nc_event.h +39 -0
- data/ext/nutcracker/src/nc_log.c +254 -0
- data/ext/nutcracker/src/nc_log.h +120 -0
- data/ext/nutcracker/src/nc_mbuf.c +285 -0
- data/ext/nutcracker/src/nc_mbuf.h +67 -0
- data/ext/nutcracker/src/nc_message.c +828 -0
- data/ext/nutcracker/src/nc_message.h +253 -0
- data/ext/nutcracker/src/nc_proxy.c +359 -0
- data/ext/nutcracker/src/nc_proxy.h +34 -0
- data/ext/nutcracker/src/nc_queue.h +788 -0
- data/ext/nutcracker/src/nc_rbtree.c +348 -0
- data/ext/nutcracker/src/nc_rbtree.h +47 -0
- data/ext/nutcracker/src/nc_request.c +588 -0
- data/ext/nutcracker/src/nc_response.c +332 -0
- data/ext/nutcracker/src/nc_server.c +841 -0
- data/ext/nutcracker/src/nc_server.h +143 -0
- data/ext/nutcracker/src/nc_signal.c +131 -0
- data/ext/nutcracker/src/nc_signal.h +34 -0
- data/ext/nutcracker/src/nc_stats.c +1188 -0
- data/ext/nutcracker/src/nc_stats.h +206 -0
- data/ext/nutcracker/src/nc_string.c +109 -0
- data/ext/nutcracker/src/nc_string.h +112 -0
- data/ext/nutcracker/src/nc_util.c +619 -0
- data/ext/nutcracker/src/nc_util.h +214 -0
- data/ext/nutcracker/src/proto/Makefile.am +14 -0
- data/ext/nutcracker/src/proto/Makefile.in +482 -0
- data/ext/nutcracker/src/proto/nc_memcache.c +1306 -0
- data/ext/nutcracker/src/proto/nc_proto.h +155 -0
- data/ext/nutcracker/src/proto/nc_redis.c +2102 -0
- data/lib/nutcracker.rb +7 -0
- data/lib/nutcracker/version.rb +3 -0
- metadata +194 -0
@@ -0,0 +1,332 @@
|
|
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
|
+
#include <nc_server.h>
|
20
|
+
#include <nc_event.h>
|
21
|
+
|
22
|
+
struct msg *
|
23
|
+
rsp_get(struct conn *conn)
|
24
|
+
{
|
25
|
+
struct msg *msg;
|
26
|
+
|
27
|
+
ASSERT(!conn->client && !conn->proxy);
|
28
|
+
|
29
|
+
msg = msg_get(conn, false, conn->redis);
|
30
|
+
if (msg == NULL) {
|
31
|
+
conn->err = errno;
|
32
|
+
}
|
33
|
+
|
34
|
+
return msg;
|
35
|
+
}
|
36
|
+
|
37
|
+
void
|
38
|
+
rsp_put(struct msg *msg)
|
39
|
+
{
|
40
|
+
ASSERT(!msg->request);
|
41
|
+
ASSERT(msg->peer == NULL);
|
42
|
+
msg_put(msg);
|
43
|
+
}
|
44
|
+
|
45
|
+
static struct msg *
|
46
|
+
rsp_make_error(struct context *ctx, struct conn *conn, struct msg *msg)
|
47
|
+
{
|
48
|
+
struct msg *pmsg; /* peer message (response) */
|
49
|
+
struct msg *cmsg, *nmsg; /* current and next message (request) */
|
50
|
+
uint64_t id;
|
51
|
+
err_t err;
|
52
|
+
|
53
|
+
ASSERT(conn->client && !conn->proxy);
|
54
|
+
ASSERT(msg->request && req_error(conn, msg));
|
55
|
+
ASSERT(msg->owner == conn);
|
56
|
+
|
57
|
+
id = msg->frag_id;
|
58
|
+
if (id != 0) {
|
59
|
+
for (err = 0, cmsg = TAILQ_NEXT(msg, c_tqe);
|
60
|
+
cmsg != NULL && cmsg->frag_id == id;
|
61
|
+
cmsg = nmsg) {
|
62
|
+
nmsg = TAILQ_NEXT(cmsg, c_tqe);
|
63
|
+
|
64
|
+
/* dequeue request (error fragment) from client outq */
|
65
|
+
conn->dequeue_outq(ctx, conn, cmsg);
|
66
|
+
if (err == 0 && cmsg->err != 0) {
|
67
|
+
err = cmsg->err;
|
68
|
+
}
|
69
|
+
|
70
|
+
req_put(cmsg);
|
71
|
+
}
|
72
|
+
} else {
|
73
|
+
err = msg->err;
|
74
|
+
}
|
75
|
+
|
76
|
+
pmsg = msg->peer;
|
77
|
+
if (pmsg != NULL) {
|
78
|
+
ASSERT(!pmsg->request && pmsg->peer == msg);
|
79
|
+
msg->peer = NULL;
|
80
|
+
pmsg->peer = NULL;
|
81
|
+
rsp_put(pmsg);
|
82
|
+
}
|
83
|
+
|
84
|
+
return msg_get_error(conn->redis, err);
|
85
|
+
}
|
86
|
+
|
87
|
+
struct msg *
|
88
|
+
rsp_recv_next(struct context *ctx, struct conn *conn, bool alloc)
|
89
|
+
{
|
90
|
+
struct msg *msg;
|
91
|
+
|
92
|
+
ASSERT(!conn->client && !conn->proxy);
|
93
|
+
ASSERT(!conn->connecting);
|
94
|
+
|
95
|
+
if (conn->eof) {
|
96
|
+
msg = conn->rmsg;
|
97
|
+
|
98
|
+
/* server sent eof before sending the entire request */
|
99
|
+
if (msg != NULL) {
|
100
|
+
conn->rmsg = NULL;
|
101
|
+
|
102
|
+
ASSERT(msg->peer == NULL);
|
103
|
+
ASSERT(!msg->request);
|
104
|
+
|
105
|
+
log_error("eof s %d discarding incomplete rsp %"PRIu64" len "
|
106
|
+
"%"PRIu32"", conn->sd, msg->id, msg->mlen);
|
107
|
+
|
108
|
+
rsp_put(msg);
|
109
|
+
}
|
110
|
+
|
111
|
+
/*
|
112
|
+
* We treat TCP half-close from a server different from how we treat
|
113
|
+
* those from a client. On a FIN from a server, we close the connection
|
114
|
+
* immediately by sending the second FIN even if there were outstanding
|
115
|
+
* or pending requests. This is actually a tricky part in the FA, as
|
116
|
+
* we don't expect this to happen unless the server is misbehaving or
|
117
|
+
* it crashes
|
118
|
+
*/
|
119
|
+
conn->done = 1;
|
120
|
+
log_error("s %d active %d is done", conn->sd, conn->active(conn));
|
121
|
+
|
122
|
+
return NULL;
|
123
|
+
}
|
124
|
+
|
125
|
+
msg = conn->rmsg;
|
126
|
+
if (msg != NULL) {
|
127
|
+
ASSERT(!msg->request);
|
128
|
+
return msg;
|
129
|
+
}
|
130
|
+
|
131
|
+
if (!alloc) {
|
132
|
+
return NULL;
|
133
|
+
}
|
134
|
+
|
135
|
+
msg = rsp_get(conn);
|
136
|
+
if (msg != NULL) {
|
137
|
+
conn->rmsg = msg;
|
138
|
+
}
|
139
|
+
|
140
|
+
return msg;
|
141
|
+
}
|
142
|
+
|
143
|
+
static bool
|
144
|
+
rsp_filter(struct context *ctx, struct conn *conn, struct msg *msg)
|
145
|
+
{
|
146
|
+
struct msg *pmsg;
|
147
|
+
|
148
|
+
ASSERT(!conn->client && !conn->proxy);
|
149
|
+
|
150
|
+
if (msg_empty(msg)) {
|
151
|
+
ASSERT(conn->rmsg == NULL);
|
152
|
+
log_debug(LOG_VERB, "filter empty rsp %"PRIu64" on s %d", msg->id,
|
153
|
+
conn->sd);
|
154
|
+
rsp_put(msg);
|
155
|
+
return true;
|
156
|
+
}
|
157
|
+
|
158
|
+
pmsg = TAILQ_FIRST(&conn->omsg_q);
|
159
|
+
if (pmsg == NULL) {
|
160
|
+
log_error("filter stray rsp %"PRIu64" len %"PRIu32" on s %d", msg->id,
|
161
|
+
msg->mlen, conn->sd);
|
162
|
+
rsp_put(msg);
|
163
|
+
errno = EINVAL;
|
164
|
+
conn->err = errno;
|
165
|
+
return true;
|
166
|
+
}
|
167
|
+
ASSERT(pmsg->peer == NULL);
|
168
|
+
ASSERT(pmsg->request && !pmsg->done);
|
169
|
+
|
170
|
+
if (pmsg->swallow) {
|
171
|
+
conn->dequeue_outq(ctx, conn, pmsg);
|
172
|
+
pmsg->done = 1;
|
173
|
+
|
174
|
+
log_debug(LOG_INFO, "swallow rsp %"PRIu64" len %"PRIu32" of req "
|
175
|
+
"%"PRIu64" on s %d", msg->id, msg->mlen, pmsg->id,
|
176
|
+
conn->sd);
|
177
|
+
|
178
|
+
rsp_put(msg);
|
179
|
+
req_put(pmsg);
|
180
|
+
return true;
|
181
|
+
}
|
182
|
+
|
183
|
+
return false;
|
184
|
+
}
|
185
|
+
|
186
|
+
static void
|
187
|
+
rsp_forward_stats(struct context *ctx, struct server *server, struct msg *msg)
|
188
|
+
{
|
189
|
+
ASSERT(!msg->request);
|
190
|
+
|
191
|
+
stats_server_incr(ctx, server, responses);
|
192
|
+
stats_server_incr_by(ctx, server, response_bytes, msg->mlen);
|
193
|
+
}
|
194
|
+
|
195
|
+
static void
|
196
|
+
rsp_forward(struct context *ctx, struct conn *s_conn, struct msg *msg)
|
197
|
+
{
|
198
|
+
rstatus_t status;
|
199
|
+
struct msg *pmsg;
|
200
|
+
struct conn *c_conn;
|
201
|
+
|
202
|
+
ASSERT(!s_conn->client && !s_conn->proxy);
|
203
|
+
|
204
|
+
/* response from server implies that server is ok and heartbeating */
|
205
|
+
server_ok(ctx, s_conn);
|
206
|
+
|
207
|
+
/* dequeue peer message (request) from server */
|
208
|
+
pmsg = TAILQ_FIRST(&s_conn->omsg_q);
|
209
|
+
ASSERT(pmsg != NULL && pmsg->peer == NULL);
|
210
|
+
ASSERT(pmsg->request && !pmsg->done);
|
211
|
+
|
212
|
+
s_conn->dequeue_outq(ctx, s_conn, pmsg);
|
213
|
+
pmsg->done = 1;
|
214
|
+
|
215
|
+
/* establish msg <-> pmsg (response <-> request) link */
|
216
|
+
pmsg->peer = msg;
|
217
|
+
msg->peer = pmsg;
|
218
|
+
|
219
|
+
msg->pre_coalesce(msg);
|
220
|
+
|
221
|
+
c_conn = pmsg->owner;
|
222
|
+
ASSERT(c_conn->client && !c_conn->proxy);
|
223
|
+
|
224
|
+
if (req_done(c_conn, TAILQ_FIRST(&c_conn->omsg_q))) {
|
225
|
+
status = event_add_out(ctx->ep, c_conn);
|
226
|
+
if (status != NC_OK) {
|
227
|
+
c_conn->err = errno;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
rsp_forward_stats(ctx, s_conn->owner, msg);
|
232
|
+
}
|
233
|
+
|
234
|
+
void
|
235
|
+
rsp_recv_done(struct context *ctx, struct conn *conn, struct msg *msg,
|
236
|
+
struct msg *nmsg)
|
237
|
+
{
|
238
|
+
ASSERT(!conn->client && !conn->proxy);
|
239
|
+
ASSERT(msg != NULL && conn->rmsg == msg);
|
240
|
+
ASSERT(!msg->request);
|
241
|
+
ASSERT(msg->owner == conn);
|
242
|
+
ASSERT(nmsg == NULL || !nmsg->request);
|
243
|
+
|
244
|
+
/* enqueue next message (response), if any */
|
245
|
+
conn->rmsg = nmsg;
|
246
|
+
|
247
|
+
if (rsp_filter(ctx, conn, msg)) {
|
248
|
+
return;
|
249
|
+
}
|
250
|
+
|
251
|
+
rsp_forward(ctx, conn, msg);
|
252
|
+
}
|
253
|
+
|
254
|
+
struct msg *
|
255
|
+
rsp_send_next(struct context *ctx, struct conn *conn)
|
256
|
+
{
|
257
|
+
rstatus_t status;
|
258
|
+
struct msg *msg, *pmsg; /* response and it's peer request */
|
259
|
+
|
260
|
+
ASSERT(conn->client && !conn->proxy);
|
261
|
+
|
262
|
+
pmsg = TAILQ_FIRST(&conn->omsg_q);
|
263
|
+
if (pmsg == NULL || !req_done(conn, pmsg)) {
|
264
|
+
/* nothing is outstanding, initiate close? */
|
265
|
+
if (pmsg == NULL && conn->eof) {
|
266
|
+
conn->done = 1;
|
267
|
+
log_debug(LOG_INFO, "c %d is done", conn->sd);
|
268
|
+
}
|
269
|
+
|
270
|
+
status = event_del_out(ctx->ep, conn);
|
271
|
+
if (status != NC_OK) {
|
272
|
+
conn->err = errno;
|
273
|
+
}
|
274
|
+
|
275
|
+
return NULL;
|
276
|
+
}
|
277
|
+
|
278
|
+
msg = conn->smsg;
|
279
|
+
if (msg != NULL) {
|
280
|
+
ASSERT(!msg->request && msg->peer != NULL);
|
281
|
+
ASSERT(req_done(conn, msg->peer));
|
282
|
+
pmsg = TAILQ_NEXT(msg->peer, c_tqe);
|
283
|
+
}
|
284
|
+
|
285
|
+
if (pmsg == NULL || !req_done(conn, pmsg)) {
|
286
|
+
conn->smsg = NULL;
|
287
|
+
return NULL;
|
288
|
+
}
|
289
|
+
ASSERT(pmsg->request && !pmsg->swallow);
|
290
|
+
|
291
|
+
if (req_error(conn, pmsg)) {
|
292
|
+
msg = rsp_make_error(ctx, conn, pmsg);
|
293
|
+
if (msg == NULL) {
|
294
|
+
conn->err = errno;
|
295
|
+
return NULL;
|
296
|
+
}
|
297
|
+
msg->peer = pmsg;
|
298
|
+
pmsg->peer = msg;
|
299
|
+
stats_pool_incr(ctx, conn->owner, forward_error);
|
300
|
+
} else {
|
301
|
+
msg = pmsg->peer;
|
302
|
+
}
|
303
|
+
ASSERT(!msg->request);
|
304
|
+
|
305
|
+
conn->smsg = msg;
|
306
|
+
|
307
|
+
log_debug(LOG_VVERB, "send next rsp %"PRIu64" on c %d", msg->id, conn->sd);
|
308
|
+
|
309
|
+
return msg;
|
310
|
+
}
|
311
|
+
|
312
|
+
void
|
313
|
+
rsp_send_done(struct context *ctx, struct conn *conn, struct msg *msg)
|
314
|
+
{
|
315
|
+
struct msg *pmsg; /* peer message (request) */
|
316
|
+
|
317
|
+
ASSERT(conn->client && !conn->proxy);
|
318
|
+
ASSERT(conn->smsg == NULL);
|
319
|
+
|
320
|
+
log_debug(LOG_VVERB, "send done rsp %"PRIu64" on c %d", msg->id, conn->sd);
|
321
|
+
|
322
|
+
pmsg = msg->peer;
|
323
|
+
|
324
|
+
ASSERT(!msg->request && pmsg->request);
|
325
|
+
ASSERT(pmsg->peer == msg);
|
326
|
+
ASSERT(pmsg->done && !pmsg->swallow);
|
327
|
+
|
328
|
+
/* dequeue request from client outq */
|
329
|
+
conn->dequeue_outq(ctx, conn, pmsg);
|
330
|
+
|
331
|
+
req_put(pmsg);
|
332
|
+
}
|
@@ -0,0 +1,841 @@
|
|
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 <stdlib.h>
|
19
|
+
#include <unistd.h>
|
20
|
+
|
21
|
+
#include <nc_core.h>
|
22
|
+
#include <nc_event.h>
|
23
|
+
#include <nc_server.h>
|
24
|
+
#include <nc_conf.h>
|
25
|
+
|
26
|
+
void
|
27
|
+
server_ref(struct conn *conn, void *owner)
|
28
|
+
{
|
29
|
+
struct server *server = owner;
|
30
|
+
|
31
|
+
ASSERT(!conn->client && !conn->proxy);
|
32
|
+
ASSERT(conn->owner == NULL);
|
33
|
+
|
34
|
+
conn->family = server->family;
|
35
|
+
conn->addrlen = server->addrlen;
|
36
|
+
conn->addr = server->addr;
|
37
|
+
|
38
|
+
server->ns_conn_q++;
|
39
|
+
TAILQ_INSERT_TAIL(&server->s_conn_q, conn, conn_tqe);
|
40
|
+
|
41
|
+
conn->owner = owner;
|
42
|
+
|
43
|
+
log_debug(LOG_VVERB, "ref conn %p owner %p into '%.*s", conn, server,
|
44
|
+
server->pname.len, server->pname.data);
|
45
|
+
}
|
46
|
+
|
47
|
+
void
|
48
|
+
server_unref(struct conn *conn)
|
49
|
+
{
|
50
|
+
struct server *server;
|
51
|
+
|
52
|
+
ASSERT(!conn->client && !conn->proxy);
|
53
|
+
ASSERT(conn->owner != NULL);
|
54
|
+
|
55
|
+
server = conn->owner;
|
56
|
+
conn->owner = NULL;
|
57
|
+
|
58
|
+
ASSERT(server->ns_conn_q != 0);
|
59
|
+
server->ns_conn_q--;
|
60
|
+
TAILQ_REMOVE(&server->s_conn_q, conn, conn_tqe);
|
61
|
+
|
62
|
+
log_debug(LOG_VVERB, "unref conn %p owner %p from '%.*s'", conn, server,
|
63
|
+
server->pname.len, server->pname.data);
|
64
|
+
}
|
65
|
+
|
66
|
+
int
|
67
|
+
server_timeout(struct conn *conn)
|
68
|
+
{
|
69
|
+
struct server *server;
|
70
|
+
struct server_pool *pool;
|
71
|
+
|
72
|
+
ASSERT(!conn->client && !conn->proxy);
|
73
|
+
|
74
|
+
server = conn->owner;
|
75
|
+
pool = server->owner;
|
76
|
+
|
77
|
+
return pool->timeout;
|
78
|
+
}
|
79
|
+
|
80
|
+
bool
|
81
|
+
server_active(struct conn *conn)
|
82
|
+
{
|
83
|
+
ASSERT(!conn->client && !conn->proxy);
|
84
|
+
|
85
|
+
if (!TAILQ_EMPTY(&conn->imsg_q)) {
|
86
|
+
log_debug(LOG_VVERB, "s %d is active", conn->sd);
|
87
|
+
return true;
|
88
|
+
}
|
89
|
+
|
90
|
+
if (!TAILQ_EMPTY(&conn->omsg_q)) {
|
91
|
+
log_debug(LOG_VVERB, "s %d is active", conn->sd);
|
92
|
+
return true;
|
93
|
+
}
|
94
|
+
|
95
|
+
if (conn->rmsg != NULL) {
|
96
|
+
log_debug(LOG_VVERB, "s %d is active", conn->sd);
|
97
|
+
return true;
|
98
|
+
}
|
99
|
+
|
100
|
+
if (conn->smsg != NULL) {
|
101
|
+
log_debug(LOG_VVERB, "s %d is active", conn->sd);
|
102
|
+
return true;
|
103
|
+
}
|
104
|
+
|
105
|
+
log_debug(LOG_VVERB, "s %d is inactive", conn->sd);
|
106
|
+
|
107
|
+
return false;
|
108
|
+
}
|
109
|
+
|
110
|
+
static rstatus_t
|
111
|
+
server_each_set_owner(void *elem, void *data)
|
112
|
+
{
|
113
|
+
struct server *s = elem;
|
114
|
+
struct server_pool *sp = data;
|
115
|
+
|
116
|
+
s->owner = sp;
|
117
|
+
|
118
|
+
return NC_OK;
|
119
|
+
}
|
120
|
+
|
121
|
+
rstatus_t
|
122
|
+
server_init(struct array *server, struct array *conf_server,
|
123
|
+
struct server_pool *sp)
|
124
|
+
{
|
125
|
+
rstatus_t status;
|
126
|
+
uint32_t nserver;
|
127
|
+
|
128
|
+
nserver = array_n(conf_server);
|
129
|
+
ASSERT(nserver != 0);
|
130
|
+
ASSERT(array_n(server) == 0);
|
131
|
+
|
132
|
+
status = array_init(server, nserver, sizeof(struct server));
|
133
|
+
if (status != NC_OK) {
|
134
|
+
return status;
|
135
|
+
}
|
136
|
+
|
137
|
+
/* transform conf server to server */
|
138
|
+
status = array_each(conf_server, conf_server_each_transform, server);
|
139
|
+
if (status != NC_OK) {
|
140
|
+
server_deinit(server);
|
141
|
+
return status;
|
142
|
+
}
|
143
|
+
ASSERT(array_n(server) == nserver);
|
144
|
+
|
145
|
+
/* set server owner */
|
146
|
+
status = array_each(server, server_each_set_owner, sp);
|
147
|
+
if (status != NC_OK) {
|
148
|
+
server_deinit(server);
|
149
|
+
return status;
|
150
|
+
}
|
151
|
+
|
152
|
+
log_debug(LOG_DEBUG, "init %"PRIu32" servers in pool %"PRIu32" '%.*s'",
|
153
|
+
nserver, sp->idx, sp->name.len, sp->name.data);
|
154
|
+
|
155
|
+
return NC_OK;
|
156
|
+
}
|
157
|
+
|
158
|
+
void
|
159
|
+
server_deinit(struct array *server)
|
160
|
+
{
|
161
|
+
uint32_t i, nserver;
|
162
|
+
|
163
|
+
for (i = 0, nserver = array_n(server); i < nserver; i++) {
|
164
|
+
struct server *s;
|
165
|
+
|
166
|
+
s = array_pop(server);
|
167
|
+
ASSERT(TAILQ_EMPTY(&s->s_conn_q) && s->ns_conn_q == 0);
|
168
|
+
}
|
169
|
+
array_deinit(server);
|
170
|
+
}
|
171
|
+
|
172
|
+
struct conn *
|
173
|
+
server_conn(struct server *server)
|
174
|
+
{
|
175
|
+
struct server_pool *pool;
|
176
|
+
struct conn *conn;
|
177
|
+
|
178
|
+
pool = server->owner;
|
179
|
+
|
180
|
+
/*
|
181
|
+
* FIXME: handle multiple server connections per server and do load
|
182
|
+
* balancing on it. Support multiple algorithms for
|
183
|
+
* 'server_connections:' > 0 key
|
184
|
+
*/
|
185
|
+
|
186
|
+
if (server->ns_conn_q < pool->server_connections) {
|
187
|
+
return conn_get(server, false, pool->redis);
|
188
|
+
}
|
189
|
+
ASSERT(server->ns_conn_q == pool->server_connections);
|
190
|
+
|
191
|
+
/*
|
192
|
+
* Pick a server connection from the head of the queue and insert
|
193
|
+
* it back into the tail of queue to maintain the lru order
|
194
|
+
*/
|
195
|
+
conn = TAILQ_FIRST(&server->s_conn_q);
|
196
|
+
ASSERT(!conn->client && !conn->proxy);
|
197
|
+
|
198
|
+
TAILQ_REMOVE(&server->s_conn_q, conn, conn_tqe);
|
199
|
+
TAILQ_INSERT_TAIL(&server->s_conn_q, conn, conn_tqe);
|
200
|
+
|
201
|
+
return conn;
|
202
|
+
}
|
203
|
+
|
204
|
+
static rstatus_t
|
205
|
+
server_each_preconnect(void *elem, void *data)
|
206
|
+
{
|
207
|
+
rstatus_t status;
|
208
|
+
struct server *server;
|
209
|
+
struct server_pool *pool;
|
210
|
+
struct conn *conn;
|
211
|
+
|
212
|
+
server = elem;
|
213
|
+
pool = server->owner;
|
214
|
+
|
215
|
+
conn = server_conn(server);
|
216
|
+
if (conn == NULL) {
|
217
|
+
return NC_ENOMEM;
|
218
|
+
}
|
219
|
+
|
220
|
+
status = server_connect(pool->ctx, server, conn);
|
221
|
+
if (status != NC_OK) {
|
222
|
+
log_warn("connect to server '%.*s' failed, ignored: %s",
|
223
|
+
server->pname.len, server->pname.data, strerror(errno));
|
224
|
+
server_close(pool->ctx, conn);
|
225
|
+
}
|
226
|
+
|
227
|
+
return NC_OK;
|
228
|
+
}
|
229
|
+
|
230
|
+
static rstatus_t
|
231
|
+
server_each_disconnect(void *elem, void *data)
|
232
|
+
{
|
233
|
+
struct server *server;
|
234
|
+
struct server_pool *pool;
|
235
|
+
|
236
|
+
server = elem;
|
237
|
+
pool = server->owner;
|
238
|
+
|
239
|
+
while (!TAILQ_EMPTY(&server->s_conn_q)) {
|
240
|
+
struct conn *conn;
|
241
|
+
|
242
|
+
ASSERT(server->ns_conn_q > 0);
|
243
|
+
|
244
|
+
conn = TAILQ_FIRST(&server->s_conn_q);
|
245
|
+
conn->close(pool->ctx, conn);
|
246
|
+
}
|
247
|
+
|
248
|
+
return NC_OK;
|
249
|
+
}
|
250
|
+
|
251
|
+
static void
|
252
|
+
server_failure(struct context *ctx, struct server *server)
|
253
|
+
{
|
254
|
+
struct server_pool *pool = server->owner;
|
255
|
+
int64_t now, next;
|
256
|
+
rstatus_t status;
|
257
|
+
|
258
|
+
if (!pool->auto_eject_hosts) {
|
259
|
+
return;
|
260
|
+
}
|
261
|
+
|
262
|
+
server->failure_count++;
|
263
|
+
|
264
|
+
log_debug(LOG_VERB, "server '%.*s' failure count %"PRIu32" limit %"PRIu32,
|
265
|
+
server->pname.len, server->pname.data, server->failure_count,
|
266
|
+
pool->server_failure_limit);
|
267
|
+
|
268
|
+
if (server->failure_count < pool->server_failure_limit) {
|
269
|
+
return;
|
270
|
+
}
|
271
|
+
|
272
|
+
now = nc_usec_now();
|
273
|
+
if (now < 0) {
|
274
|
+
return;
|
275
|
+
}
|
276
|
+
next = now + pool->server_retry_timeout;
|
277
|
+
|
278
|
+
log_debug(LOG_INFO, "update pool %"PRIu32" '%.*s' to delete server '%.*s' "
|
279
|
+
"for next %"PRIu32" secs", pool->idx, pool->name.len,
|
280
|
+
pool->name.data, server->pname.len, server->pname.data,
|
281
|
+
pool->server_retry_timeout / 1000 / 1000);
|
282
|
+
|
283
|
+
stats_pool_incr(ctx, pool, server_ejects);
|
284
|
+
|
285
|
+
server->failure_count = 0;
|
286
|
+
server->next_retry = next;
|
287
|
+
|
288
|
+
status = server_pool_run(pool);
|
289
|
+
if (status != NC_OK) {
|
290
|
+
log_error("updating pool %"PRIu32" '%.*s' failed: %s", pool->idx,
|
291
|
+
pool->name.len, pool->name.data, strerror(errno));
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
static void
|
296
|
+
server_close_stats(struct context *ctx, struct server *server, err_t err,
|
297
|
+
unsigned eof, unsigned connected)
|
298
|
+
{
|
299
|
+
if (connected) {
|
300
|
+
stats_server_decr(ctx, server, server_connections);
|
301
|
+
}
|
302
|
+
|
303
|
+
if (eof) {
|
304
|
+
stats_server_incr(ctx, server, server_eof);
|
305
|
+
return;
|
306
|
+
}
|
307
|
+
|
308
|
+
switch (err) {
|
309
|
+
case ETIMEDOUT:
|
310
|
+
stats_server_incr(ctx, server, server_timedout);
|
311
|
+
break;
|
312
|
+
case EPIPE:
|
313
|
+
case ECONNRESET:
|
314
|
+
case ECONNABORTED:
|
315
|
+
case ECONNREFUSED:
|
316
|
+
case ENOTCONN:
|
317
|
+
case ENETDOWN:
|
318
|
+
case ENETUNREACH:
|
319
|
+
case EHOSTDOWN:
|
320
|
+
case EHOSTUNREACH:
|
321
|
+
default:
|
322
|
+
stats_server_incr(ctx, server, server_err);
|
323
|
+
break;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
void
|
328
|
+
server_close(struct context *ctx, struct conn *conn)
|
329
|
+
{
|
330
|
+
rstatus_t status;
|
331
|
+
struct msg *msg, *nmsg; /* current and next message */
|
332
|
+
struct conn *c_conn; /* peer client connection */
|
333
|
+
|
334
|
+
ASSERT(!conn->client && !conn->proxy);
|
335
|
+
|
336
|
+
server_close_stats(ctx, conn->owner, conn->err, conn->eof,
|
337
|
+
conn->connected);
|
338
|
+
|
339
|
+
if (conn->sd < 0) {
|
340
|
+
server_failure(ctx, conn->owner);
|
341
|
+
conn->unref(conn);
|
342
|
+
conn_put(conn);
|
343
|
+
return;
|
344
|
+
}
|
345
|
+
|
346
|
+
for (msg = TAILQ_FIRST(&conn->imsg_q); msg != NULL; msg = nmsg) {
|
347
|
+
nmsg = TAILQ_NEXT(msg, s_tqe);
|
348
|
+
|
349
|
+
/* dequeue the message (request) from server inq */
|
350
|
+
conn->dequeue_inq(ctx, conn, msg);
|
351
|
+
|
352
|
+
/*
|
353
|
+
* Don't send any error response, if
|
354
|
+
* 1. request is tagged as noreply or,
|
355
|
+
* 2. client has already closed its connection
|
356
|
+
*/
|
357
|
+
if (msg->swallow || msg->noreply) {
|
358
|
+
log_debug(LOG_INFO, "close s %d swallow req %"PRIu64" len %"PRIu32
|
359
|
+
" type %d", conn->sd, msg->id, msg->mlen, msg->type);
|
360
|
+
req_put(msg);
|
361
|
+
} else {
|
362
|
+
c_conn = msg->owner;
|
363
|
+
ASSERT(c_conn->client && !c_conn->proxy);
|
364
|
+
|
365
|
+
msg->done = 1;
|
366
|
+
msg->error = 1;
|
367
|
+
msg->err = conn->err;
|
368
|
+
|
369
|
+
if (req_done(c_conn, TAILQ_FIRST(&c_conn->omsg_q))) {
|
370
|
+
event_add_out(ctx->ep, msg->owner);
|
371
|
+
}
|
372
|
+
|
373
|
+
log_debug(LOG_INFO, "close s %d schedule error for req %"PRIu64" "
|
374
|
+
"len %"PRIu32" type %d from c %d%c %s", conn->sd, msg->id,
|
375
|
+
msg->mlen, msg->type, c_conn->sd, conn->err ? ':' : ' ',
|
376
|
+
conn->err ? strerror(conn->err): " ");
|
377
|
+
}
|
378
|
+
}
|
379
|
+
ASSERT(TAILQ_EMPTY(&conn->imsg_q));
|
380
|
+
|
381
|
+
for (msg = TAILQ_FIRST(&conn->omsg_q); msg != NULL; msg = nmsg) {
|
382
|
+
nmsg = TAILQ_NEXT(msg, s_tqe);
|
383
|
+
|
384
|
+
/* dequeue the message (request) from server outq */
|
385
|
+
conn->dequeue_outq(ctx, conn, msg);
|
386
|
+
|
387
|
+
if (msg->swallow) {
|
388
|
+
log_debug(LOG_INFO, "close s %d swallow req %"PRIu64" len %"PRIu32
|
389
|
+
" type %d", conn->sd, msg->id, msg->mlen, msg->type);
|
390
|
+
req_put(msg);
|
391
|
+
} else {
|
392
|
+
c_conn = msg->owner;
|
393
|
+
ASSERT(c_conn->client && !c_conn->proxy);
|
394
|
+
|
395
|
+
msg->done = 1;
|
396
|
+
msg->error = 1;
|
397
|
+
msg->err = conn->err;
|
398
|
+
|
399
|
+
if (req_done(c_conn, TAILQ_FIRST(&c_conn->omsg_q))) {
|
400
|
+
event_add_out(ctx->ep, msg->owner);
|
401
|
+
}
|
402
|
+
|
403
|
+
log_debug(LOG_INFO, "close s %d schedule error for req %"PRIu64" "
|
404
|
+
"len %"PRIu32" type %d from c %d%c %s", conn->sd, msg->id,
|
405
|
+
msg->mlen, msg->type, c_conn->sd, conn->err ? ':' : ' ',
|
406
|
+
conn->err ? strerror(conn->err): " ");
|
407
|
+
}
|
408
|
+
}
|
409
|
+
ASSERT(TAILQ_EMPTY(&conn->omsg_q));
|
410
|
+
|
411
|
+
msg = conn->rmsg;
|
412
|
+
if (msg != NULL) {
|
413
|
+
conn->rmsg = NULL;
|
414
|
+
|
415
|
+
ASSERT(!msg->request);
|
416
|
+
ASSERT(msg->peer == NULL);
|
417
|
+
|
418
|
+
rsp_put(msg);
|
419
|
+
|
420
|
+
log_debug(LOG_INFO, "close s %d discarding rsp %"PRIu64" len %"PRIu32" "
|
421
|
+
"in error", conn->sd, msg->id, msg->mlen);
|
422
|
+
}
|
423
|
+
|
424
|
+
ASSERT(conn->smsg == NULL);
|
425
|
+
|
426
|
+
server_failure(ctx, conn->owner);
|
427
|
+
|
428
|
+
conn->unref(conn);
|
429
|
+
|
430
|
+
status = close(conn->sd);
|
431
|
+
if (status < 0) {
|
432
|
+
log_error("close s %d failed, ignored: %s", conn->sd, strerror(errno));
|
433
|
+
}
|
434
|
+
conn->sd = -1;
|
435
|
+
|
436
|
+
conn_put(conn);
|
437
|
+
}
|
438
|
+
|
439
|
+
rstatus_t
|
440
|
+
server_connect(struct context *ctx, struct server *server, struct conn *conn)
|
441
|
+
{
|
442
|
+
rstatus_t status;
|
443
|
+
|
444
|
+
ASSERT(!conn->client && !conn->proxy);
|
445
|
+
|
446
|
+
if (conn->sd > 0) {
|
447
|
+
/* already connected on server connection */
|
448
|
+
return NC_OK;
|
449
|
+
}
|
450
|
+
|
451
|
+
log_debug(LOG_VVERB, "connect to server '%.*s'", server->pname.len,
|
452
|
+
server->pname.data);
|
453
|
+
|
454
|
+
conn->sd = socket(conn->family, SOCK_STREAM, 0);
|
455
|
+
if (conn->sd < 0) {
|
456
|
+
log_error("socket for server '%.*s' failed: %s", server->pname.len,
|
457
|
+
server->pname.data, strerror(errno));
|
458
|
+
status = NC_ERROR;
|
459
|
+
goto error;
|
460
|
+
}
|
461
|
+
|
462
|
+
status = nc_set_nonblocking(conn->sd);
|
463
|
+
if (status != NC_OK) {
|
464
|
+
log_error("set nonblock on s %d for server '%.*s' failed: %s",
|
465
|
+
conn->sd, server->pname.len, server->pname.data,
|
466
|
+
strerror(errno));
|
467
|
+
goto error;
|
468
|
+
}
|
469
|
+
|
470
|
+
status = nc_set_tcpnodelay(conn->sd);
|
471
|
+
if (status != NC_OK) {
|
472
|
+
log_warn("set tcpnodelay on s %d for server '%.*s' failed, ignored: %s",
|
473
|
+
conn->sd, server->pname.len, server->pname.data,
|
474
|
+
strerror(errno));
|
475
|
+
}
|
476
|
+
|
477
|
+
status = event_add_conn(ctx->ep, conn);
|
478
|
+
if (status != NC_OK) {
|
479
|
+
log_error("event add conn e %d s %d for server '%.*s' failed: %s",
|
480
|
+
ctx->ep, conn->sd, server->pname.len, server->pname.data,
|
481
|
+
strerror(errno));
|
482
|
+
goto error;
|
483
|
+
}
|
484
|
+
|
485
|
+
ASSERT(!conn->connecting && !conn->connected);
|
486
|
+
|
487
|
+
status = connect(conn->sd, conn->addr, conn->addrlen);
|
488
|
+
if (status != NC_OK) {
|
489
|
+
if (errno == EINPROGRESS) {
|
490
|
+
conn->connecting = 1;
|
491
|
+
log_debug(LOG_DEBUG, "connecting on s %d to server '%.*s'",
|
492
|
+
conn->sd, server->pname.len, server->pname.data);
|
493
|
+
return NC_OK;
|
494
|
+
}
|
495
|
+
|
496
|
+
log_error("connect on s %d to server '%.*s' failed: %s", conn->sd,
|
497
|
+
server->pname.len, server->pname.data, strerror(errno));
|
498
|
+
|
499
|
+
goto error;
|
500
|
+
}
|
501
|
+
|
502
|
+
ASSERT(!conn->connecting);
|
503
|
+
conn->connected = 1;
|
504
|
+
log_debug(LOG_INFO, "connected on s %d to server '%.*s'", conn->sd,
|
505
|
+
server->pname.len, server->pname.data);
|
506
|
+
|
507
|
+
return NC_OK;
|
508
|
+
|
509
|
+
error:
|
510
|
+
conn->err = errno;
|
511
|
+
return status;
|
512
|
+
}
|
513
|
+
|
514
|
+
void
|
515
|
+
server_connected(struct context *ctx, struct conn *conn)
|
516
|
+
{
|
517
|
+
struct server *server = conn->owner;
|
518
|
+
|
519
|
+
ASSERT(!conn->client && !conn->proxy);
|
520
|
+
ASSERT(conn->connecting && !conn->connected);
|
521
|
+
|
522
|
+
stats_server_incr(ctx, server, server_connections);
|
523
|
+
|
524
|
+
conn->connecting = 0;
|
525
|
+
conn->connected = 1;
|
526
|
+
|
527
|
+
log_debug(LOG_INFO, "connected on s %d to server '%.*s'", conn->sd,
|
528
|
+
server->pname.len, server->pname.data);
|
529
|
+
}
|
530
|
+
|
531
|
+
void
|
532
|
+
server_ok(struct context *ctx, struct conn *conn)
|
533
|
+
{
|
534
|
+
struct server *server = conn->owner;
|
535
|
+
|
536
|
+
ASSERT(!conn->client && !conn->proxy);
|
537
|
+
ASSERT(conn->connected);
|
538
|
+
|
539
|
+
if (server->failure_count != 0) {
|
540
|
+
log_debug(LOG_VERB, "reset server '%.*s' failure count from %"PRIu32
|
541
|
+
" to 0", server->pname.len, server->pname.data,
|
542
|
+
server->failure_count);
|
543
|
+
server->failure_count = 0;
|
544
|
+
server->next_retry = 0LL;
|
545
|
+
}
|
546
|
+
}
|
547
|
+
|
548
|
+
static rstatus_t
|
549
|
+
server_pool_update(struct server_pool *pool)
|
550
|
+
{
|
551
|
+
rstatus_t status;
|
552
|
+
int64_t now;
|
553
|
+
uint32_t pnlive_server; /* prev # live server */
|
554
|
+
|
555
|
+
if (!pool->auto_eject_hosts) {
|
556
|
+
return NC_OK;
|
557
|
+
}
|
558
|
+
|
559
|
+
if (pool->next_rebuild == 0LL) {
|
560
|
+
return NC_OK;
|
561
|
+
}
|
562
|
+
|
563
|
+
now = nc_usec_now();
|
564
|
+
if (now < 0) {
|
565
|
+
return NC_ERROR;
|
566
|
+
}
|
567
|
+
|
568
|
+
if (now <= pool->next_rebuild) {
|
569
|
+
if (pool->nlive_server == 0) {
|
570
|
+
errno = ECONNREFUSED;
|
571
|
+
return NC_ERROR;
|
572
|
+
}
|
573
|
+
return NC_OK;
|
574
|
+
}
|
575
|
+
|
576
|
+
pnlive_server = pool->nlive_server;
|
577
|
+
|
578
|
+
status = server_pool_run(pool);
|
579
|
+
if (status != NC_OK) {
|
580
|
+
log_error("updating pool %"PRIu32" with dist %d failed: %s", pool->idx,
|
581
|
+
pool->dist_type, strerror(errno));
|
582
|
+
return status;
|
583
|
+
}
|
584
|
+
|
585
|
+
log_debug(LOG_INFO, "update pool %"PRIu32" '%.*s' to add %"PRIu32" servers",
|
586
|
+
pool->idx, pool->name.len, pool->name.data,
|
587
|
+
pool->nlive_server - pnlive_server);
|
588
|
+
|
589
|
+
|
590
|
+
return NC_OK;
|
591
|
+
}
|
592
|
+
|
593
|
+
static uint32_t
|
594
|
+
server_pool_hash(struct server_pool *pool, uint8_t *key, uint32_t keylen)
|
595
|
+
{
|
596
|
+
ASSERT(array_n(&pool->server) != 0);
|
597
|
+
|
598
|
+
if (array_n(&pool->server) == 1) {
|
599
|
+
return 0;
|
600
|
+
}
|
601
|
+
|
602
|
+
ASSERT(key != NULL && keylen != 0);
|
603
|
+
|
604
|
+
return pool->key_hash((char *)key, keylen);
|
605
|
+
}
|
606
|
+
|
607
|
+
static struct server *
|
608
|
+
server_pool_server(struct server_pool *pool, uint8_t *key, uint32_t keylen)
|
609
|
+
{
|
610
|
+
struct server *server;
|
611
|
+
uint32_t hash, idx;
|
612
|
+
|
613
|
+
ASSERT(array_n(&pool->server) != 0);
|
614
|
+
ASSERT(key != NULL && keylen != 0);
|
615
|
+
|
616
|
+
switch (pool->dist_type) {
|
617
|
+
case DIST_KETAMA:
|
618
|
+
hash = server_pool_hash(pool, key, keylen);
|
619
|
+
idx = ketama_dispatch(pool->continuum, pool->ncontinuum, hash);
|
620
|
+
break;
|
621
|
+
|
622
|
+
case DIST_MODULA:
|
623
|
+
hash = server_pool_hash(pool, key, keylen);
|
624
|
+
idx = modula_dispatch(pool->continuum, pool->ncontinuum, hash);
|
625
|
+
break;
|
626
|
+
|
627
|
+
case DIST_RANDOM:
|
628
|
+
idx = random_dispatch(pool->continuum, pool->ncontinuum, 0);
|
629
|
+
break;
|
630
|
+
|
631
|
+
default:
|
632
|
+
NOT_REACHED();
|
633
|
+
return NULL;
|
634
|
+
}
|
635
|
+
ASSERT(idx < array_n(&pool->server));
|
636
|
+
|
637
|
+
server = array_get(&pool->server, idx);
|
638
|
+
|
639
|
+
log_debug(LOG_VERB, "key '%.*s' on dist %d maps to server '%.*s'", keylen,
|
640
|
+
key, pool->dist_type, server->pname.len, server->pname.data);
|
641
|
+
|
642
|
+
return server;
|
643
|
+
}
|
644
|
+
|
645
|
+
struct conn *
|
646
|
+
server_pool_conn(struct context *ctx, struct server_pool *pool, uint8_t *key,
|
647
|
+
uint32_t keylen)
|
648
|
+
{
|
649
|
+
rstatus_t status;
|
650
|
+
struct server *server;
|
651
|
+
struct conn *conn;
|
652
|
+
|
653
|
+
status = server_pool_update(pool);
|
654
|
+
if (status != NC_OK) {
|
655
|
+
return NULL;
|
656
|
+
}
|
657
|
+
|
658
|
+
/* from a given {key, keylen} pick a server from pool */
|
659
|
+
server = server_pool_server(pool, key, keylen);
|
660
|
+
if (server == NULL) {
|
661
|
+
return NULL;
|
662
|
+
}
|
663
|
+
|
664
|
+
/* pick a connection to a given server */
|
665
|
+
conn = server_conn(server);
|
666
|
+
if (conn == NULL) {
|
667
|
+
return NULL;
|
668
|
+
}
|
669
|
+
|
670
|
+
status = server_connect(ctx, server, conn);
|
671
|
+
if (status != NC_OK) {
|
672
|
+
server_close(ctx, conn);
|
673
|
+
return NULL;
|
674
|
+
}
|
675
|
+
|
676
|
+
return conn;
|
677
|
+
}
|
678
|
+
|
679
|
+
static rstatus_t
|
680
|
+
server_pool_each_preconnect(void *elem, void *data)
|
681
|
+
{
|
682
|
+
rstatus_t status;
|
683
|
+
struct server_pool *sp = elem;
|
684
|
+
|
685
|
+
if (!sp->preconnect) {
|
686
|
+
return NC_OK;
|
687
|
+
}
|
688
|
+
|
689
|
+
status = array_each(&sp->server, server_each_preconnect, NULL);
|
690
|
+
if (status != NC_OK) {
|
691
|
+
return status;
|
692
|
+
}
|
693
|
+
|
694
|
+
return NC_OK;
|
695
|
+
}
|
696
|
+
|
697
|
+
rstatus_t
|
698
|
+
server_pool_preconnect(struct context *ctx)
|
699
|
+
{
|
700
|
+
rstatus_t status;
|
701
|
+
|
702
|
+
status = array_each(&ctx->pool, server_pool_each_preconnect, NULL);
|
703
|
+
if (status != NC_OK) {
|
704
|
+
return status;
|
705
|
+
}
|
706
|
+
|
707
|
+
return NC_OK;
|
708
|
+
}
|
709
|
+
|
710
|
+
static rstatus_t
|
711
|
+
server_pool_each_disconnect(void *elem, void *data)
|
712
|
+
{
|
713
|
+
rstatus_t status;
|
714
|
+
struct server_pool *sp = elem;
|
715
|
+
|
716
|
+
status = array_each(&sp->server, server_each_disconnect, NULL);
|
717
|
+
if (status != NC_OK) {
|
718
|
+
return status;
|
719
|
+
}
|
720
|
+
|
721
|
+
return NC_OK;
|
722
|
+
}
|
723
|
+
|
724
|
+
void
|
725
|
+
server_pool_disconnect(struct context *ctx)
|
726
|
+
{
|
727
|
+
array_each(&ctx->pool, server_pool_each_disconnect, NULL);
|
728
|
+
}
|
729
|
+
|
730
|
+
static rstatus_t
|
731
|
+
server_pool_each_set_owner(void *elem, void *data)
|
732
|
+
{
|
733
|
+
struct server_pool *sp = elem;
|
734
|
+
struct context *ctx = data;
|
735
|
+
|
736
|
+
sp->ctx = ctx;
|
737
|
+
|
738
|
+
return NC_OK;
|
739
|
+
}
|
740
|
+
|
741
|
+
rstatus_t
|
742
|
+
server_pool_run(struct server_pool *pool)
|
743
|
+
{
|
744
|
+
ASSERT(array_n(&pool->server) != 0);
|
745
|
+
|
746
|
+
switch (pool->dist_type) {
|
747
|
+
case DIST_KETAMA:
|
748
|
+
return ketama_update(pool);
|
749
|
+
|
750
|
+
case DIST_MODULA:
|
751
|
+
return modula_update(pool);
|
752
|
+
|
753
|
+
case DIST_RANDOM:
|
754
|
+
return random_update(pool);
|
755
|
+
|
756
|
+
default:
|
757
|
+
NOT_REACHED();
|
758
|
+
return NC_ERROR;
|
759
|
+
}
|
760
|
+
|
761
|
+
return NC_OK;
|
762
|
+
}
|
763
|
+
|
764
|
+
static rstatus_t
|
765
|
+
server_pool_each_run(void *elem, void *data)
|
766
|
+
{
|
767
|
+
return server_pool_run(elem);
|
768
|
+
}
|
769
|
+
|
770
|
+
rstatus_t
|
771
|
+
server_pool_init(struct array *server_pool, struct array *conf_pool,
|
772
|
+
struct context *ctx)
|
773
|
+
{
|
774
|
+
rstatus_t status;
|
775
|
+
uint32_t npool;
|
776
|
+
|
777
|
+
npool = array_n(conf_pool);
|
778
|
+
ASSERT(npool != 0);
|
779
|
+
ASSERT(array_n(server_pool) == 0);
|
780
|
+
|
781
|
+
status = array_init(server_pool, npool, sizeof(struct server_pool));
|
782
|
+
if (status != NC_OK) {
|
783
|
+
return status;
|
784
|
+
}
|
785
|
+
|
786
|
+
/* transform conf pool to server pool */
|
787
|
+
status = array_each(conf_pool, conf_pool_each_transform, server_pool);
|
788
|
+
if (status != NC_OK) {
|
789
|
+
server_pool_deinit(server_pool);
|
790
|
+
return status;
|
791
|
+
}
|
792
|
+
ASSERT(array_n(server_pool) == npool);
|
793
|
+
|
794
|
+
/* set ctx as the server pool owner */
|
795
|
+
status = array_each(server_pool, server_pool_each_set_owner, ctx);
|
796
|
+
if (status != NC_OK) {
|
797
|
+
server_pool_deinit(server_pool);
|
798
|
+
return status;
|
799
|
+
}
|
800
|
+
|
801
|
+
/* update server pool continuum */
|
802
|
+
status = array_each(server_pool, server_pool_each_run, NULL);
|
803
|
+
if (status != NC_OK) {
|
804
|
+
server_pool_deinit(server_pool);
|
805
|
+
return status;
|
806
|
+
}
|
807
|
+
|
808
|
+
log_debug(LOG_DEBUG, "init %"PRIu32" pools", npool);
|
809
|
+
|
810
|
+
return NC_OK;
|
811
|
+
}
|
812
|
+
|
813
|
+
void
|
814
|
+
server_pool_deinit(struct array *server_pool)
|
815
|
+
{
|
816
|
+
uint32_t i, npool;
|
817
|
+
|
818
|
+
for (i = 0, npool = array_n(server_pool); i < npool; i++) {
|
819
|
+
struct server_pool *sp;
|
820
|
+
|
821
|
+
sp = array_pop(server_pool);
|
822
|
+
ASSERT(sp->p_conn == NULL);
|
823
|
+
ASSERT(TAILQ_EMPTY(&sp->c_conn_q) && sp->nc_conn_q == 0);
|
824
|
+
|
825
|
+
if (sp->continuum != NULL) {
|
826
|
+
nc_free(sp->continuum);
|
827
|
+
sp->ncontinuum = 0;
|
828
|
+
sp->nserver_continuum = 0;
|
829
|
+
sp->nlive_server = 0;
|
830
|
+
}
|
831
|
+
|
832
|
+
server_deinit(&sp->server);
|
833
|
+
|
834
|
+
log_debug(LOG_DEBUG, "deinit pool %"PRIu32" '%.*s'", sp->idx,
|
835
|
+
sp->name.len, sp->name.data);
|
836
|
+
}
|
837
|
+
|
838
|
+
array_deinit(server_pool);
|
839
|
+
|
840
|
+
log_debug(LOG_DEBUG, "deinit %"PRIu32" pools", npool);
|
841
|
+
}
|