root/src/modules/postgres.c

Revision d0f189b538d40f75068aac027df6e9f9245a4d5a, 10.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

while it's obvious why this worked before, C should require a nobreak keyword before labels separated by code.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007-2010, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <math.h>
40
41 #include "noit_module.h"
42 #include "noit_check.h"
43 #include "noit_check_tools.h"
44 #include "utils/noit_log.h"
45 #include "utils/noit_hash.h"
46
47 #include <libpq-fe.h>
48
49 /* Ripped from postgres 8.3.3 */
50 #ifndef BOOLOID
51 #define BOOLOID                  16
52 #endif
53 #ifndef INT2OID
54 #define INT2OID                  21
55 #endif
56 #ifndef INT4OID
57 #define INT4OID                  23
58 #endif
59 #ifndef INT8OID
60 #define INT8OID                  20
61 #endif
62 #ifndef FLOAT4OID
63 #define FLOAT4OID                700
64 #endif
65 #ifndef FLOAT8OID
66 #define FLOAT8OID                701
67 #endif
68 #ifndef NUMERICOID
69 #define NUMERICOID               1700
70 #endif
71
72 typedef struct {
73   noit_module_t *self;
74   noit_check_t *check;
75   stats_t current;
76   PGconn *conn;
77   PGresult *result;
78   int rv;
79   double connect_duration_d;
80   double *connect_duration;
81   double query_duration_d;
82   double *query_duration;
83   noit_hash_table attrs;
84   int timed_out;
85   char *error;
86 } postgres_check_info_t;
87
88 static noit_log_stream_t nlerr = NULL;
89 static noit_log_stream_t nldeb = NULL;
90
91 static void postgres_cleanup(noit_module_t *self, noit_check_t *check) {
92   postgres_check_info_t *ci = check->closure;
93   if(ci) {
94     if(ci->result) PQclear(ci->result);
95     if(ci->conn) PQfinish(ci->conn);
96     noit_check_release_attrs(&ci->attrs);
97     if(ci->error) free(ci->error);
98     memset(ci, 0, sizeof(*ci));
99   }
100 }
101 static void postgres_ingest_stats(postgres_check_info_t *ci) {
102   if(ci->rv == PGRES_TUPLES_OK) {
103     /* metrics */
104     int nrows, ncols, i, j;
105     nrows = PQntuples(ci->result);
106     ncols = PQnfields(ci->result);
107     noit_stats_set_metric(&ci->current, "row_count", METRIC_INT32, &nrows);
108     for (i=0; i<nrows; i++) {
109       noitL(nldeb, "postgres: row %d [%d cols]:\n", i, ncols);
110       if(ncols<2) continue;
111       if(PQgetisnull(ci->result, i, 0)) continue;
112       for (j=1; j<ncols; j++) {
113         Oid coltype;
114         int iv, *piv;
115         int64_t lv, *plv;
116         double dv, *pdv;
117         char *sv;
118         char mname[128];
119  
120         snprintf(mname, sizeof(mname), "%s`%s",
121                  PQgetvalue(ci->result, i, 0), PQfname(ci->result, j));
122         coltype = PQftype(ci->result, j);
123         noitL(nldeb, "postgres:   col %d (%s) type %d: %s\n", j, mname, coltype,
124               PQgetisnull(ci->result, i, j) ? "[[null]]" : PQgetvalue(ci->result, i, j));
125         switch(coltype) {
126           case BOOLOID:
127             if(PQgetisnull(ci->result, i, j)) piv = NULL;
128             else {
129               iv = strcmp(PQgetvalue(ci->result, i, j), "f") ? 1 : 0;
130               piv = &iv;
131             }
132             noit_stats_set_metric(&ci->current, mname, METRIC_INT32, piv);
133             break;
134           case INT2OID:
135           case INT4OID:
136           case INT8OID:
137             if(PQgetisnull(ci->result, i, j)) plv = NULL;
138             else {
139               lv = strtoll(PQgetvalue(ci->result, i, j), NULL, 10);
140               plv = &lv;
141             }
142             noit_stats_set_metric(&ci->current, mname, METRIC_INT64, plv);
143             break;
144           case FLOAT4OID:
145           case FLOAT8OID:
146           case NUMERICOID:
147             if(PQgetisnull(ci->result, i, j)) pdv = NULL;
148             else {
149               dv = atof(PQgetvalue(ci->result, i, j));
150               pdv = &dv;
151             }
152             noit_stats_set_metric(&ci->current, mname, METRIC_DOUBLE, pdv);
153             break;
154           default:
155             if(PQgetisnull(ci->result, i, j)) sv = NULL;
156             else sv = PQgetvalue(ci->result, i, j);
157             noit_stats_set_metric(&ci->current, mname, METRIC_GUESS, sv);
158             break;
159         }
160       }
161     }
162   }
163 }
164 static void postgres_log_results(noit_module_t *self, noit_check_t *check) {
165   struct timeval duration;
166   postgres_check_info_t *ci = check->closure;
167
168   gettimeofday(&ci->current.whence, NULL);
169   sub_timeval(ci->current.whence, check->last_fire_time, &duration);
170   ci->current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
171   ci->current.available = NP_UNAVAILABLE;
172   ci->current.state = NP_BAD;
173   if(ci->connect_duration)
174     noit_stats_set_metric(&ci->current, "connect_duration", METRIC_DOUBLE,
175                           ci->connect_duration);
176   if(ci->query_duration)
177     noit_stats_set_metric(&ci->current, "query_duration", METRIC_DOUBLE,
178                           ci->query_duration);
179   if(ci->error) ci->current.status = ci->error;
180   else if(ci->timed_out) ci->current.status = "timeout";
181   else if(ci->rv == PGRES_COMMAND_OK) {
182     ci->current.available = NP_AVAILABLE;
183     ci->current.state = NP_GOOD;
184     ci->current.status = "command ok";
185   }
186   else if(ci->rv == PGRES_TUPLES_OK) {
187     ci->current.available = NP_AVAILABLE;
188     ci->current.state = NP_GOOD;
189     ci->current.status = "tuples ok";
190   }
191   else ci->current.status = "internal error";
192
193   noit_check_set_stats(self, check, &ci->current);
194 }
195
196 #define FETCH_CONFIG_OR(key, str) do { \
197   if(!noit_hash_retr_str(check->config, #key, strlen(#key), &key)) \
198     key = str; \
199 } while(0)
200
201 #define AVAIL_BAIL(str) do { \
202   PGconn *conn_swap = ci->conn; \
203   PGresult *result_swap = ci->result; \
204   if(ci->result) { ci->result = NULL; PQclear(result_swap); } \
205   if(ci->conn) { ci->conn = NULL; PQfinish(conn_swap); } \
206   ci->timed_out = 0; \
207   ci->error = strdup(str); \
208   return 0; \
209 } while(0)
210
211 static int postgres_drive_session(eventer_t e, int mask, void *closure,
212                                   struct timeval *now) {
213   const char *dsn, *sql;
214   char sql_buff[8192];
215   char dsn_buff[512];
216   struct timeval t1, t2, diff;
217   postgres_check_info_t *ci = closure;
218   noit_check_t *check = ci->check;
219
220   if(mask & (EVENTER_READ | EVENTER_WRITE)) {
221     /* this case is impossible from the eventer.  It is called as
222      * such on the synchronous completion of the event.
223      */
224     postgres_log_results(ci->self, ci->check);
225     postgres_cleanup(ci->self, ci->check);
226     check->flags &= ~NP_RUNNING;
227     return 0;
228   }
229
230   switch(mask) {
231     case EVENTER_ASYNCH_WORK:
232       noit_check_stats_clear(&ci->current);
233       ci->connect_duration = NULL;
234       ci->query_duration = NULL;
235
236       FETCH_CONFIG_OR(dsn, "");
237       noit_check_interpolate(dsn_buff, sizeof(dsn_buff), dsn,
238                              &ci->attrs, check->config);
239       ci->conn = PQconnectdb(dsn_buff);
240       if(!ci->conn) AVAIL_BAIL("PQconnectdb failed");
241       if(PQstatus(ci->conn) != CONNECTION_OK)
242         AVAIL_BAIL(PQerrorMessage(ci->conn));
243
244       FETCH_CONFIG_OR(sql, "");
245       noit_check_interpolate(sql_buff, sizeof(sql_buff), sql,
246                              &ci->attrs, check->config);
247       gettimeofday(&t1, NULL);
248       sub_timeval(t1, check->last_fire_time, &diff);
249       ci->connect_duration_d = diff.tv_sec * 1000.0 + diff.tv_usec / 1000.0;
250       ci->connect_duration = &ci->connect_duration_d;
251
252       ci->result = PQexec(ci->conn, sql_buff);
253
254       gettimeofday(&t2, NULL);
255       sub_timeval(t2, t1, &diff);
256       ci->query_duration_d = diff.tv_sec * 1000.0 + diff.tv_usec / 1000.0;
257       ci->query_duration = &ci->query_duration_d;
258
259       if(!ci->result) AVAIL_BAIL("PQexec failed");
260       ci->rv = PQresultStatus(ci->result);
261       switch(ci->rv) {
262        case PGRES_TUPLES_OK:
263         postgres_ingest_stats(ci);
264        case PGRES_COMMAND_OK:
265         break;
266        default:
267         AVAIL_BAIL(PQresultErrorMessage(ci->result));
268       }
269       if(ci->result) {
270         PGresult *result_swap = ci->result;
271         ci->result = NULL;
272         PQclear(result_swap);
273       }
274       if(ci->conn) {
275         PGconn *conn_swap = ci->conn;
276         ci->conn = NULL;
277         PQfinish(conn_swap);
278       }
279       ci->timed_out = 0;
280       return 0;
281       break;
282     case EVENTER_ASYNCH_CLEANUP:
283       /* This sets us up for a completion call. */
284       e->mask = EVENTER_READ | EVENTER_WRITE;
285       break;
286     default:
287       abort();
288   }
289   return 0;
290 }
291
292 static int postgres_initiate(noit_module_t *self, noit_check_t *check,
293                              noit_check_t *cause) {
294   postgres_check_info_t *ci = check->closure;
295   struct timeval __now;
296
297   /* We cannot be running */
298   assert(!(check->flags & NP_RUNNING));
299   check->flags |= NP_RUNNING;
300
301   ci->self = self;
302   ci->check = check;
303
304   ci->timed_out = 1;
305   ci->rv = -1;
306   noit_check_make_attrs(check, &ci->attrs);
307   gettimeofday(&__now, NULL);
308   memcpy(&check->last_fire_time, &__now, sizeof(__now));
309
310   /* Register a handler for the worker */
311   noit_check_run_full_asynch(check, postgres_drive_session);
312   return 0;
313 }
314
315 static int postgres_initiate_check(noit_module_t *self, noit_check_t *check,
316                                    int once, noit_check_t *cause) {
317   if(!check->closure) check->closure = calloc(1, sizeof(postgres_check_info_t));
318   INITIATE_CHECK(postgres_initiate, self, check, cause);
319   return 0;
320 }
321
322 static int postgres_onload(noit_image_t *self) {
323   nlerr = noit_log_stream_find("error/postgres");
324   nldeb = noit_log_stream_find("debug/postgres");
325   if(!nlerr) nlerr = noit_stderr;
326   if(!nldeb) nldeb = noit_debug;
327
328   eventer_name_callback("http/postgres_drive_session", postgres_drive_session);
329   return 0;
330 }
331
332 #include "postgres.xmlh"
333 noit_module_t postgres = {
334   {
335     NOIT_MODULE_MAGIC,
336     NOIT_MODULE_ABI_VERSION,
337     "postgres",
338     "PostgreSQL Checker",
339     postgres_xml_description,
340     postgres_onload
341   },
342   NULL,
343   NULL,
344   postgres_initiate_check,
345   postgres_cleanup
346 };
347
Note: See TracBrowser for help on using the browser.