nutcracker 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/README.md +22 -0
  2. data/Rakefile +55 -0
  3. data/bin/nutcracker +2 -0
  4. data/ext/nutcracker/ChangeLog +66 -0
  5. data/ext/nutcracker/LICENSE +177 -0
  6. data/ext/nutcracker/Makefile.am +7 -0
  7. data/ext/nutcracker/Makefile.in +726 -0
  8. data/ext/nutcracker/NOTICE +124 -0
  9. data/ext/nutcracker/README.md +240 -0
  10. data/ext/nutcracker/aclocal.m4 +956 -0
  11. data/ext/nutcracker/conf/nutcracker.leaf.yml +10 -0
  12. data/ext/nutcracker/conf/nutcracker.root.yml +8 -0
  13. data/ext/nutcracker/conf/nutcracker.yml +67 -0
  14. data/ext/nutcracker/config.h.in +316 -0
  15. data/ext/nutcracker/config/config.guess +1561 -0
  16. data/ext/nutcracker/config/config.sub +1686 -0
  17. data/ext/nutcracker/config/depcomp +630 -0
  18. data/ext/nutcracker/config/install-sh +520 -0
  19. data/ext/nutcracker/config/ltmain.sh +8413 -0
  20. data/ext/nutcracker/config/missing +376 -0
  21. data/ext/nutcracker/configure +18862 -0
  22. data/ext/nutcracker/configure.ac +155 -0
  23. data/ext/nutcracker/contrib/Makefile.am +3 -0
  24. data/ext/nutcracker/contrib/Makefile.in +560 -0
  25. data/ext/nutcracker/contrib/yaml-0.1.4.tar.gz +0 -0
  26. data/ext/nutcracker/contrib/yaml-0.1.4/LICENSE +19 -0
  27. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.am +20 -0
  28. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.in +736 -0
  29. data/ext/nutcracker/contrib/yaml-0.1.4/README +27 -0
  30. data/ext/nutcracker/contrib/yaml-0.1.4/aclocal.m4 +956 -0
  31. data/ext/nutcracker/contrib/yaml-0.1.4/config.h.in +80 -0
  32. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.guess +1561 -0
  33. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.sub +1686 -0
  34. data/ext/nutcracker/contrib/yaml-0.1.4/config/depcomp +630 -0
  35. data/ext/nutcracker/contrib/yaml-0.1.4/config/install-sh +520 -0
  36. data/ext/nutcracker/contrib/yaml-0.1.4/config/ltmain.sh +8406 -0
  37. data/ext/nutcracker/contrib/yaml-0.1.4/config/missing +376 -0
  38. data/ext/nutcracker/contrib/yaml-0.1.4/configure +13085 -0
  39. data/ext/nutcracker/contrib/yaml-0.1.4/configure.ac +75 -0
  40. data/ext/nutcracker/contrib/yaml-0.1.4/doc/doxygen.cfg +222 -0
  41. data/ext/nutcracker/contrib/yaml-0.1.4/include/yaml.h +1971 -0
  42. data/ext/nutcracker/contrib/yaml-0.1.4/m4/libtool.m4 +7357 -0
  43. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltoptions.m4 +368 -0
  44. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltsugar.m4 +123 -0
  45. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltversion.m4 +23 -0
  46. data/ext/nutcracker/contrib/yaml-0.1.4/m4/lt~obsolete.m4 +92 -0
  47. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.am +4 -0
  48. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.in +484 -0
  49. data/ext/nutcracker/contrib/yaml-0.1.4/src/api.c +1392 -0
  50. data/ext/nutcracker/contrib/yaml-0.1.4/src/dumper.c +394 -0
  51. data/ext/nutcracker/contrib/yaml-0.1.4/src/emitter.c +2329 -0
  52. data/ext/nutcracker/contrib/yaml-0.1.4/src/loader.c +432 -0
  53. data/ext/nutcracker/contrib/yaml-0.1.4/src/parser.c +1374 -0
  54. data/ext/nutcracker/contrib/yaml-0.1.4/src/reader.c +465 -0
  55. data/ext/nutcracker/contrib/yaml-0.1.4/src/scanner.c +3570 -0
  56. data/ext/nutcracker/contrib/yaml-0.1.4/src/writer.c +141 -0
  57. data/ext/nutcracker/contrib/yaml-0.1.4/src/yaml_private.h +640 -0
  58. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.am +8 -0
  59. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.in +675 -0
  60. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor-alt.c +800 -0
  61. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor.c +1130 -0
  62. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter-alt.c +217 -0
  63. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter.c +202 -0
  64. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-dumper.c +311 -0
  65. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-emitter.c +327 -0
  66. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-loader.c +63 -0
  67. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-parser.c +63 -0
  68. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-scanner.c +63 -0
  69. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-reader.c +354 -0
  70. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-version.c +29 -0
  71. data/ext/nutcracker/extconf.rb +5 -0
  72. data/ext/nutcracker/m4/libtool.m4 +7376 -0
  73. data/ext/nutcracker/m4/ltoptions.m4 +368 -0
  74. data/ext/nutcracker/m4/ltsugar.m4 +123 -0
  75. data/ext/nutcracker/m4/ltversion.m4 +23 -0
  76. data/ext/nutcracker/m4/lt~obsolete.m4 +92 -0
  77. data/ext/nutcracker/notes/c-styleguide.txt +425 -0
  78. data/ext/nutcracker/notes/debug.txt +96 -0
  79. data/ext/nutcracker/notes/memcache.txt +123 -0
  80. data/ext/nutcracker/notes/recommendation.md +118 -0
  81. data/ext/nutcracker/notes/redis.md +415 -0
  82. data/ext/nutcracker/notes/socket.txt +131 -0
  83. data/ext/nutcracker/scripts/multi_get.sh +26 -0
  84. data/ext/nutcracker/scripts/nutcracker.init +73 -0
  85. data/ext/nutcracker/scripts/nutcracker.spec +52 -0
  86. data/ext/nutcracker/scripts/pipelined_read.sh +23 -0
  87. data/ext/nutcracker/scripts/pipelined_write.sh +29 -0
  88. data/ext/nutcracker/scripts/populate_memcached.sh +24 -0
  89. data/ext/nutcracker/scripts/redis-check.py +23 -0
  90. data/ext/nutcracker/scripts/redis-check.sh +564 -0
  91. data/ext/nutcracker/src/Makefile.am +46 -0
  92. data/ext/nutcracker/src/Makefile.in +726 -0
  93. data/ext/nutcracker/src/hashkit/Makefile.am +22 -0
  94. data/ext/nutcracker/src/hashkit/Makefile.in +501 -0
  95. data/ext/nutcracker/src/hashkit/nc_crc32.c +105 -0
  96. data/ext/nutcracker/src/hashkit/nc_fnv.c +82 -0
  97. data/ext/nutcracker/src/hashkit/nc_hashkit.h +74 -0
  98. data/ext/nutcracker/src/hashkit/nc_hsieh.c +93 -0
  99. data/ext/nutcracker/src/hashkit/nc_jenkins.c +230 -0
  100. data/ext/nutcracker/src/hashkit/nc_ketama.c +240 -0
  101. data/ext/nutcracker/src/hashkit/nc_md5.c +379 -0
  102. data/ext/nutcracker/src/hashkit/nc_modula.c +144 -0
  103. data/ext/nutcracker/src/hashkit/nc_murmur.c +99 -0
  104. data/ext/nutcracker/src/hashkit/nc_one_at_a_time.c +51 -0
  105. data/ext/nutcracker/src/hashkit/nc_random.c +146 -0
  106. data/ext/nutcracker/src/nc.c +573 -0
  107. data/ext/nutcracker/src/nc_array.c +204 -0
  108. data/ext/nutcracker/src/nc_array.h +73 -0
  109. data/ext/nutcracker/src/nc_client.c +189 -0
  110. data/ext/nutcracker/src/nc_client.h +28 -0
  111. data/ext/nutcracker/src/nc_conf.c +1766 -0
  112. data/ext/nutcracker/src/nc_conf.h +134 -0
  113. data/ext/nutcracker/src/nc_connection.c +392 -0
  114. data/ext/nutcracker/src/nc_connection.h +99 -0
  115. data/ext/nutcracker/src/nc_core.c +334 -0
  116. data/ext/nutcracker/src/nc_core.h +131 -0
  117. data/ext/nutcracker/src/nc_event.c +214 -0
  118. data/ext/nutcracker/src/nc_event.h +39 -0
  119. data/ext/nutcracker/src/nc_log.c +254 -0
  120. data/ext/nutcracker/src/nc_log.h +120 -0
  121. data/ext/nutcracker/src/nc_mbuf.c +285 -0
  122. data/ext/nutcracker/src/nc_mbuf.h +67 -0
  123. data/ext/nutcracker/src/nc_message.c +828 -0
  124. data/ext/nutcracker/src/nc_message.h +253 -0
  125. data/ext/nutcracker/src/nc_proxy.c +359 -0
  126. data/ext/nutcracker/src/nc_proxy.h +34 -0
  127. data/ext/nutcracker/src/nc_queue.h +788 -0
  128. data/ext/nutcracker/src/nc_rbtree.c +348 -0
  129. data/ext/nutcracker/src/nc_rbtree.h +47 -0
  130. data/ext/nutcracker/src/nc_request.c +588 -0
  131. data/ext/nutcracker/src/nc_response.c +332 -0
  132. data/ext/nutcracker/src/nc_server.c +841 -0
  133. data/ext/nutcracker/src/nc_server.h +143 -0
  134. data/ext/nutcracker/src/nc_signal.c +131 -0
  135. data/ext/nutcracker/src/nc_signal.h +34 -0
  136. data/ext/nutcracker/src/nc_stats.c +1188 -0
  137. data/ext/nutcracker/src/nc_stats.h +206 -0
  138. data/ext/nutcracker/src/nc_string.c +109 -0
  139. data/ext/nutcracker/src/nc_string.h +112 -0
  140. data/ext/nutcracker/src/nc_util.c +619 -0
  141. data/ext/nutcracker/src/nc_util.h +214 -0
  142. data/ext/nutcracker/src/proto/Makefile.am +14 -0
  143. data/ext/nutcracker/src/proto/Makefile.in +482 -0
  144. data/ext/nutcracker/src/proto/nc_memcache.c +1306 -0
  145. data/ext/nutcracker/src/proto/nc_proto.h +155 -0
  146. data/ext/nutcracker/src/proto/nc_redis.c +2102 -0
  147. data/lib/nutcracker.rb +7 -0
  148. data/lib/nutcracker/version.rb +3 -0
  149. metadata +194 -0
@@ -0,0 +1,143 @@
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
+ #ifndef _NC_SERVER_H_
19
+ #define _NC_SERVER_H_
20
+
21
+ #include <nc_core.h>
22
+
23
+ /*
24
+ * server_pool is a collection of servers and their continuum. Each
25
+ * server_pool is the owner of a single proxy connection and one or
26
+ * more client connections. server_pool itself is owned by the current
27
+ * context.
28
+ *
29
+ * Each server is the owner of one or more server connections. server
30
+ * itself is owned by the server_pool.
31
+ *
32
+ * +-------------+
33
+ * | |<---------------------+
34
+ * | |<------------+ |
35
+ * | | +-------+--+-----+----+--------------+
36
+ * | pool 0 |+--->| | | |
37
+ * | | | server 0 | server 1 | ... ... |
38
+ * | | | | | |--+
39
+ * | | +----------+----------+--------------+ |
40
+ * +-------------+ //
41
+ * | |
42
+ * | |
43
+ * | |
44
+ * | pool 1 |
45
+ * | |
46
+ * | |
47
+ * | |
48
+ * +-------------+
49
+ * | |
50
+ * | |
51
+ * . .
52
+ * . ... .
53
+ * . .
54
+ * | |
55
+ * | |
56
+ * +-------------+
57
+ * |
58
+ * |
59
+ * //
60
+ */
61
+
62
+ typedef uint32_t (*hash_t)(const char *, size_t);
63
+
64
+ struct continuum {
65
+ uint32_t index; /* server index */
66
+ uint32_t value; /* hash value */
67
+ };
68
+
69
+ struct server {
70
+ uint32_t idx; /* server index */
71
+ struct server_pool *owner; /* owner pool */
72
+
73
+ struct string pname; /* name:port:weight (ref in conf_server) */
74
+ struct string name; /* name (ref in conf_server) */
75
+ uint16_t port; /* port */
76
+ uint32_t weight; /* weight */
77
+ int family; /* socket family */
78
+ socklen_t addrlen; /* socket length */
79
+ struct sockaddr *addr; /* socket address (ref in conf_server) */
80
+
81
+ uint32_t ns_conn_q; /* # server connection */
82
+ struct conn_tqh s_conn_q; /* server connection q */
83
+
84
+ int64_t next_retry; /* next retry time in usec */
85
+ uint32_t failure_count; /* # consecutive failures */
86
+ };
87
+
88
+ struct server_pool {
89
+ uint32_t idx; /* pool index */
90
+ struct context *ctx; /* owner context */
91
+
92
+ struct conn *p_conn; /* proxy connection (listener) */
93
+ uint32_t nc_conn_q; /* # client connection */
94
+ struct conn_tqh c_conn_q; /* client connection q */
95
+
96
+ struct array server; /* server[] */
97
+ uint32_t ncontinuum; /* # continuum points */
98
+ uint32_t nserver_continuum; /* # servers - live and dead on continuum (const) */
99
+ struct continuum *continuum; /* continuum */
100
+ uint32_t nlive_server; /* # live server */
101
+ int64_t next_rebuild; /* next distribution rebuild time in usec */
102
+
103
+ struct string name; /* pool name (ref in conf_pool) */
104
+ struct string addrstr; /* pool address (ref in conf_pool) */
105
+ uint16_t port; /* port */
106
+ int family; /* socket family */
107
+ socklen_t addrlen; /* socket length */
108
+ struct sockaddr *addr; /* socket address (ref in conf_pool) */
109
+ int dist_type; /* distribution type (dist_type_t) */
110
+ int key_hash_type; /* key hash type (hash_type_t) */
111
+ hash_t key_hash; /* key hasher */
112
+ struct string hash_tag; /* key hash tag (ref in conf_pool) */
113
+ int timeout; /* timeout in msec */
114
+ int backlog; /* listen backlog */
115
+ uint32_t client_connections; /* maximum # client connection */
116
+ uint32_t server_connections; /* maximum # server connection */
117
+ int64_t server_retry_timeout; /* server retry timeout in usec */
118
+ uint32_t server_failure_limit; /* server failure limit */
119
+ unsigned auto_eject_hosts:1; /* auto_eject_hosts? */
120
+ unsigned preconnect:1; /* preconnect? */
121
+ unsigned redis:1; /* redis? */
122
+ };
123
+
124
+ void server_ref(struct conn *conn, void *owner);
125
+ void server_unref(struct conn *conn);
126
+ int server_timeout(struct conn *conn);
127
+ bool server_active(struct conn *conn);
128
+ rstatus_t server_init(struct array *server, struct array *conf_server, struct server_pool *sp);
129
+ void server_deinit(struct array *server);
130
+ struct conn *server_conn(struct server *server);
131
+ rstatus_t server_connect(struct context *ctx, struct server *server, struct conn *conn);
132
+ void server_close(struct context *ctx, struct conn *conn);
133
+ void server_connected(struct context *ctx, struct conn *conn);
134
+ void server_ok(struct context *ctx, struct conn *conn);
135
+
136
+ struct conn *server_pool_conn(struct context *ctx, struct server_pool *pool, uint8_t *key, uint32_t keylen);
137
+ rstatus_t server_pool_run(struct server_pool *pool);
138
+ rstatus_t server_pool_preconnect(struct context *ctx);
139
+ void server_pool_disconnect(struct context *ctx);
140
+ rstatus_t server_pool_init(struct array *server_pool, struct array *conf_pool, struct context *ctx);
141
+ void server_pool_deinit(struct array *server_pool);
142
+
143
+ #endif
@@ -0,0 +1,131 @@
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 <signal.h>
20
+
21
+ #include <nc_core.h>
22
+ #include <nc_signal.h>
23
+
24
+ static struct signal signals[] = {
25
+ { SIGUSR1, "SIGUSR1", 0, signal_handler },
26
+ { SIGUSR2, "SIGUSR2", 0, signal_handler },
27
+ { SIGTTIN, "SIGTTIN", 0, signal_handler },
28
+ { SIGTTOU, "SIGTTOU", 0, signal_handler },
29
+ { SIGHUP, "SIGHUP", 0, signal_handler },
30
+ { SIGINT, "SIGINT", 0, signal_handler },
31
+ { SIGSEGV, "SIGSEGV", (int)SA_RESETHAND, signal_handler },
32
+ { SIGPIPE, "SIGPIPE", 0, SIG_IGN },
33
+ { 0, NULL, 0, NULL }
34
+ };
35
+
36
+ rstatus_t
37
+ signal_init(void)
38
+ {
39
+ struct signal *sig;
40
+
41
+ for (sig = signals; sig->signo != 0; sig++) {
42
+ rstatus_t status;
43
+ struct sigaction sa;
44
+
45
+ memset(&sa, 0, sizeof(sa));
46
+ sa.sa_handler = sig->handler;
47
+ sa.sa_flags = sig->flags;
48
+ sigemptyset(&sa.sa_mask);
49
+
50
+ status = sigaction(sig->signo, &sa, NULL);
51
+ if (status < 0) {
52
+ log_error("sigaction(%s) failed: %s", sig->signame,
53
+ strerror(errno));
54
+ return NC_ERROR;
55
+ }
56
+ }
57
+
58
+ return NC_OK;
59
+ }
60
+
61
+ void
62
+ signal_deinit(void)
63
+ {
64
+ }
65
+
66
+ void
67
+ signal_handler(int signo)
68
+ {
69
+ struct signal *sig;
70
+ void (*action)(void);
71
+ char *actionstr;
72
+ bool done;
73
+
74
+ for (sig = signals; sig->signo != 0; sig++) {
75
+ if (sig->signo == signo) {
76
+ break;
77
+ }
78
+ }
79
+ ASSERT(sig->signo != 0);
80
+
81
+ actionstr = "";
82
+ action = NULL;
83
+ done = false;
84
+
85
+ switch (signo) {
86
+ case SIGUSR1:
87
+ break;
88
+
89
+ case SIGUSR2:
90
+ break;
91
+
92
+ case SIGTTIN:
93
+ actionstr = ", up logging level";
94
+ action = log_level_up;
95
+ break;
96
+
97
+ case SIGTTOU:
98
+ actionstr = ", down logging level";
99
+ action = log_level_down;
100
+ break;
101
+
102
+ case SIGHUP:
103
+ actionstr = ", reopening log file";
104
+ action = log_reopen;
105
+ break;
106
+
107
+ case SIGINT:
108
+ done = true;
109
+ actionstr = ", exiting";
110
+ break;
111
+
112
+ case SIGSEGV:
113
+ nc_stacktrace(1);
114
+ actionstr = ", core dumping";
115
+ raise(SIGSEGV);
116
+ break;
117
+
118
+ default:
119
+ NOT_REACHED();
120
+ }
121
+
122
+ loga("signal %d (%s) received%s", signo, sig->signame, actionstr);
123
+
124
+ if (action != NULL) {
125
+ action();
126
+ }
127
+
128
+ if (done) {
129
+ exit(1);
130
+ }
131
+ }
@@ -0,0 +1,34 @@
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
+ #ifndef _NC_SIGNAL_H_
19
+ #define _NC_SIGNAL_H_
20
+
21
+ #include <nc_core.h>
22
+
23
+ struct signal {
24
+ int signo;
25
+ char *signame;
26
+ int flags;
27
+ void (*handler)(int signo);
28
+ };
29
+
30
+ rstatus_t signal_init(void);
31
+ void signal_deinit(void);
32
+ void signal_handler(int signo);
33
+
34
+ #endif
@@ -0,0 +1,1188 @@
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 <stdio.h>
19
+ #include <stdlib.h>
20
+ #include <unistd.h>
21
+
22
+ #include <sys/types.h>
23
+ #include <sys/socket.h>
24
+ #include <sys/epoll.h>
25
+ #include <netinet/in.h>
26
+
27
+ #include <nc_core.h>
28
+ #include <nc_server.h>
29
+
30
+ struct stats_desc {
31
+ char *name; /* stats name */
32
+ char *desc; /* stats description */
33
+ };
34
+
35
+ #define DEFINE_ACTION(_name, _type, _desc) { .type = _type, .name = string(#_name) },
36
+ static struct stats_metric stats_pool_codec[] = {
37
+ STATS_POOL_CODEC( DEFINE_ACTION )
38
+ };
39
+
40
+ static struct stats_metric stats_server_codec[] = {
41
+ STATS_SERVER_CODEC( DEFINE_ACTION )
42
+ };
43
+ #undef DEFINE_ACTION
44
+
45
+ #define DEFINE_ACTION(_name, _type, _desc) { .name = #_name, .desc = _desc },
46
+ static struct stats_desc stats_pool_desc[] = {
47
+ STATS_POOL_CODEC( DEFINE_ACTION )
48
+ };
49
+
50
+ static struct stats_desc stats_server_desc[] = {
51
+ STATS_SERVER_CODEC( DEFINE_ACTION )
52
+ };
53
+ #undef DEFINE_ACTION
54
+
55
+ void
56
+ stats_describe(void)
57
+ {
58
+ uint32_t i;
59
+
60
+ log_stderr("pool stats:");
61
+ for (i = 0; i < NELEMS(stats_pool_desc); i++) {
62
+ log_stderr(" %-20s\"%s\"", stats_pool_desc[i].name,
63
+ stats_pool_desc[i].desc);
64
+ }
65
+
66
+ log_stderr("");
67
+
68
+ log_stderr("server stats:");
69
+ for (i = 0; i < NELEMS(stats_server_desc); i++) {
70
+ log_stderr(" %-20s\"%s\"", stats_server_desc[i].name,
71
+ stats_server_desc[i].desc);
72
+ }
73
+ }
74
+
75
+ static void
76
+ stats_metric_init(struct stats_metric *stm)
77
+ {
78
+ switch (stm->type) {
79
+ case STATS_COUNTER:
80
+ stm->value.counter = 0LL;
81
+ break;
82
+
83
+ case STATS_GAUGE:
84
+ stm->value.counter = 0LL;
85
+ break;
86
+
87
+ case STATS_TIMESTAMP:
88
+ stm->value.timestamp = 0LL;
89
+ break;
90
+
91
+ default:
92
+ NOT_REACHED();
93
+ }
94
+ }
95
+
96
+ static void
97
+ stats_metric_reset(struct array *stats_metric)
98
+ {
99
+ uint32_t i, nmetric;
100
+
101
+ nmetric = array_n(stats_metric);
102
+ ASSERT(nmetric == STATS_POOL_NFIELD || nmetric == STATS_SERVER_NFIELD);
103
+
104
+ for (i = 0; i < nmetric; i++) {
105
+ struct stats_metric *stm = array_get(stats_metric, i);
106
+
107
+ stats_metric_init(stm);
108
+ }
109
+ }
110
+
111
+ static rstatus_t
112
+ stats_pool_metric_init(struct array *stats_metric)
113
+ {
114
+ rstatus_t status;
115
+ uint32_t i, nfield = STATS_POOL_NFIELD;
116
+
117
+ status = array_init(stats_metric, nfield, sizeof(struct stats_metric));
118
+ if (status != NC_OK) {
119
+ return status;
120
+ }
121
+
122
+ for (i = 0; i < nfield; i++) {
123
+ struct stats_metric *stm = array_push(stats_metric);
124
+
125
+ /* initialize from pool codec first */
126
+ *stm = stats_pool_codec[i];
127
+
128
+ /* initialize individual metric */
129
+ stats_metric_init(stm);
130
+ }
131
+
132
+ return NC_OK;
133
+ }
134
+
135
+ static rstatus_t
136
+ stats_server_metric_init(struct stats_server *sts)
137
+ {
138
+ rstatus_t status;
139
+ uint32_t i, nfield = STATS_SERVER_NFIELD;
140
+
141
+ status = array_init(&sts->metric, nfield, sizeof(struct stats_metric));
142
+ if (status != NC_OK) {
143
+ return status;
144
+ }
145
+
146
+ for (i = 0; i < nfield; i++) {
147
+ struct stats_metric *stm = array_push(&sts->metric);
148
+
149
+ /* initialize from server codec first */
150
+ *stm = stats_server_codec[i];
151
+
152
+ /* initialize individual metric */
153
+ stats_metric_init(stm);
154
+ }
155
+
156
+ return NC_OK;
157
+ }
158
+
159
+ static void
160
+ stats_metric_deinit(struct array *metric)
161
+ {
162
+ uint32_t i, nmetric;
163
+
164
+ nmetric = array_n(metric);
165
+ for (i = 0; i < nmetric; i++) {
166
+ array_pop(metric);
167
+ }
168
+ array_deinit(metric);
169
+ }
170
+
171
+ static rstatus_t
172
+ stats_server_init(struct stats_server *sts, struct server *s)
173
+ {
174
+ rstatus_t status;
175
+
176
+ sts->name = s->name;
177
+ array_null(&sts->metric);
178
+
179
+ status = stats_server_metric_init(sts);
180
+ if (status != NC_OK) {
181
+ return status;
182
+ }
183
+
184
+ log_debug(LOG_VVVERB, "init stats server '%.*s' with %"PRIu32" metric",
185
+ sts->name.len, sts->name.data, array_n(&sts->metric));
186
+
187
+ return NC_OK;
188
+
189
+ }
190
+
191
+ static rstatus_t
192
+ stats_server_map(struct array *stats_server, struct array *server)
193
+ {
194
+ rstatus_t status;
195
+ uint32_t i, nserver;
196
+
197
+ nserver = array_n(server);
198
+ ASSERT(nserver != 0);
199
+
200
+ status = array_init(stats_server, nserver, sizeof(struct stats_server));
201
+ if (status != NC_OK) {
202
+ return status;
203
+ }
204
+
205
+ for (i = 0; i < nserver; i++) {
206
+ struct server *s = array_get(server, i);
207
+ struct stats_server *sts = array_push(stats_server);
208
+
209
+ status = stats_server_init(sts, s);
210
+ if (status != NC_OK) {
211
+ return status;
212
+ }
213
+ }
214
+
215
+ log_debug(LOG_VVVERB, "map %"PRIu32" stats servers", nserver);
216
+
217
+ return NC_OK;
218
+ }
219
+
220
+ static void
221
+ stats_server_unmap(struct array *stats_server)
222
+ {
223
+ uint32_t i, nserver;
224
+
225
+ nserver = array_n(stats_server);
226
+
227
+ for (i = 0; i < nserver; i++) {
228
+ struct stats_server *sts = array_pop(stats_server);
229
+ stats_metric_deinit(&sts->metric);
230
+ }
231
+ array_deinit(stats_server);
232
+
233
+ log_debug(LOG_VVVERB, "unmap %"PRIu32" stats servers", nserver);
234
+ }
235
+
236
+ static rstatus_t
237
+ stats_pool_init(struct stats_pool *stp, struct server_pool *sp)
238
+ {
239
+ rstatus_t status;
240
+
241
+ stp->name = sp->name;
242
+ array_null(&stp->metric);
243
+ array_null(&stp->server);
244
+
245
+ status = stats_pool_metric_init(&stp->metric);
246
+ if (status != NC_OK) {
247
+ return status;
248
+ }
249
+
250
+ status = stats_server_map(&stp->server, &sp->server);
251
+ if (status != NC_OK) {
252
+ stats_metric_deinit(&stp->metric);
253
+ return status;
254
+ }
255
+
256
+ log_debug(LOG_VVVERB, "init stats pool '%.*s' with %"PRIu32" metric and "
257
+ "%"PRIu32" server", stp->name.len, stp->name.data,
258
+ array_n(&stp->metric), array_n(&stp->metric));
259
+
260
+ return NC_OK;
261
+ }
262
+
263
+ static void
264
+ stats_pool_reset(struct array *stats_pool)
265
+ {
266
+ uint32_t i, npool;
267
+
268
+ npool = array_n(stats_pool);
269
+
270
+ for (i = 0; i < npool; i++) {
271
+ struct stats_pool *stp = array_get(stats_pool, i);
272
+ uint32_t j, nserver;
273
+
274
+ stats_metric_reset(&stp->metric);
275
+
276
+ nserver = array_n(&stp->server);
277
+ for (j = 0; j < nserver; j++) {
278
+ struct stats_server *sts = array_get(&stp->server, j);
279
+ stats_metric_reset(&sts->metric);
280
+ }
281
+ }
282
+ }
283
+
284
+ static rstatus_t
285
+ stats_pool_map(struct array *stats_pool, struct array *server_pool)
286
+ {
287
+ rstatus_t status;
288
+ uint32_t i, npool;
289
+
290
+ npool = array_n(server_pool);
291
+ ASSERT(npool != 0);
292
+
293
+ status = array_init(stats_pool, npool, sizeof(struct stats_pool));
294
+ if (status != NC_OK) {
295
+ return status;
296
+ }
297
+
298
+ for (i = 0; i < npool; i++) {
299
+ struct server_pool *sp = array_get(server_pool, i);
300
+ struct stats_pool *stp = array_push(stats_pool);
301
+
302
+ status = stats_pool_init(stp, sp);
303
+ if (status != NC_OK) {
304
+ return status;
305
+ }
306
+ }
307
+
308
+ log_debug(LOG_VVVERB, "map %"PRIu32" stats pools", npool);
309
+
310
+ return NC_OK;
311
+ }
312
+
313
+ static void
314
+ stats_pool_unmap(struct array *stats_pool)
315
+ {
316
+ uint32_t i, npool;
317
+
318
+ npool = array_n(stats_pool);
319
+
320
+ for (i = 0; i < npool; i++) {
321
+ struct stats_pool *stp = array_pop(stats_pool);
322
+ stats_metric_deinit(&stp->metric);
323
+ stats_server_unmap(&stp->server);
324
+ }
325
+ array_deinit(stats_pool);
326
+
327
+ log_debug(LOG_VVVERB, "unmap %"PRIu32" stats pool", npool);
328
+ }
329
+
330
+ static rstatus_t
331
+ stats_create_buf(struct stats *st)
332
+ {
333
+ uint32_t int64_max_digits = 20; /* INT64_MAX = 9223372036854775807 */
334
+ uint32_t key_value_extra = 8; /* "key": "value", */
335
+ uint32_t pool_extra = 8; /* '"pool_name": { ' + ' }' */
336
+ uint32_t server_extra = 8; /* '"server_name": { ' + ' }' */
337
+ size_t size = 0;
338
+ uint32_t i;
339
+
340
+ ASSERT(st->buf.data == NULL && st->buf.size == 0);
341
+
342
+ /* header */
343
+ size += 1;
344
+
345
+ size += st->service_str.len;
346
+ size += st->service.len;
347
+ size += key_value_extra;
348
+
349
+ size += st->source_str.len;
350
+ size += st->source.len;
351
+ size += key_value_extra;
352
+
353
+ size += st->version_str.len;
354
+ size += st->version.len;
355
+ size += key_value_extra;
356
+
357
+ size += st->uptime_str.len;
358
+ size += int64_max_digits;
359
+ size += key_value_extra;
360
+
361
+ size += st->timestamp_str.len;
362
+ size += int64_max_digits;
363
+ size += key_value_extra;
364
+
365
+ /* server pools */
366
+ for (i = 0; i < array_n(&st->sum); i++) {
367
+ struct stats_pool *stp = array_get(&st->sum, i);
368
+ uint32_t j;
369
+
370
+ size += stp->name.len;
371
+ size += pool_extra;
372
+
373
+ for (j = 0; j < array_n(&stp->metric); j++) {
374
+ struct stats_metric *stm = array_get(&stp->metric, j);
375
+
376
+ size += stm->name.len;
377
+ size += int64_max_digits;
378
+ size += key_value_extra;
379
+ }
380
+
381
+ /* servers per pool */
382
+ for (j = 0; j < array_n(&stp->server); j++) {
383
+ struct stats_server *sts = array_get(&stp->server, j);
384
+ uint32_t k;
385
+
386
+ size += sts->name.len;
387
+ size += server_extra;
388
+
389
+ for (k = 0; k < array_n(&sts->metric); k++) {
390
+ struct stats_metric *stm = array_get(&sts->metric, k);
391
+
392
+ size += stm->name.len;
393
+ size += int64_max_digits;
394
+ size += key_value_extra;
395
+ }
396
+ }
397
+ }
398
+
399
+ /* footer */
400
+ size += 2;
401
+
402
+ size = NC_ALIGN(size, NC_ALIGNMENT);
403
+
404
+ st->buf.data = nc_alloc(size);
405
+ if (st->buf.data == NULL) {
406
+ log_error("create stats buffer of size %zu failed: %s", size,
407
+ strerror(errno));
408
+ return NC_ENOMEM;
409
+ }
410
+ st->buf.size = size;
411
+
412
+ log_debug(LOG_DEBUG, "stats buffer size %zu", size);
413
+
414
+ return NC_OK;
415
+ }
416
+
417
+ static void
418
+ stats_destroy_buf(struct stats *st)
419
+ {
420
+ if (st->buf.size != 0) {
421
+ ASSERT(st->buf.data != NULL);
422
+ nc_free(st->buf.data);
423
+ st->buf.size = 0;
424
+ }
425
+ }
426
+
427
+ static rstatus_t
428
+ stats_add_string(struct stats *st, struct string *key, struct string *val)
429
+ {
430
+ struct stats_buffer *buf;
431
+ uint8_t *pos;
432
+ size_t room;
433
+ int n;
434
+
435
+ buf = &st->buf;
436
+ pos = buf->data + buf->len;
437
+ room = buf->size - buf->len - 1;
438
+
439
+ n = nc_snprintf(pos, room, "\"%.*s\":\"%.*s\", ", key->len, key->data,
440
+ val->len, val->data);
441
+ if (n < 0 || n >= (int)room) {
442
+ return NC_ERROR;
443
+ }
444
+
445
+ buf->len += (size_t)n;
446
+
447
+ return NC_OK;
448
+ }
449
+
450
+ static rstatus_t
451
+ stats_add_num(struct stats *st, struct string *key, int64_t val)
452
+ {
453
+ struct stats_buffer *buf;
454
+ uint8_t *pos;
455
+ size_t room;
456
+ int n;
457
+
458
+ buf = &st->buf;
459
+ pos = buf->data + buf->len;
460
+ room = buf->size - buf->len - 1;
461
+
462
+ n = nc_snprintf(pos, room, "\"%.*s\":%"PRId64", ", key->len, key->data,
463
+ val);
464
+ if (n < 0 || n >= (int)room) {
465
+ return NC_ERROR;
466
+ }
467
+
468
+ buf->len += (size_t)n;
469
+
470
+ return NC_OK;
471
+ }
472
+
473
+ static rstatus_t
474
+ stats_add_header(struct stats *st)
475
+ {
476
+ rstatus_t status;
477
+ struct stats_buffer *buf;
478
+ int64_t cur_ts, uptime;
479
+
480
+ buf = &st->buf;
481
+ buf->data[0] = '{';
482
+ buf->len = 1;
483
+
484
+ cur_ts = (int64_t)time(NULL);
485
+ uptime = cur_ts - st->start_ts;
486
+
487
+ status = stats_add_string(st, &st->service_str, &st->service);
488
+ if (status != NC_OK) {
489
+ return status;
490
+ }
491
+
492
+ status = stats_add_string(st, &st->source_str, &st->source);
493
+ if (status != NC_OK) {
494
+ return status;
495
+ }
496
+
497
+ status = stats_add_string(st, &st->version_str, &st->version);
498
+ if (status != NC_OK) {
499
+ return status;
500
+ }
501
+
502
+ status = stats_add_num(st, &st->uptime_str, uptime);
503
+ if (status != NC_OK) {
504
+ return status;
505
+ }
506
+
507
+ status = stats_add_num(st, &st->timestamp_str, cur_ts);
508
+ if (status != NC_OK) {
509
+ return status;
510
+ }
511
+
512
+ return NC_OK;
513
+ }
514
+
515
+ static rstatus_t
516
+ stats_add_footer(struct stats *st)
517
+ {
518
+ struct stats_buffer *buf;
519
+ uint8_t *pos;
520
+
521
+ buf = &st->buf;
522
+
523
+ if (buf->len == buf->size) {
524
+ return NC_ERROR;
525
+ }
526
+
527
+ /* overwrite the last byte and add a new byte */
528
+ pos = buf->data + buf->len - 1;
529
+ pos[0] = '}';
530
+ pos[1] = '\n';
531
+ buf->len += 1;
532
+
533
+ return NC_OK;
534
+ }
535
+
536
+ static rstatus_t
537
+ stats_begin_nesting(struct stats *st, struct string *key)
538
+ {
539
+ struct stats_buffer *buf;
540
+ uint8_t *pos;
541
+ size_t room;
542
+ int n;
543
+
544
+ buf = &st->buf;
545
+ pos = buf->data + buf->len;
546
+ room = buf->size - buf->len - 1;
547
+
548
+ n = nc_snprintf(pos, room, "\"%.*s\": {", key->len, key->data);
549
+ if (n < 0 || n >= (int)room) {
550
+ return NC_ERROR;
551
+ }
552
+
553
+ buf->len += (size_t)n;
554
+
555
+ return NC_OK;
556
+ }
557
+
558
+ static rstatus_t
559
+ stats_end_nesting(struct stats *st)
560
+ {
561
+ struct stats_buffer *buf;
562
+ uint8_t *pos;
563
+
564
+ buf = &st->buf;
565
+ pos = buf->data + buf->len;
566
+
567
+ pos -= 2; /* go back by 2 bytes */
568
+
569
+ switch (pos[0]) {
570
+ case ',':
571
+ /* overwrite last two bytes; len remains unchanged */
572
+ ASSERT(pos[1] == ' ');
573
+ pos[0] = '}';
574
+ pos[1] = ',';
575
+ break;
576
+
577
+ case '}':
578
+ if (buf->len == buf->size) {
579
+ return NC_ERROR;
580
+ }
581
+ /* overwrite the last byte and add a new byte */
582
+ ASSERT(pos[1] == ',');
583
+ pos[1] = '}';
584
+ pos[2] = ',';
585
+ buf->len += 1;
586
+ break;
587
+
588
+ default:
589
+ NOT_REACHED();
590
+ }
591
+
592
+ return NC_OK;
593
+ }
594
+
595
+ static rstatus_t
596
+ stats_copy_metric(struct stats *st, struct array *metric)
597
+ {
598
+ rstatus_t status;
599
+ uint32_t i;
600
+
601
+ for (i = 0; i < array_n(metric); i++) {
602
+ struct stats_metric *stm = array_get(metric, i);
603
+
604
+ status = stats_add_num(st, &stm->name, stm->value.counter);
605
+ if (status != NC_OK) {
606
+ return status;
607
+ }
608
+ }
609
+
610
+ return NC_OK;
611
+ }
612
+
613
+ static void
614
+ stats_aggregate_metric(struct array *dst, struct array *src)
615
+ {
616
+ uint32_t i;
617
+
618
+ for (i = 0; i < array_n(src); i++) {
619
+ struct stats_metric *stm1, *stm2;
620
+
621
+ stm1 = array_get(src, i);
622
+ stm2 = array_get(dst, i);
623
+
624
+ ASSERT(stm1->type == stm2->type);
625
+
626
+ switch (stm1->type) {
627
+ case STATS_COUNTER:
628
+ stm2->value.counter += stm1->value.counter;
629
+ break;
630
+
631
+ case STATS_GAUGE:
632
+ stm2->value.counter += stm1->value.counter;
633
+ break;
634
+
635
+ case STATS_TIMESTAMP:
636
+ if (stm1->value.timestamp) {
637
+ stm2->value.timestamp = stm1->value.timestamp;
638
+ }
639
+ break;
640
+
641
+ default:
642
+ NOT_REACHED();
643
+ }
644
+ }
645
+ }
646
+
647
+ static void
648
+ stats_aggregate(struct stats *st)
649
+ {
650
+ uint32_t i;
651
+
652
+ if (st->aggregate == 0) {
653
+ log_debug(LOG_PVERB, "skip aggregate of shadow %p to sum %p as "
654
+ "generator is slow", st->shadow.elem, st->sum.elem);
655
+ return;
656
+ }
657
+
658
+ log_debug(LOG_PVERB, "aggregate stats shadow %p to sum %p", st->shadow.elem,
659
+ st->sum.elem);
660
+
661
+ for (i = 0; i < array_n(&st->shadow); i++) {
662
+ struct stats_pool *stp1, *stp2;
663
+ uint32_t j;
664
+
665
+ stp1 = array_get(&st->shadow, i);
666
+ stp2 = array_get(&st->sum, i);
667
+ stats_aggregate_metric(&stp2->metric, &stp1->metric);
668
+
669
+ for (j = 0; j < array_n(&stp1->server); j++) {
670
+ struct stats_server *sts1, *sts2;
671
+
672
+ sts1 = array_get(&stp1->server, j);
673
+ sts2 = array_get(&stp2->server, j);
674
+ stats_aggregate_metric(&sts2->metric, &sts1->metric);
675
+ }
676
+ }
677
+
678
+ st->aggregate = 0;
679
+ }
680
+
681
+ static rstatus_t
682
+ stats_make_rsp(struct stats *st)
683
+ {
684
+ rstatus_t status;
685
+ uint32_t i;
686
+
687
+ status = stats_add_header(st);
688
+ if (status != NC_OK) {
689
+ return status;
690
+ }
691
+
692
+ for (i = 0; i < array_n(&st->sum); i++) {
693
+ struct stats_pool *stp = array_get(&st->sum, i);
694
+ uint32_t j;
695
+
696
+ status = stats_begin_nesting(st, &stp->name);
697
+ if (status != NC_OK) {
698
+ return status;
699
+ }
700
+
701
+ /* copy pool metric from sum(c) to buffer */
702
+ status = stats_copy_metric(st, &stp->metric);
703
+ if (status != NC_OK) {
704
+ return status;
705
+ }
706
+
707
+ for (j = 0; j < array_n(&stp->server); j++) {
708
+ struct stats_server *sts = array_get(&stp->server, j);
709
+
710
+ status = stats_begin_nesting(st, &sts->name);
711
+ if (status != NC_OK) {
712
+ return status;
713
+ }
714
+
715
+ /* copy server metric from sum(c) to buffer */
716
+ status = stats_copy_metric(st, &sts->metric);
717
+ if (status != NC_OK) {
718
+ return status;
719
+ }
720
+
721
+ status = stats_end_nesting(st);
722
+ if (status != NC_OK) {
723
+ return status;
724
+ }
725
+ }
726
+
727
+ status = stats_end_nesting(st);
728
+ if (status != NC_OK) {
729
+ return status;
730
+ }
731
+ }
732
+
733
+ status = stats_add_footer(st);
734
+ if (status != NC_OK) {
735
+ return status;
736
+ }
737
+
738
+ return NC_OK;
739
+ }
740
+
741
+ static rstatus_t
742
+ stats_send_rsp(struct stats *st)
743
+ {
744
+ rstatus_t status;
745
+ ssize_t n;
746
+ int sd;
747
+
748
+ status = stats_make_rsp(st);
749
+ if (status != NC_OK) {
750
+ return status;
751
+ }
752
+
753
+ sd = accept(st->sd, NULL, NULL);
754
+ if (sd < 0) {
755
+ log_error("accept on m %d failed: %s", st->sd, strerror(errno));
756
+ return NC_ERROR;
757
+ }
758
+
759
+ log_debug(LOG_VERB, "send stats on sd %d %d bytes", sd, st->buf.len);
760
+
761
+ n = nc_sendn(sd, st->buf.data, st->buf.len);
762
+ if (n < 0) {
763
+ log_error("send stats on sd %d failed: %s", sd, strerror(errno));
764
+ close(sd);
765
+ return NC_ERROR;
766
+ }
767
+
768
+ close(sd);
769
+
770
+ return NC_OK;
771
+ }
772
+
773
+ static void *
774
+ stats_loop(void *arg)
775
+ {
776
+ struct stats *st = arg;
777
+ int n;
778
+
779
+ for (;;) {
780
+ n = epoll_wait(st->ep, &st->event, 1, st->interval);
781
+ if (n < 0) {
782
+ if (errno == EINTR) {
783
+ continue;
784
+ }
785
+ log_error("epoll wait on e %d with event m %d failed: %s",
786
+ st->ep, st->sd, strerror(errno));
787
+ break;
788
+ }
789
+
790
+ /* aggregate stats from shadow (b) -> sum (c) */
791
+ stats_aggregate(st);
792
+
793
+ if (n == 0) {
794
+ continue;
795
+ }
796
+
797
+ /* send aggregate stats sum (c) to collector */
798
+ stats_send_rsp(st);
799
+ }
800
+
801
+ return NULL;
802
+ }
803
+
804
+ static rstatus_t
805
+ stats_listen(struct stats *st)
806
+ {
807
+ rstatus_t status;
808
+ struct sockinfo si;
809
+
810
+ status = nc_resolve(&st->addr, st->port, &si);
811
+ if (status < 0) {
812
+ return status;
813
+ }
814
+
815
+ st->sd = socket(si.family, SOCK_STREAM, 0);
816
+ if (st->sd < 0) {
817
+ log_error("socket failed: %s", strerror(errno));
818
+ return NC_ERROR;
819
+ }
820
+
821
+ status = nc_set_reuseaddr(st->sd);
822
+ if (status < 0) {
823
+ log_error("set reuseaddr on m %d failed: %s", st->sd, strerror(errno));
824
+ return NC_ERROR;
825
+ }
826
+
827
+ status = bind(st->sd, (struct sockaddr *)&si.addr, si.addrlen);
828
+ if (status < 0) {
829
+ log_error("bind on m %d to addr '%.*s:%u' failed: %s", st->sd,
830
+ st->addr.len, st->addr.data, st->port, strerror(errno));
831
+ return NC_ERROR;
832
+ }
833
+
834
+ status = listen(st->sd, SOMAXCONN);
835
+ if (status < 0) {
836
+ log_error("listen on m %d failed: %s", st->sd, strerror(errno));
837
+ return NC_ERROR;
838
+ }
839
+
840
+ log_debug(LOG_NOTICE, "m %d listening on '%.*s:%u'", st->sd,
841
+ st->addr.len, st->addr.data, st->port);
842
+
843
+ return NC_OK;
844
+ }
845
+
846
+ static rstatus_t
847
+ stats_start_aggregator(struct stats *st)
848
+ {
849
+ rstatus_t status;
850
+ struct epoll_event ev;
851
+
852
+ if (!stats_enabled) {
853
+ return NC_OK;
854
+ }
855
+
856
+ status = stats_listen(st);
857
+ if (status != NC_OK) {
858
+ return status;
859
+ }
860
+
861
+ st->ep = epoll_create(10);
862
+ if (st->ep < 0) {
863
+ log_error("epoll create failed: %s", strerror(errno));
864
+ return NC_ERROR;
865
+ }
866
+
867
+ ev.data.fd = st->sd;
868
+ ev.events = EPOLLIN;
869
+
870
+ status = epoll_ctl(st->ep, EPOLL_CTL_ADD, st->sd, &ev);
871
+ if (status < 0) {
872
+ log_error("epoll ctl on e %d sd %d failed: %s", st->ep, st->sd,
873
+ strerror(errno));
874
+ return NC_ERROR;
875
+ }
876
+
877
+ status = pthread_create(&st->tid, NULL, stats_loop, st);
878
+ if (status < 0) {
879
+ log_error("stats aggregator create failed: %s", strerror(status));
880
+ return NC_ERROR;
881
+ }
882
+
883
+ return NC_OK;
884
+ }
885
+
886
+ static void
887
+ stats_stop_aggregator(struct stats *st)
888
+ {
889
+ if (!stats_enabled) {
890
+ return;
891
+ }
892
+
893
+ close(st->sd);
894
+ close(st->ep);
895
+ }
896
+
897
+ struct stats *
898
+ stats_create(uint16_t stats_port, char *stats_ip, int stats_interval,
899
+ char *source, struct array *server_pool)
900
+ {
901
+ rstatus_t status;
902
+ struct stats *st;
903
+
904
+ st = nc_alloc(sizeof(*st));
905
+ if (st == NULL) {
906
+ return NULL;
907
+ }
908
+
909
+ st->port = stats_port;
910
+ st->interval = stats_interval;
911
+ string_set_raw(&st->addr, stats_ip);
912
+
913
+ st->start_ts = (int64_t)time(NULL);
914
+
915
+ st->buf.len = 0;
916
+ st->buf.data = NULL;
917
+ st->buf.size = 0;
918
+
919
+ array_null(&st->current);
920
+ array_null(&st->shadow);
921
+ array_null(&st->sum);
922
+
923
+ st->tid = (pthread_t) -1;
924
+ st->ep = -1;
925
+ st->sd = -1;
926
+
927
+ string_set_text(&st->service_str, "service");
928
+ string_set_text(&st->service, "nutcracker");
929
+
930
+ string_set_text(&st->source_str, "source");
931
+ string_set_raw(&st->source, source);
932
+
933
+ string_set_text(&st->version_str, "version");
934
+ string_set_text(&st->version, NC_VERSION_STRING);
935
+
936
+ string_set_text(&st->uptime_str, "uptime");
937
+ string_set_text(&st->timestamp_str, "timestamp");
938
+
939
+ st->updated = 0;
940
+ st->aggregate = 0;
941
+
942
+ /* map server pool to current (a), shadow (b) and sum (c) */
943
+
944
+ status = stats_pool_map(&st->current, server_pool);
945
+ if (status != NC_OK) {
946
+ goto error;
947
+ }
948
+
949
+ status = stats_pool_map(&st->shadow, server_pool);
950
+ if (status != NC_OK) {
951
+ goto error;
952
+ }
953
+
954
+ status = stats_pool_map(&st->sum, server_pool);
955
+ if (status != NC_OK) {
956
+ goto error;
957
+ }
958
+
959
+ status = stats_create_buf(st);
960
+ if (status != NC_OK) {
961
+ goto error;
962
+ }
963
+
964
+ status = stats_start_aggregator(st);
965
+ if (status != NC_OK) {
966
+ goto error;
967
+ }
968
+
969
+ return st;
970
+
971
+ error:
972
+ stats_destroy(st);
973
+ return NULL;
974
+ }
975
+
976
+ void
977
+ stats_destroy(struct stats *st)
978
+ {
979
+ stats_stop_aggregator(st);
980
+ stats_pool_unmap(&st->sum);
981
+ stats_pool_unmap(&st->shadow);
982
+ stats_pool_unmap(&st->current);
983
+ stats_destroy_buf(st);
984
+ nc_free(st);
985
+ }
986
+
987
+ void
988
+ stats_swap(struct stats *st)
989
+ {
990
+ if (!stats_enabled) {
991
+ return;
992
+ }
993
+
994
+ if (st->aggregate == 1) {
995
+ log_debug(LOG_PVERB, "skip swap of current %p shadow %p as aggregator "
996
+ "is busy", st->current.elem, st->shadow.elem);
997
+ return;
998
+ }
999
+
1000
+ if (st->updated == 0) {
1001
+ log_debug(LOG_PVERB, "skip swap of current %p shadow %p as there is "
1002
+ "nothing new", st->current.elem, st->shadow.elem);
1003
+ return;
1004
+ }
1005
+
1006
+ log_debug(LOG_PVERB, "swap stats current %p shadow %p", st->current.elem,
1007
+ st->shadow.elem);
1008
+
1009
+ array_swap(&st->current, &st->shadow);
1010
+
1011
+ /*
1012
+ * Reset current (a) stats before giving it back to generator to keep
1013
+ * stats addition idempotent
1014
+ */
1015
+ stats_pool_reset(&st->current);
1016
+ st->updated = 0;
1017
+
1018
+ st->aggregate = 1;
1019
+ }
1020
+
1021
+ static struct stats_metric *
1022
+ stats_pool_to_metric(struct context *ctx, struct server_pool *pool,
1023
+ stats_pool_field_t fidx)
1024
+ {
1025
+ struct stats *st;
1026
+ struct stats_pool *stp;
1027
+ struct stats_metric *stm;
1028
+ uint32_t pidx;
1029
+
1030
+ pidx = pool->idx;
1031
+
1032
+ st = ctx->stats;
1033
+ stp = array_get(&st->current, pidx);
1034
+ stm = array_get(&stp->metric, fidx);
1035
+
1036
+ st->updated = 1;
1037
+
1038
+ log_debug(LOG_VVVERB, "metric '%.*s' in pool %"PRIu32"", stm->name.len,
1039
+ stm->name.data, pidx);
1040
+
1041
+ return stm;
1042
+ }
1043
+
1044
+ void
1045
+ _stats_pool_incr(struct context *ctx, struct server_pool *pool,
1046
+ stats_pool_field_t fidx)
1047
+ {
1048
+ struct stats_metric *stm;
1049
+
1050
+ stm = stats_pool_to_metric(ctx, pool, fidx);
1051
+
1052
+ ASSERT(stm->type == STATS_COUNTER || stm->type == STATS_GAUGE);
1053
+ stm->value.counter++;
1054
+
1055
+ log_debug(LOG_VVVERB, "incr field '%.*s' to %"PRId64"", stm->name.len,
1056
+ stm->name.data, stm->value.counter);
1057
+ }
1058
+
1059
+ void
1060
+ _stats_pool_decr(struct context *ctx, struct server_pool *pool,
1061
+ stats_pool_field_t fidx)
1062
+ {
1063
+ struct stats_metric *stm;
1064
+
1065
+ stm = stats_pool_to_metric(ctx, pool, fidx);
1066
+
1067
+ ASSERT(stm->type == STATS_GAUGE);
1068
+ stm->value.counter--;
1069
+
1070
+ log_debug(LOG_VVVERB, "decr field '%.*s' to %"PRId64"", stm->name.len,
1071
+ stm->name.data, stm->value.counter);
1072
+ }
1073
+
1074
+ void
1075
+ _stats_pool_incr_by(struct context *ctx, struct server_pool *pool,
1076
+ stats_pool_field_t fidx, int64_t val)
1077
+ {
1078
+ struct stats_metric *stm;
1079
+
1080
+ stm = stats_pool_to_metric(ctx, pool, fidx);
1081
+
1082
+ ASSERT(stm->type == STATS_COUNTER || stm->type == STATS_GAUGE);
1083
+ stm->value.counter += val;
1084
+
1085
+ log_debug(LOG_VVVERB, "incr by field '%.*s' to %"PRId64"", stm->name.len,
1086
+ stm->name.data, stm->value.counter);
1087
+ }
1088
+
1089
+ void
1090
+ _stats_pool_decr_by(struct context *ctx, struct server_pool *pool,
1091
+ stats_pool_field_t fidx, int64_t val)
1092
+ {
1093
+ struct stats_metric *stm;
1094
+
1095
+ stm = stats_pool_to_metric(ctx, pool, fidx);
1096
+
1097
+ ASSERT(stm->type == STATS_GAUGE);
1098
+ stm->value.counter -= val;
1099
+
1100
+ log_debug(LOG_VVVERB, "decr by field '%.*s' to %"PRId64"", stm->name.len,
1101
+ stm->name.data, stm->value.counter);
1102
+ }
1103
+
1104
+ static struct stats_metric *
1105
+ stats_server_to_metric(struct context *ctx, struct server *server,
1106
+ stats_server_field_t fidx)
1107
+ {
1108
+ struct stats *st;
1109
+ struct stats_pool *stp;
1110
+ struct stats_server *sts;
1111
+ struct stats_metric *stm;
1112
+ uint32_t pidx, sidx;
1113
+
1114
+ sidx = server->idx;
1115
+ pidx = server->owner->idx;
1116
+
1117
+ st = ctx->stats;
1118
+ stp = array_get(&st->current, pidx);
1119
+ sts = array_get(&stp->server, sidx);
1120
+ stm = array_get(&sts->metric, fidx);
1121
+
1122
+ st->updated = 1;
1123
+
1124
+ log_debug(LOG_VVVERB, "metric '%.*s' in pool %"PRIu32" server %"PRIu32"",
1125
+ stm->name.len, stm->name.data, pidx, sidx);
1126
+
1127
+ return stm;
1128
+ }
1129
+
1130
+ void
1131
+ _stats_server_incr(struct context *ctx, struct server *server,
1132
+ stats_server_field_t fidx)
1133
+ {
1134
+ struct stats_metric *stm;
1135
+
1136
+ stm = stats_server_to_metric(ctx, server, fidx);
1137
+
1138
+ ASSERT(stm->type == STATS_COUNTER || stm->type == STATS_GAUGE);
1139
+ stm->value.counter++;
1140
+
1141
+ log_debug(LOG_VVVERB, "incr field '%.*s' to %"PRId64"", stm->name.len,
1142
+ stm->name.data, stm->value.counter);
1143
+ }
1144
+
1145
+ void
1146
+ _stats_server_decr(struct context *ctx, struct server *server,
1147
+ stats_server_field_t fidx)
1148
+ {
1149
+ struct stats_metric *stm;
1150
+
1151
+ stm = stats_server_to_metric(ctx, server, fidx);
1152
+
1153
+ ASSERT(stm->type == STATS_GAUGE);
1154
+ stm->value.counter--;
1155
+
1156
+ log_debug(LOG_VVVERB, "decr field '%.*s' to %"PRId64"", stm->name.len,
1157
+ stm->name.data, stm->value.counter);
1158
+ }
1159
+
1160
+ void
1161
+ _stats_server_incr_by(struct context *ctx, struct server *server,
1162
+ stats_server_field_t fidx, int64_t val)
1163
+ {
1164
+ struct stats_metric *stm;
1165
+
1166
+ stm = stats_server_to_metric(ctx, server, fidx);
1167
+
1168
+ ASSERT(stm->type == STATS_COUNTER || stm->type == STATS_GAUGE);
1169
+ stm->value.counter += val;
1170
+
1171
+ log_debug(LOG_VVVERB, "incr by field '%.*s' to %"PRId64"", stm->name.len,
1172
+ stm->name.data, stm->value.counter);
1173
+ }
1174
+
1175
+ void
1176
+ _stats_server_decr_by(struct context *ctx, struct server *server,
1177
+ stats_server_field_t fidx, int64_t val)
1178
+ {
1179
+ struct stats_metric *stm;
1180
+
1181
+ stm = stats_server_to_metric(ctx, server, fidx);
1182
+
1183
+ ASSERT(stm->type == STATS_GAUGE);
1184
+ stm->value.counter -= val;
1185
+
1186
+ log_debug(LOG_VVVERB, "decr by field '%.*s' to %"PRId64"", stm->name.len,
1187
+ stm->name.data, stm->value.counter);
1188
+ }