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