void ngx_postgres_upstream_free_peer(ngx_peer_connection_t *pc, void *data, ngx_uint_t state) { ngx_postgres_upstream_peer_data_t *pgdt = data; ngx_postgres_upstream_srv_conf_t *pgscf; dd("entering"); #if defined(nginx_version) && (nginx_version < 8017) if (data == NULL) { dd("returning"); return; } #endif pgscf = pgdt->srv_conf; if (pgscf->max_cached) { ngx_postgres_keepalive_free_peer(pc, pgdt, pgscf, state); } if (pc->connection) { dd("free connection to PostgreSQL database"); ngx_postgres_upstream_free_connection(pc->log, pc->connection, pgdt->pgconn, pgscf); pgdt->pgconn = NULL; pc->connection = NULL; } dd("returning"); }
void ngx_postgres_keepalive_cleanup(void *data) { ngx_postgres_upstream_srv_conf_t *pgscf = data; ngx_postgres_keepalive_cache_t *item; ngx_queue_t *q; dd("entering"); /* ngx_queue_empty is broken when used on unitialized queue */ if (pgscf->cache.prev == NULL) { dd("returning"); return; } /* just to be on the safe-side */ pgscf->max_cached = 0; while (!ngx_queue_empty(&pgscf->cache)) { q = ngx_queue_head(&pgscf->cache); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_postgres_keepalive_cache_t, queue); dd("postgres: disconnecting %p", item->connection); ngx_postgres_upstream_free_connection(item->connection->log, item->connection, item->pgconn, pgscf); } dd("returning"); }
void ngx_postgres_keepalive_close_handler(ngx_event_t *ev) { ngx_postgres_upstream_srv_conf_t *pgscf; ngx_postgres_keepalive_cache_t *item; ngx_connection_t *c; PGresult *res; dd("entering"); c = ev->data; item = c->data; if (c->close) { goto close; } if (PQconsumeInput(item->pgconn) && !PQisBusy(item->pgconn)) { res = PQgetResult(item->pgconn); if (res == NULL) { dd("returning"); return; } PQclear(res); dd("received result on idle keepalive connection"); ngx_log_error(NGX_LOG_ERR, c->log, 0, "postgres: received result on idle keepalive connection"); } close: pgscf = item->srv_conf; ngx_postgres_upstream_free_connection(ev->log, c, item->pgconn, pgscf); ngx_queue_remove(&item->queue); ngx_queue_insert_head(&pgscf->free, &item->queue); dd("returning"); }
ngx_int_t ngx_postgres_upstream_get_peer(ngx_peer_connection_t *pc, void *data) { ngx_postgres_upstream_peer_data_t *pgdt = data; ngx_postgres_upstream_srv_conf_t *pgscf; #if defined(nginx_version) && (nginx_version < 8017) ngx_postgres_ctx_t *pgctx; #endif ngx_postgres_upstream_peers_t *peers; ngx_postgres_upstream_peer_t *peer; ngx_connection_t *pgxc = NULL; int fd; ngx_event_t *rev, *wev; ngx_int_t rc; u_char *connstring, *last; size_t len; dd("entering"); #if defined(nginx_version) && (nginx_version < 8017) if (data == NULL) { goto failed; } pgctx = ngx_http_get_module_ctx(pgdt->request, ngx_postgres_module); #endif pgscf = pgdt->srv_conf; pgdt->failed = 0; if (pgscf->max_cached && pgscf->single) { rc = ngx_postgres_keepalive_get_peer_single(pc, pgdt, pgscf); if (rc != NGX_DECLINED) { /* re-use keepalive peer */ dd("re-using keepalive peer (single)"); pgdt->state = state_db_send_query; ngx_postgres_process_events(pgdt->request); dd("returning NGX_AGAIN"); return NGX_AGAIN; } } peers = pgscf->peers; if (pgscf->current > peers->number - 1) { pgscf->current = 0; } peer = &peers->peer[pgscf->current++]; pgdt->name.len = peer->name.len; pgdt->name.data = peer->name.data; pgdt->sockaddr = *peer->sockaddr; pc->name = &pgdt->name; pc->sockaddr = &pgdt->sockaddr; pc->socklen = peer->socklen; pc->cached = 0; if ((pgscf->max_cached) && (!pgscf->single)) { rc = ngx_postgres_keepalive_get_peer_multi(pc, pgdt, pgscf); if (rc != NGX_DECLINED) { /* re-use keepalive peer */ dd("re-using keepalive peer (multi)"); pgdt->state = state_db_send_query; ngx_postgres_process_events(pgdt->request); dd("returning NGX_AGAIN"); return NGX_AGAIN; } } if ((pgscf->reject) && (pgscf->active_conns >= pgscf->max_cached)) { ngx_log_error(NGX_LOG_INFO, pc->log, 0, "postgres: keepalive connection pool is full," " rejecting request to upstream \"%V\"", &peer->name); /* a bit hack-ish way to return error response (setup part) */ pc->connection = ngx_get_connection(0, pc->log); #if defined(nginx_version) && (nginx_version < 8017) pgctx->status = NGX_HTTP_SERVICE_UNAVAILABLE; #endif dd("returning NGX_AGAIN (NGX_HTTP_SERVICE_UNAVAILABLE)"); return NGX_AGAIN; } /* sizeof("...") - 1 + 1 (for spaces and '\0' omitted */ len = sizeof("hostaddr=") + peer->host.len + sizeof("port=") + sizeof("65535") - 1 + sizeof("dbname=") + peer->dbname.len + sizeof("user="******"password="******"sslmode=disable"); connstring = ngx_pnalloc(pgdt->request->pool, len); if (connstring == NULL) { #if defined(nginx_version) && (nginx_version >= 8017) dd("returning NGX_ERROR"); return NGX_ERROR; #else goto failed; #endif } /* TODO add unix sockets */ last = ngx_snprintf(connstring, len - 1, "hostaddr=%V port=%d dbname=%V user=%V password=%V" " sslmode=disable", &peer->host, peer->port, &peer->dbname, &peer->user, &peer->password); *last = '\0'; dd("PostgreSQL connection string: %s", connstring); /* * internal checks in PQsetnonblocking are taking care of any * PQconnectStart failures, so we don't need to check them here. */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "postgres: connecting"); pgdt->pgconn = PQconnectStart((const char *)connstring); if (PQsetnonblocking(pgdt->pgconn, 1) == -1) { ngx_log_error(NGX_LOG_ERR, pc->log, 0, "postgres: connection failed: %s in upstream \"%V\"", PQerrorMessage(pgdt->pgconn), &peer->name); PQfinish(pgdt->pgconn); pgdt->pgconn = NULL; #if defined(nginx_version) && (nginx_version >= 8017) dd("returning NGX_DECLINED"); return NGX_DECLINED; #else pgctx->status = NGX_HTTP_BAD_GATEWAY; goto failed; #endif } #if defined(DDEBUG) && (DDEBUG > 1) PQtrace(pgdt->pgconn, stderr); #endif dd("connection status:%d", (int) PQstatus(pgdt->pgconn)); /* take spot in keepalive connection pool */ pgscf->active_conns++; /* add the file descriptor (fd) into an nginx connection structure */ fd = PQsocket(pgdt->pgconn); if (fd == -1) { ngx_log_error(NGX_LOG_ERR, pc->log, 0, "postgres: failed to get connection fd"); goto invalid; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "postgres: connection fd:%d", fd); pgxc = pc->connection = ngx_get_connection(fd, pc->log); if (pgxc == NULL) { ngx_log_error(NGX_LOG_ERR, pc->log, 0, "postgres: failed to get a free nginx connection"); goto invalid; } pgxc->log = pc->log; pgxc->log_error = pc->log_error; pgxc->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); rev = pgxc->read; wev = pgxc->write; rev->log = pc->log; wev->log = pc->log; /* register the connection with postgres connection fd into the * nginx event model */ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { dd("NGX_USE_RTSIG_EVENT"); if (ngx_add_conn(pgxc) != NGX_OK) { goto bad_add; } } else if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { dd("NGX_USE_CLEAR_EVENT"); if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT) != NGX_OK) { goto bad_add; } if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_CLEAR_EVENT) != NGX_OK) { goto bad_add; } } else { dd("NGX_USE_LEVEL_EVENT"); if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) != NGX_OK) { goto bad_add; } if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT) != NGX_OK) { goto bad_add; } } pgxc->log->action = "connecting to PostgreSQL database"; pgdt->state = state_db_connect; dd("returning NGX_AGAIN"); return NGX_AGAIN; bad_add: ngx_log_error(NGX_LOG_ERR, pc->log, 0, "postgres: failed to add nginx connection"); invalid: ngx_postgres_upstream_free_connection(pc->log, pc->connection, pgdt->pgconn, pgscf); #if defined(nginx_version) && (nginx_version >= 8017) dd("returning NGX_ERROR"); return NGX_ERROR; #else failed: /* a bit hack-ish way to return error response (setup part) */ pc->connection = ngx_get_connection(0, pc->log); dd("returning NGX_AGAIN (NGX_ERROR)"); return NGX_AGAIN; #endif }
void ngx_postgres_keepalive_free_peer(ngx_peer_connection_t *pc, ngx_postgres_upstream_peer_data_t *pgp, ngx_postgres_upstream_srv_conf_t *pgscf, ngx_uint_t state) { ngx_postgres_keepalive_cache_t *item; ngx_queue_t *q; ngx_connection_t *c; ngx_http_upstream_t *u; dd("entering"); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "postgres: free keepalive peer"); if (state & NGX_PEER_FAILED) { pgp->failed = 1; } u = pgp->upstream; if ((!pgp->failed) && (pc->connection != NULL) && (u->headers_in.status_n == NGX_HTTP_OK)) { c = pc->connection; if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } if (c->write->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { if (ngx_del_event(c->write, NGX_WRITE_EVENT, 0) != NGX_OK) { return; } } pc->connection = NULL; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "postgres: free keepalive peer: saving connection %p", c); if (ngx_queue_empty(&pgscf->free)) { /* connection pool is already full */ q = ngx_queue_last(&pgscf->cache); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_postgres_keepalive_cache_t, queue); ngx_postgres_upstream_free_connection(pc->log, item->connection, item->pgconn, pgscf); } else { q = ngx_queue_head(&pgscf->free); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_postgres_keepalive_cache_t, queue); } item->connection = c; ngx_queue_insert_head(&pgscf->cache, q); c->write->handler = ngx_postgres_keepalive_dummy_handler; c->read->handler = ngx_postgres_keepalive_close_handler; c->data = item; c->idle = 1; c->log = ngx_cycle->log; #if defined(nginx_version) && (nginx_version >= 1001004) c->pool->log = ngx_cycle->log; #endif c->read->log = ngx_cycle->log; c->write->log = ngx_cycle->log; item->socklen = pc->socklen; ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); item->pgconn = pgp->pgconn; item->name.data = pgp->name.data; item->name.len = pgp->name.len; } dd("returning"); }