1 /*
2 * nuster stats functions.
3 *
4 * Copyright (C) Jiang Wenyuan, < koubunen AT gmail DOT com >
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13 #include <inttypes.h>
14
15 #include <types/global.h>
16
17 #include <proto/stream_interface.h>
18 #include <proto/proxy.h>
19
20 #include <nuster/nuster.h>
21
22 void
nst_stats_update_cache(int state,uint64_t bytes)23 nst_stats_update_cache(int state, uint64_t bytes) {
24 nst_shctx_lock(global.nuster.stats);
25
26 global.nuster.stats->cache.total++;
27
28 switch(state) {
29 case NST_CTX_STATE_HIT_MEMORY:
30 case NST_CTX_STATE_HIT_DISK:
31 global.nuster.stats->cache.hit++;
32 global.nuster.stats->cache.bytes += bytes;
33 break;
34 case NST_CTX_STATE_CREATE:
35 global.nuster.stats->cache.abort++;
36 break;
37 case NST_CTX_STATE_DONE:
38 global.nuster.stats->cache.fetch++;
39 break;
40 default:
41 break;
42 }
43
44 nst_shctx_unlock(global.nuster.stats);
45 }
46
47 void
nst_stats_update_nosql(int state)48 nst_stats_update_nosql(int state) {
49 nst_shctx_lock(global.nuster.stats);
50
51 global.nuster.stats->nosql.total++;
52
53 switch(state) {
54 case NST_CTX_STATE_HIT_MEMORY:
55 case NST_CTX_STATE_HIT_DISK:
56 global.nuster.stats->nosql.get++;
57 break;
58 case NST_CTX_STATE_CREATE:
59 global.nuster.stats->nosql.post++;
60 break;
61 case NST_CTX_STATE_DELETE:
62 global.nuster.stats->nosql.delete++;
63 break;
64 default:
65 break;
66 }
67
68 nst_shctx_unlock(global.nuster.stats);
69 }
70
71 /*
72 * return 1 if the req is done, otherwise 0
73 */
74
75 int
nst_stats_applet(hpx_stream_t * s,hpx_channel_t * req,hpx_proxy_t * px)76 nst_stats_applet(hpx_stream_t *s, hpx_channel_t *req, hpx_proxy_t *px) {
77 hpx_stream_interface_t *si = &s->si[1];
78 hpx_appctx_t *appctx;
79
80 s->target = &nuster.applet.stats.obj_type;
81
82 if(unlikely(!si_register_handler(si, objt_applet(s->target)))) {
83 return 1;
84 } else {
85 appctx = si_appctx(si);
86 appctx->st0 = NST_STATS_HEADER;
87 appctx->st1 = px->uuid;
88 appctx->st2 = 0;
89
90 req->analysers &= (AN_REQ_HTTP_BODY | AN_REQ_FLT_HTTP_HDRS | AN_REQ_FLT_END);
91 req->analysers &= ~AN_REQ_FLT_XFER_DATA;
92 req->analysers |= AN_REQ_HTTP_XFER_BODY;
93
94 return 1;
95 }
96
97 }
98
99 static int
_nst_stats_header(hpx_appctx_t * appctx,hpx_stream_interface_t * si,hpx_htx_t * htx)100 _nst_stats_header(hpx_appctx_t *appctx, hpx_stream_interface_t *si, hpx_htx_t *htx) {
101 hpx_stream_t *s = si_strm(si);
102 hpx_htx_sl_t *sl;
103 unsigned int flags;
104
105 flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_ENC|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
106 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("200"), ist("OK"));
107
108 if(!sl) {
109 goto full;
110 }
111
112 sl->info.res.status = 200;
113
114 if(!htx_add_header(htx, ist("Cache-Control"), ist("no-cache"))) {
115 goto full;
116 }
117
118 if(!htx_add_header(htx, ist("Content-Type"), ist("text/plain"))) {
119 goto full;
120 }
121
122 if(!htx_add_endof(htx, HTX_BLK_EOH)) {
123 goto full;
124 }
125
126 channel_add_input(&s->res, htx->data);
127
128 return 1;
129
130 full:
131 htx_reset(htx);
132 si_rx_room_blk(si);
133
134 return 0;
135 }
136
137 static int
_nst_stats_putdata(hpx_channel_t * chn,hpx_htx_t * htx,hpx_buffer_t * chk)138 _nst_stats_putdata(hpx_channel_t *chn, hpx_htx_t *htx, hpx_buffer_t *chk) {
139
140 if(chk->data >= channel_htx_recv_max(chn, htx)) {
141 return 0;
142 }
143
144 if(!htx_add_data_atonce(htx, ist2(chk->area, chk->data))) {
145 return 0;
146 }
147
148 channel_add_input(chn, chk->data);
149 chk->data = 0;
150
151 return 1;
152 }
153
154 static int
_getMaxPaddingLen()155 _getMaxPaddingLen() {
156 hpx_proxy_t *p;
157 int max = 32;
158
159 p = proxies_list;
160
161 while(p) {
162
163 if((p->cap & PR_CAP_BE)
164 && (p->nuster.mode == NST_MODE_CACHE || p->nuster.mode == NST_MODE_NOSQL)) {
165
166 nst_rule_t *rule = NULL;
167 int s1 = strlen(p->id);
168
169 rule = nuster.proxy[p->uuid]->rule;
170
171 while(rule) {
172 int s2 = s1 + 8 + strlen(rule->name);
173
174 if(s2 > max) {
175 max = s2;
176 }
177
178 rule = rule->next;
179 }
180 }
181
182 p = p->next;
183 }
184
185 return max;
186 }
187
188 static int
_nst_stats_payload(hpx_appctx_t * appctx,hpx_stream_interface_t * si,hpx_htx_t * htx)189 _nst_stats_payload(hpx_appctx_t *appctx, hpx_stream_interface_t *si, hpx_htx_t *htx) {
190 hpx_channel_t *res = si_ic(si);
191 int len = _getMaxPaddingLen();
192
193 chunk_reset(&trash);
194
195 chunk_appendf(&trash, "**NUSTER**\n");
196
197 chunk_appendf(&trash, "%-*s%s\n", len, "nuster.cache:",
198 global.nuster.cache.status == NST_STATUS_ON ? "on" : "off");
199
200 chunk_appendf(&trash, "%-*s%s\n", len, "nuster.nosql:",
201 global.nuster.nosql.status == NST_STATUS_ON ? "on" : "off");
202
203 chunk_appendf(&trash, "%-*s%s\n", len, "nuster.manager:",
204 global.nuster.manager.status == NST_STATUS_ON ? "on" : "off");
205
206 if(global.nuster.manager.status == NST_STATUS_ON) {
207 chunk_appendf(&trash, "\n**MANAGER**\n");
208
209 chunk_appendf(&trash, "%-*s%s\n", len, "manager.uri:", global.nuster.manager.uri.ptr);
210
211 chunk_appendf(&trash, "%-*s%s\n", len, "manager.purge_method:",
212 global.nuster.manager.purge_method.ptr);
213 }
214
215 if(global.nuster.cache.status == NST_STATUS_ON || global.nuster.nosql.status == NST_STATUS_ON) {
216 chunk_appendf(&trash, "\n**DICT**\n");
217
218 if(global.nuster.cache.status == NST_STATUS_ON) {
219 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.cache.size:",
220 global.nuster.cache.dict_size);
221
222 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.cache.length:",
223 nuster.cache->dict.size);
224
225 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.cache.used:",
226 nuster.cache->dict.used);
227
228 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.cache.cleanup_idx:",
229 nuster.cache->dict.cleanup_idx);
230
231 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.cache.sync_idx:",
232 nuster.cache->dict.sync_idx);
233 }
234
235 if(global.nuster.nosql.status == NST_STATUS_ON) {
236 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.nosql.size:",
237 global.nuster.nosql.dict_size);
238
239 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.nosql.length:",
240 nuster.nosql->dict.size);
241
242 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.nosql.used:",
243 nuster.nosql->dict.used);
244
245 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.nosql.cleanup_idx:",
246 nuster.nosql->dict.cleanup_idx);
247
248 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "dict.nosql.sync_idx:",
249 nuster.nosql->dict.sync_idx);
250 }
251 }
252
253 chunk_appendf(&trash, "\n**STORE MEMORY**\n");
254
255 /*
256 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "memory.common.total:",
257 global.nuster.memory->total);
258
259 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "memory.common.used:",
260 global.nuster.memory->used);
261 */
262
263 if(global.nuster.cache.status == NST_STATUS_ON) {
264 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "store.memory.cache.size:",
265 global.nuster.cache.memory->size);
266
267 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "store.memory.cache.used:",
268 global.nuster.cache.memory->used);
269
270 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "store.memory.cache.count:",
271 nuster.cache->store.ring.count);
272 }
273
274 if(global.nuster.nosql.status == NST_STATUS_ON) {
275 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "store.memory.nosql.size:",
276 global.nuster.nosql.memory->size);
277
278 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "store.memory.nosql.used:",
279 global.nuster.nosql.memory->used);
280
281 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "store.memory.nosql.count:",
282 nuster.nosql->store.ring.count);
283 }
284
285 if(global.nuster.cache.status == NST_STATUS_ON || global.nuster.nosql.status == NST_STATUS_ON) {
286 if(global.nuster.cache.root.len || global.nuster.nosql.root.len) {
287 chunk_appendf(&trash, "\n**STORE DISK**\n");
288 }
289 }
290
291 if(global.nuster.cache.status == NST_STATUS_ON && global.nuster.cache.root.len) {
292 chunk_appendf(&trash, "%-*s%s\n", len, "store.disk.cache.dir:",
293 global.nuster.cache.root.ptr);
294
295 chunk_appendf(&trash, "%-*s%s\n", len, "store.disk.cache.loaded:",
296 nuster.cache->store.disk.loaded ? "yes" : "no");
297 }
298
299 if(global.nuster.nosql.status == NST_STATUS_ON && global.nuster.nosql.root.len) {
300 chunk_appendf(&trash, "%-*s%s\n", len, "store.disk.nosql.dir:",
301 global.nuster.nosql.root.ptr);
302
303 chunk_appendf(&trash, "%-*s%s\n", len, "store.disk.nosql.loaded:",
304 nuster.nosql->store.disk.loaded ? "yes" : "no");
305 }
306
307 if(global.nuster.cache.status == NST_STATUS_ON || global.nuster.nosql.status == NST_STATUS_ON) {
308 chunk_appendf(&trash, "\n**STATS**\n");
309 }
310
311 if(global.nuster.cache.status == NST_STATUS_ON) {
312 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.cache.total:",
313 global.nuster.stats->cache.total);
314
315 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.cache.hit:",
316 global.nuster.stats->cache.hit);
317
318 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.cache.fetch:",
319 global.nuster.stats->cache.fetch);
320
321 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.cache.abort:",
322 global.nuster.stats->cache.abort);
323
324 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.cache.bytes:",
325 global.nuster.stats->cache.bytes);
326 }
327
328 if(global.nuster.nosql.status == NST_STATUS_ON) {
329 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.nosql.total:",
330 global.nuster.stats->nosql.total);
331
332 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.nosql.get:",
333 global.nuster.stats->nosql.get);
334
335 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.nosql.post:",
336 global.nuster.stats->nosql.post);
337
338 chunk_appendf(&trash, "%-*s%"PRIu64"\n", len, "stats.nosql.delete:",
339 global.nuster.stats->nosql.delete);
340 }
341
342 if(!_nst_stats_putdata(res, htx, &trash)) {
343 goto full;
344 }
345
346 return 1;
347
348 full:
349 si_rx_room_blk(si);
350
351 return 0;
352 }
353
354 static int
_nst_stats_proxy(hpx_appctx_t * appctx,hpx_stream_interface_t * si,hpx_htx_t * htx)355 _nst_stats_proxy(hpx_appctx_t *appctx, hpx_stream_interface_t *si, hpx_htx_t *htx) {
356 hpx_channel_t *res = si_ic(si);
357 hpx_proxy_t *p;
358 int len = _getMaxPaddingLen();
359
360 chunk_reset(&trash);
361
362 p = proxies_list;
363
364 while(p) {
365 nst_rule_t *rule = NULL;
366
367 if(htx_almost_full(htx)) {
368 si_rx_room_blk(si);
369
370 return 0;
371 }
372
373 if(p->uuid != appctx->st1) {
374 goto next;
375 }
376
377 if((p->cap & PR_CAP_BE)
378 && (p->nuster.mode == NST_MODE_CACHE || p->nuster.mode == NST_MODE_NOSQL)) {
379
380 rule = nuster.proxy[p->uuid]->rule;
381
382 while(rule) {
383
384 if(htx_almost_full(htx)) {
385 si_rx_room_blk(si);
386
387 return 0;
388 }
389
390 if(rule->uuid == appctx->st2) {
391 int i = len - strlen(p->id) - 8 - strlen(rule->name);
392
393 if(rule->idx == 0) {
394 chunk_printf(&trash, "\n**PROXY %s %s**\n",
395 p->nuster.mode == NST_MODE_CACHE ? "cache" : "nosql",
396 p->id);
397 }
398
399 chunk_appendf(&trash, "%s.rule.%s: ", p->id, rule->name);
400
401 while(i--) {
402 chunk_appendf(&trash, " ");
403 }
404
405 chunk_appendf(&trash, "state=%-4smemory=%-4sdisk=%-6sttl=%"PRIu32"\n",
406 rule->state == NST_RULE_ENABLED ? "on" : "off",
407 nst_store_memory_on(rule->store) ? "on" : "off",
408 nst_store_disk_on(rule->store) ? "on"
409 : nst_store_disk_off(rule->store) ? "off"
410 : "sync",
411 rule->ttl
412 );
413
414 if(!_nst_stats_putdata(res, htx, &trash)) {
415 goto full;
416 }
417
418 appctx->st2++;
419 }
420
421 rule = rule->next;
422 }
423 }
424
425 next:
426 appctx->st1 = p->next ? p->next->uuid : 0;
427
428 p = p->next;
429 }
430
431 return 1;
432
433 full:
434 si_rx_room_blk(si);
435
436 return 0;
437 }
438
439 static void
nst_stats_handler(hpx_appctx_t * appctx)440 nst_stats_handler(hpx_appctx_t *appctx) {
441 hpx_stream_interface_t *si = appctx->owner;
442 hpx_channel_t *req = si_oc(si);
443 hpx_channel_t *res = si_ic(si);
444 hpx_stream_t *s = si_strm(si);
445 hpx_htx_t *req_htx, *res_htx;
446
447 req_htx = htx_from_buf(&req->buf);
448 res_htx = htx_from_buf(&res->buf);
449
450 if(appctx->st0 == NST_STATS_HEADER) {
451
452 if(_nst_stats_header(appctx, si, res_htx)) {
453 appctx->st0 = NST_STATS_PAYLOAD;
454 }
455 }
456
457 if(appctx->st0 == NST_STATS_PAYLOAD) {
458
459 if(_nst_stats_payload(appctx, si, res_htx)) {
460 appctx->st0 = NST_STATS_PROXY;
461 }
462 }
463
464 if(appctx->st0 == NST_STATS_PROXY) {
465
466 if(_nst_stats_proxy(appctx, si, res_htx)) {
467 appctx->st0 = NST_STATS_DONE;
468 }
469 }
470
471 if(appctx->st0 == NST_STATS_DONE) {
472
473 if(!htx_add_endof(res_htx, HTX_BLK_EOM)) {
474 si_rx_room_blk(si);
475
476 goto out;
477 }
478
479 channel_add_input(&s->res, 1);
480
481 if(!(res->flags & CF_SHUTR)) {
482 res->flags |= CF_READ_NULL;
483 si_shutr(si);
484 }
485
486 /* eat the whole request */
487 if(co_data(req)) {
488 co_htx_skip(req, req_htx, co_data(req));
489 htx_to_buf(req_htx, &req->buf);
490 }
491 }
492
493 out:
494 htx_to_buf(res_htx, &res->buf);
495
496 if(!channel_is_empty(res)) {
497 si_stop_get(si);
498 }
499 }
500
501 int
nst_stats_init()502 nst_stats_init() {
503 global.nuster.stats = nst_memory_alloc(global.nuster.memory, sizeof(nst_stats_t));
504
505 if(!global.nuster.stats) {
506 return NST_ERR;
507 }
508
509 memset(global.nuster.stats, 0, sizeof(nst_stats_t));
510
511 if(nst_shctx_init(global.nuster.stats) != NST_OK) {
512 return NST_ERR;
513 }
514
515 nuster.applet.stats.fct = nst_stats_handler;
516
517 return NST_OK;
518 }
519
520