/* * send frontend exiting messages to all connections. this is called * in any case when child process exits, for example failover, child * life time expires or child max connections expires. */ static void send_frontend_exits(void) { int i; POOL_CONNECTION_POOL *p = pool_connection_pool; #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif POOL_SETMASK2(&BlockSig, &oldmask); for (i=0;i<pool_config->max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; pool_send_frontend_exits(p); } POOL_SETMASK(&oldmask); }
/* * set backend connection close timer */ void pool_connection_pool_timer(POOL_CONNECTION_POOL *backend) { POOL_CONNECTION_POOL *p = pool_connection_pool; int i; pool_debug("pool_connection_pool_timer: set close time %ld", time(NULL)); MASTER_CONNECTION(backend)->closetime = time(NULL); /* set connection close time */ if (pool_config->connection_life_time == 0) return; /* look for any other timeout */ for (i=0;i<pool_config->max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; if (p != backend && MASTER_CONNECTION(p)->closetime) return; } /* no other timer found. set my timer */ pool_debug("pool_connection_pool_timer: set alarm after %d seconds", pool_config->connection_life_time); pool_signal(SIGALRM, pool_backend_timer_handler); alarm(pool_config->connection_life_time); }
/* * find connection by user and database */ POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int protoMajor, int check_socket) { #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif int i; POOL_CONNECTION_POOL *p = pool_connection_pool; if (p == NULL) { pool_error("pool_get_cp: pool_connection_pool is not initialized"); return NULL; } POOL_SETMASK2(&BlockSig, &oldmask); for (i=0;i<pool_config.max_pool;i++) { if (MASTER_CONNECTION(p) && MASTER_CONNECTION(p)->sp->major == protoMajor && MASTER_CONNECTION(p)->sp->user != NULL && strcmp(MASTER_CONNECTION(p)->sp->user, user) == 0 && strcmp(MASTER_CONNECTION(p)->sp->database, database) == 0) { /* mark this connection is under use */ MASTER_CONNECTION(p)->closetime = 0; POOL_SETMASK(&oldmask); if (check_socket && (check_socket_status(MASTER(p)->fd) < 0 || (DUAL_MODE && check_socket_status(MASTER(p)->fd) < 0))) { pool_log("connection closed. retry to create new connection pool."); pool_free_startup_packet(MASTER_CONNECTION(p)->sp); pool_close(MASTER_CONNECTION(p)->con); free(MASTER_CONNECTION(p)); if (DUAL_MODE) { pool_close(SECONDARY_CONNECTION(p)->con); free(SECONDARY_CONNECTION(p)); } memset(p, 0, sizeof(POOL_CONNECTION_POOL)); return NULL; } return p; } p++; } POOL_SETMASK(&oldmask); return NULL; }
/* * Judge if we have to_regclass or not. */ bool pool_has_to_regclass(void) { /* * Query to know if to_regclass exists. */ #define HAS_TOREGCLASSQUERY "SELECT count(*) from (SELECT has_function_privilege('%s', 'pg_catalog.to_regclass(cstring)', 'execute') WHERE EXISTS(SELECT * FROM pg_catalog.pg_proc AS p WHERE p.proname = 'to_regclass')) AS s" bool result; static POOL_RELCACHE * relcache; POOL_CONNECTION_POOL *backend; char *user; backend = pool_get_session_context(false)->backend; user = MASTER_CONNECTION(backend)->sp->user; if (!relcache) { relcache = pool_create_relcache(pool_config->relcache_size, HAS_TOREGCLASSQUERY, int_register_func, int_unregister_func, false); if (relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking to_regclass presence"))); return false; } } result = pool_search_relcache(relcache, backend, user) == 0 ? 0 : 1; return result; }
/* * Judge if we have pgpool_regclass or not. */ bool pool_has_pgpool_regclass(void) { /* * Query to know if pgpool_regclass exists. */ #define HASPGPOOL_REGCLASSQUERY "SELECT count(*) from (SELECT has_function_privilege('%s', 'pgpool_regclass(cstring)', 'execute') WHERE EXISTS(SELECT * FROM pg_catalog.pg_proc AS p WHERE p.proname = 'pgpool_regclass')) AS s" bool result; static POOL_RELCACHE *relcache; POOL_CONNECTION_POOL *backend; char *user; backend = pool_get_session_context()->backend; user = MASTER_CONNECTION(backend)->sp->user; if (!relcache) { relcache = pool_create_relcache(pool_config->relcache_size, HASPGPOOL_REGCLASSQUERY, int_register_func, int_unregister_func, false); if (relcache == NULL) { pool_error("has_pgpool_regclass: pool_create_relcache error"); return false; } } result = pool_search_relcache(relcache, backend, user)==0?0:1; return result; }
/* * Create "ERROR" etc., timestamp, pid, user name string and return * it. The returned string is in a static buff and subsequent calls * will overwrite it. */ static char *optstring(int kind) { static char *kindstr[] = {"ERROR:", "DEBUG:", "LOG: "}; char timebuf[MAXSTRFTIME]; time_t now; static char optbuf[MAXSTRFTIME+7+8+NAMEDATALEN]; #ifndef POOL_TOOLS char username[NAMEDATALEN]; #endif char buf[128]; #ifndef POOL_TOOLS POOL_SESSION_CONTEXT *c; #endif optbuf[0] = '\0'; if (pool_config->print_timestamp) { now = time(NULL); strftime(timebuf, MAXSTRFTIME, "%Y-%m-%d %H:%M:%S ", localtime(&now)); strcat(optbuf, timebuf); } snprintf(buf, sizeof(buf), "%s pid: %d", kindstr[kind], (int)getpid()); strcat(optbuf, buf); #ifndef POOL_TOOLS if (pool_config->print_user) { if ((c = pool_get_session_context())) if (MASTER_CONNECTION(c->backend) && MASTER_CONNECTION(c->backend)->sp && MASTER_CONNECTION(c->backend)->sp->user) { strlcpy(username, MASTER_CONNECTION(c->backend)->sp->user, sizeof(username)); strcat(optbuf, " user: "); strcat(optbuf, username); } } #endif return optbuf; }
/* * signal handler for SIGUSR1 * close all idle connections */ static RETSIGTYPE close_idle_connection(int sig) { int i, j; POOL_CONNECTION_POOL *p = pool_connection_pool; ConnectionInfo *info; pool_debug("child receives close connection request"); for (j=0;j<pool_config->max_pool;j++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; if (MASTER_CONNECTION(p)->closetime > 0) /* idle connection? */ { pool_debug("close_idle_connection: close idle connection: user %s database %s", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_send_frontend_exits(p); for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; if (i == 0) { /* only first backend allocated the memory for the start up packet */ pool_free_startup_packet(CONNECTION_SLOT(p, i)->sp); } pool_close(CONNECTION(p, i)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo)); } } }
static POOL_CONNECTION_POOL *new_connection(POOL_CONNECTION_POOL *p) { /* create master connection */ MASTER_CONNECTION(p) = malloc(sizeof(POOL_CONNECTION_POOL_SLOT)); if (MASTER_CONNECTION(p) == NULL) { pool_error("pool_create_cp: malloc() failed"); return NULL; } create_cp(MASTER_CONNECTION(p), 0); /* initialize Paramter Status save structure */ if (pool_init_params(&MASTER(p)->params)) { return NULL; } p->num = 1; /* number of slots */ /* create secondary connection */ if (DUAL_MODE) { SECONDARY_CONNECTION(p) = malloc(sizeof(POOL_CONNECTION_POOL_SLOT)); if (SECONDARY_CONNECTION(p) == NULL) { pool_error("pool_create_cp: malloc() failed"); return NULL; } create_cp(SECONDARY_CONNECTION(p), 1); /* initialize Paramter Status save structure */ if (pool_init_params(&SECONDARY(p)->params)) { return NULL; } p->num++; /* number of slots */ } return p; }
/* * disconnect and release a connection to the database */ void pool_discard_cp(char *user, char *database, int protoMajor) { POOL_CONNECTION_POOL *p = pool_get_cp(user, database, protoMajor, 0); if (p == NULL) { pool_error("pool_discard_cp: cannot get connection pool for user %s datbase %s", user, database); return; } pool_free_startup_packet(MASTER_CONNECTION(p)->sp); pool_close(MASTER_CONNECTION(p)->con); free(MASTER_CONNECTION(p)); if (DUAL_MODE) { /* do not free memory! we did not allocate them */ pool_close(SECONDARY_CONNECTION(p)->con); free(SECONDARY_CONNECTION(p)); } memset(p, 0, sizeof(POOL_CONNECTION_POOL)); }
/* * Show ps idle status */ void pool_ps_idle_display(POOL_CONNECTION_POOL * backend) { StartupPacket *sp; char psbuf[1024]; sp = MASTER_CONNECTION(backend)->sp; if (MASTER(backend)->tstate == 'T') snprintf(psbuf, sizeof(psbuf), "%s %s %s idle in transaction", sp->user, sp->database, remote_ps_data); else snprintf(psbuf, sizeof(psbuf), "%s %s %s idle", sp->user, sp->database, remote_ps_data); set_ps_display(psbuf, false); }
/* * create a connection pool by user and database */ POOL_CONNECTION_POOL *pool_create_cp(void) { int i, freed = 0; time_t closetime; POOL_CONNECTION_POOL *oldestp; POOL_CONNECTION_POOL *ret; ConnectionInfo *info; POOL_CONNECTION_POOL *p = pool_connection_pool; if (p == NULL) { pool_error("pool_create_cp: pool_connection_pool is not initialized"); return NULL; } for (i=0;i<pool_config->max_pool;i++) { if (MASTER_CONNECTION(p) == NULL) { ret = new_connection(p); if (ret) pool_index = i; return ret; } p++; } pool_debug("no empty connection slot was found"); /* * no empty connection slot was found. look for the oldest connection and discard it. */ oldestp = p = pool_connection_pool; closetime = MASTER_CONNECTION(p)->closetime; pool_index = 0; for (i=0;i<pool_config->max_pool;i++) { pool_debug("user: %s database: %s closetime: %ld", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database, MASTER_CONNECTION(p)->closetime); if (MASTER_CONNECTION(p)->closetime < closetime) { closetime = MASTER_CONNECTION(p)->closetime; oldestp = p; pool_index = i; } p++; } p = oldestp; pool_send_frontend_exits(p); pool_debug("discarding old %zd th connection. user: %s database: %s", oldestp - pool_connection_pool, MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; if (!freed) { pool_free_startup_packet(CONNECTION_SLOT(p, i)->sp); freed = 1; } pool_close(CONNECTION(p, i)); free(CONNECTION_SLOT(p, i)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); ret = new_connection(p); return ret; }
/* * Send extended query and wait for response * send_type: * -1: do not send this node_id * 0: send to all nodes * >0: send to this node_id */ POOL_STATUS pool_extended_send_and_wait(POOL_QUERY_CONTEXT *query_context, char *kind, int len, char *contents, int send_type, int node_id) { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION *frontend; POOL_CONNECTION_POOL *backend; bool is_commit; bool is_begin_read_write; int i; int str_len; int rewritten_len; char *str; char *rewritten_begin; session_context = pool_get_session_context(); frontend = session_context->frontend; backend = session_context->backend; is_commit = is_commit_or_rollback_query(query_context->parse_tree); is_begin_read_write = false; str_len = 0; rewritten_len = 0; str = NULL; rewritten_begin = NULL; /* * If the query is BEGIN READ WRITE or * BEGIN ... SERIALIZABLE in master/slave mode, * we send BEGIN to slaves/standbys instead. * original_query which is BEGIN READ WRITE is sent to primary. * rewritten_query which is BEGIN is sent to standbys. */ if (pool_need_to_treat_as_if_default_transaction(query_context)) { is_begin_read_write = true; if (*kind == 'P') { rewritten_begin = remove_read_write(len, contents, &rewritten_len); if (rewritten_begin == NULL) return POOL_END; } } if (!rewritten_begin) { str_len = len; str = contents; } /* Send query */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; /* * If in reset context, we send COMMIT/ABORT to nodes those * are not in I(idle) state. This will ensure that * transactions are closed. */ if (is_commit && session_context->reset_context && TSTATE(backend, i) == 'I') { pool_unset_node_to_be_sent(query_context, i); continue; } if (rewritten_begin) { if (REAL_PRIMARY_NODE_ID == i) { str = contents; str_len = len; } else { str = rewritten_begin; str_len = rewritten_len; } } if (pool_config->log_per_node_statement) { char msgbuf[QUERY_STRING_BUFFER_LEN]; char *stmt; if (*kind == 'P' || *kind == 'E') { if (query_context->rewritten_query) { if (is_begin_read_write) { if (REAL_PRIMARY_NODE_ID == i) stmt = query_context->original_query; else stmt = query_context->rewritten_query; } else { stmt = query_context->rewritten_query; } } else { stmt = query_context->original_query; } if (*kind == 'P') snprintf(msgbuf, sizeof(msgbuf), "Parse: %s", stmt); else snprintf(msgbuf, sizeof(msgbuf), "Execute: %s", stmt); } else { snprintf(msgbuf, sizeof(msgbuf), "%c message", *kind); } per_node_statement_log(backend, i, msgbuf); } if (send_extended_protocol_message(backend, i, kind, str_len, str) != POOL_CONTINUE) { free(rewritten_begin); return POOL_END; } } if (!is_begin_read_write) { if (query_context->rewritten_query) str = query_context->rewritten_query; else str = query_context->original_query; } /* Wait for response */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; /* * If in master/slave mode, we do not send COMMIT/ABORT to * slaves/standbys if it's in I(idle) state. */ if (is_commit && MASTER_SLAVE && !IS_MASTER_NODE_ID(i) && TSTATE(backend, i) == 'I') { continue; } if (is_begin_read_write) { if (REAL_PRIMARY_NODE_ID == i) str = query_context->original_query; else str = query_context->rewritten_query; } if (wait_for_query_response(frontend, CONNECTION(backend, i), MAJOR(backend)) != POOL_CONTINUE) { /* Cancel current transaction */ CancelPacket cancel_packet; cancel_packet.protoVersion = htonl(PROTO_CANCEL); cancel_packet.pid = MASTER_CONNECTION(backend)->pid; cancel_packet.key= MASTER_CONNECTION(backend)->key; cancel_request(&cancel_packet); free(rewritten_begin); return POOL_END; } /* * Check if some error detected. If so, emit * log. This is usefull when invalid encoding error * occurs. In this case, PostgreSQL does not report * what statement caused that error and make users * confused. */ per_node_error_log(backend, i, str, "pool_send_and_wait: Error or notice message from backend: ", true); } free(rewritten_begin); return POOL_CONTINUE; }
/* * Send simple query and wait for response * send_type: * -1: do not send this node_id * 0: send to all nodes * >0: send to this node_id */ POOL_STATUS pool_send_and_wait(POOL_QUERY_CONTEXT *query_context, int send_type, int node_id) { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION *frontend; POOL_CONNECTION_POOL *backend; bool is_commit; bool is_begin_read_write; int i; int len; char *string; session_context = pool_get_session_context(); frontend = session_context->frontend; backend = session_context->backend; is_commit = is_commit_or_rollback_query(query_context->parse_tree); is_begin_read_write = false; len = 0; string = NULL; /* * If the query is BEGIN READ WRITE or * BEGIN ... SERIALIZABLE in master/slave mode, * we send BEGIN to slaves/standbys instead. * original_query which is BEGIN READ WRITE is sent to primary. * rewritten_query which is BEGIN is sent to standbys. */ if (pool_need_to_treat_as_if_default_transaction(query_context)) { is_begin_read_write = true; } else { if (query_context->rewritten_query) { len = query_context->rewritten_length; string = query_context->rewritten_query; } else { len = query_context->original_length; string = query_context->original_query; } } /* Send query */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; /* * If in master/slave mode, we do not send COMMIT/ABORT to * slaves/standbys if it's in I(idle) state. */ if (is_commit && MASTER_SLAVE && !IS_MASTER_NODE_ID(i) && TSTATE(backend, i) == 'I') { pool_unset_node_to_be_sent(query_context, i); continue; } /* * If in reset context, we send COMMIT/ABORT to nodes those * are not in I(idle) state. This will ensure that * transactions are closed. */ if (is_commit && session_context->reset_context && TSTATE(backend, i) == 'I') { pool_unset_node_to_be_sent(query_context, i); continue; } if (is_begin_read_write) { if (REAL_PRIMARY_NODE_ID == i) { len = query_context->original_length; string = query_context->original_query; } else { len = query_context->rewritten_length; string = query_context->rewritten_query; } } per_node_statement_log(backend, i, string); if (send_simplequery_message(CONNECTION(backend, i), len, string, MAJOR(backend)) != POOL_CONTINUE) { return POOL_END; } } /* Wait for response */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; #ifdef NOT_USED /* * If in master/slave mode, we do not send COMMIT/ABORT to * slaves/standbys if it's in I(idle) state. */ if (is_commit && MASTER_SLAVE && !IS_MASTER_NODE_ID(i) && TSTATE(backend, i) == 'I') { continue; } #endif if (is_begin_read_write) { if(REAL_PRIMARY_NODE_ID == i) string = query_context->original_query; else string = query_context->rewritten_query; } if (wait_for_query_response(frontend, CONNECTION(backend, i), MAJOR(backend)) != POOL_CONTINUE) { /* Cancel current transaction */ CancelPacket cancel_packet; cancel_packet.protoVersion = htonl(PROTO_CANCEL); cancel_packet.pid = MASTER_CONNECTION(backend)->pid; cancel_packet.key= MASTER_CONNECTION(backend)->key; cancel_request(&cancel_packet); return POOL_END; } /* * Check if some error detected. If so, emit * log. This is usefull when invalid encoding error * occurs. In this case, PostgreSQL does not report * what statement caused that error and make users * confused. */ per_node_error_log(backend, i, string, "pool_send_and_wait: Error or notice message from backend: ", true); } return POOL_CONTINUE; }
/* * child main loop */ void do_child(int unix_fd, int inet_fd) { POOL_CONNECTION *frontend; POOL_CONNECTION_POOL *backend; struct timeval now; struct timezone tz; struct timeval timeout; static int connected; /* non 0 if has been accepted connections from frontend */ int connections_count = 0; /* used if child_max_connections > 0 */ int found; char psbuf[NI_MAXHOST + 128]; pool_debug("I am %d", getpid()); /* Identify myself via ps */ init_ps_display("", "", "", ""); /* set up signal handlers */ signal(SIGALRM, SIG_DFL); signal(SIGTERM, die); signal(SIGINT, die); signal(SIGHUP, reload_config_handler); signal(SIGQUIT, die); signal(SIGCHLD, SIG_DFL); signal(SIGUSR1, close_idle_connection); signal(SIGUSR2, wakeup_handler); signal(SIGPIPE, SIG_IGN); #ifdef NONE_BLOCK /* set listen fds to none-blocking */ pool_set_nonblock(unix_fd); if (inet_fd) { pool_set_nonblock(inet_fd); } #endif /* Initialize my backend status */ pool_initialize_private_backend_status(); /* Initialize per process context */ pool_init_process_context(); /* initialize random seed */ gettimeofday(&now, &tz); #if defined(sun) || defined(__sun) srand((unsigned int) now.tv_usec); #else srandom((unsigned int) now.tv_usec); #endif /* initialize system db connection */ init_system_db_connection(); /* initialize connection pool */ if (pool_init_cp()) { child_exit(1); } /* * Open pool_passwd in child process. This is necessary to avoid the * file descriptor race condition reported in [pgpool-general: 1141]. */ if (strcmp("", pool_config->pool_passwd)) { pool_reopen_passwd_file(); } timeout.tv_sec = pool_config->child_life_time; timeout.tv_usec = 0; for (;;) { StartupPacket *sp; idle = 1; /* pgpool stop request already sent? */ check_stop_request(); /* Check if restart request is set because of failback event * happend. If so, exit myself with exit code 1 to be * restarted by pgpool parent. */ if (pool_get_my_process_info()->need_to_restart) { pool_log("do_child: failback event found. restart myself."); pool_get_my_process_info()->need_to_restart = 0; child_exit(1); } accepted = 0; /* perform accept() */ frontend = do_accept(unix_fd, inet_fd, &timeout); if (frontend == NULL) /* connection request from frontend timed out */ { /* check select() timeout */ if (connected && pool_config->child_life_time > 0 && timeout.tv_sec == 0 && timeout.tv_usec == 0) { pool_debug("child life %d seconds expired", pool_config->child_life_time); /* * Doesn't need to call this. child_exit() calls it. * send_frontend_exits(); */ child_exit(2); } continue; } /* set frontend fd to blocking */ pool_unset_nonblock(frontend->fd); /* reset busy flag */ idle = 0; /* check backend timer is expired */ if (backend_timer_expired) { pool_backend_timer(); backend_timer_expired = 0; } /* read the startup packet */ retry_startup: sp = read_startup_packet(frontend); if (sp == NULL) { /* failed to read the startup packet. return to the accept() loop */ pool_close(frontend); connection_count_down(); continue; } /* cancel request? */ if (sp->major == 1234 && sp->minor == 5678) { cancel_request((CancelPacket *)sp->startup_packet); pool_close(frontend); pool_free_startup_packet(sp); connection_count_down(); continue; } /* SSL? */ if (sp->major == 1234 && sp->minor == 5679 && !frontend->ssl_active) { pool_debug("SSLRequest from client"); pool_ssl_negotiate_serverclient(frontend); goto retry_startup; } if (pool_config->enable_pool_hba) { /* * do client authentication. * Note that ClientAuthentication does not return if frontend * was rejected; it simply terminates this process. */ frontend->protoVersion = sp->major; frontend->database = strdup(sp->database); if (frontend->database == NULL) { pool_error("do_child: strdup failed: %s\n", strerror(errno)); child_exit(1); } frontend->username = strdup(sp->user); if (frontend->username == NULL) { pool_error("do_child: strdup failed: %s\n", strerror(errno)); child_exit(1); } ClientAuthentication(frontend); } /* * Ok, negotiation with frontend has been done. Let's go to the * next step. Connect to backend if there's no existing * connection which can be reused by this frontend. * Authentication is also done in this step. */ /* Check if restart request is set because of failback event * happend. If so, close idle connections to backend and make * a new copy of backend status. */ if (pool_get_my_process_info()->need_to_restart) { pool_log("do_child: failback event found. discard existing connections"); pool_get_my_process_info()->need_to_restart = 0; close_idle_connection(0); pool_initialize_private_backend_status(); } /* * if there's no connection associated with user and database, * we need to connect to the backend and send the startup packet. */ /* look for existing connection */ found = 0; backend = pool_get_cp(sp->user, sp->database, sp->major, 1); if (backend != NULL) { found = 1; /* existing connection associated with same user/database/major found. * however we should make sure that the startup packet contents are identical. * OPTION data and others might be different. */ if (sp->len != MASTER_CONNECTION(backend)->sp->len) { pool_debug("do_child: connection exists but startup packet length is not identical"); found = 0; } else if(memcmp(sp->startup_packet, MASTER_CONNECTION(backend)->sp->startup_packet, sp->len) != 0) { pool_debug("do_child: connection exists but startup packet contents is not identical"); found = 0; } if (found == 0) { /* we need to discard existing connection since startup packet is different */ pool_discard_cp(sp->user, sp->database, sp->major); backend = NULL; } } if (backend == NULL) { /* create a new connection to backend */ if ((backend = connect_backend(sp, frontend)) == NULL) { connection_count_down(); continue; } } else { /* reuse existing connection */ if (!connect_using_existing_connection(frontend, backend, sp)) continue; } connected = 1; /* show ps status */ sp = MASTER_CONNECTION(backend)->sp; snprintf(psbuf, sizeof(psbuf), "%s %s %s idle", sp->user, sp->database, remote_ps_data); set_ps_display(psbuf, false); /* * Initialize per session context */ pool_init_session_context(frontend, backend); /* Mark this connection pool is connected from frontend */ pool_coninfo_set_frontend_connected(pool_get_process_context()->proc_id, pool_pool_index()); /* query process loop */ for (;;) { POOL_STATUS status; status = pool_process_query(frontend, backend, 0); sp = MASTER_CONNECTION(backend)->sp; switch (status) { /* client exits */ case POOL_END: /* * do not cache connection if: * pool_config->connection_cahe == 0 or * database name is template0, template1, postgres or regression */ if (pool_config->connection_cache == 0 || !strcmp(sp->database, "template0") || !strcmp(sp->database, "template1") || !strcmp(sp->database, "postgres") || !strcmp(sp->database, "regression")) { reset_connection(); pool_close(frontend); pool_send_frontend_exits(backend); pool_discard_cp(sp->user, sp->database, sp->major); } else { POOL_STATUS status1; /* send reset request to backend */ status1 = pool_process_query(frontend, backend, 1); pool_close(frontend); /* if we detect errors on resetting connection, we need to discard * this connection since it might be in unknown status */ if (status1 != POOL_CONTINUE) { pool_debug("error in resetting connections. discarding connection pools..."); pool_send_frontend_exits(backend); pool_discard_cp(sp->user, sp->database, sp->major); } else pool_connection_pool_timer(backend); } break; /* error occurred. discard backend connection pool and disconnect connection to the frontend */ case POOL_ERROR: pool_log("do_child: exits with status 1 due to error"); child_exit(1); break; /* fatal error occurred. just exit myself... */ case POOL_FATAL: notice_backend_error(1); child_exit(1); break; /* not implemented yet */ case POOL_IDLE: do_accept(unix_fd, inet_fd, &timeout); pool_debug("accept while idle"); break; default: break; } if (status != POOL_CONTINUE) break; } /* Destroy session context */ pool_session_context_destroy(); /* Mark this connection pool is not connected from frontend */ pool_coninfo_unset_frontend_connected(pool_get_process_context()->proc_id, pool_pool_index()); accepted = 0; connection_count_down(); timeout.tv_sec = pool_config->child_life_time; timeout.tv_usec = 0; /* increment queries counter if necessary */ if ( pool_config->child_max_connections > 0 ) connections_count++; /* check if maximum connections count for this child reached */ if ( ( pool_config->child_max_connections > 0 ) && ( connections_count >= pool_config->child_max_connections ) ) { pool_log("child exiting, %d connections reached", pool_config->child_max_connections); send_frontend_exits(); child_exit(2); } } child_exit(0); }
/* * Search relcache. If found, return user data. Otherwise return 0. * If not found in cache, do the query and store the result into cache and return it. */ void *pool_search_relcache(POOL_RELCACHE *relcache, POOL_CONNECTION_POOL *backend, char *table) { char *rel; char *dbname; int i; int maxrefcnt = INT_MAX; char query[1024]; POOL_SELECT_RESULT *res = NULL; int index = 0; /* Eliminate double quotes */ rel = malloc(strlen(table)+1); if (!rel) { pool_error("pool_search_relcache: malloc failed"); return NULL; } for(i=0;*table;table++) { if (*table != '"') rel[i++] = *table; } rel[i] = '\0'; /* Obtain database name */ dbname = MASTER_CONNECTION(backend)->sp->database; /* Look for cache first */ for (i=0;i<relcache->num;i++) { /* * If cache is session local, we need to check session id */ if (relcache->cache_is_session_local) { if (relcache->cache[i].session_id != LocalSessionId) continue; } if (strcasecmp(relcache->cache[i].dbname, dbname) == 0 && strcasecmp(relcache->cache[i].relname, rel) == 0) { /* Found */ if (relcache->cache[i].refcnt < INT_MAX) relcache->cache[i].refcnt++; free(rel); return relcache->cache[i].data; } } /* Not in cache. Check the system catalog */ snprintf(query, sizeof(query), relcache->sql, rel); per_node_statement_log(backend, MASTER_NODE_ID, query); if (do_query(MASTER(backend), query, &res) != POOL_CONTINUE) { pool_error("pool_search_relcache: do_query failed"); if (res) free_select_result(res); free(rel); return NULL; } /* * Look for replacement in cache */ for (i=0;i<relcache->num;i++) { /* * If cache is session local, we can discard old cache immediately */ if (relcache->cache_is_session_local) { if (relcache->cache[i].session_id != LocalSessionId) { index = i; break; } } if (relcache->cache[i].refcnt == 0) { /* Found empty slot */ index = i; break; } else if (relcache->cache[i].refcnt < maxrefcnt) { maxrefcnt = relcache->cache[i].refcnt; index = i; } } /* Register cache */ strncpy(relcache->cache[index].dbname, dbname, MAX_ITEM_LENGTH); strncpy(relcache->cache[index].relname, rel, MAX_ITEM_LENGTH); relcache->cache[index].refcnt = 1; relcache->cache[index].session_id = LocalSessionId; free(rel); /* * Call user defined unregister/register fuction. */ (*relcache->unregister_func)(relcache->cache[index].data); relcache->cache[index].data = (*relcache->register_func)(res); free_select_result(res); return relcache->cache[index].data; }
void pool_backend_timer(void) { #define TMINTMAX 0x7fffffff POOL_CONNECTION_POOL *p = pool_connection_pool; int i, j; time_t now; time_t nearest = TMINTMAX; ConnectionInfo *info; POOL_SETMASK(&BlockSig); now = time(NULL); pool_debug("pool_backend_timer_handler called at %ld", now); for (i=0;i<pool_config->max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; /* timer expire? */ if (MASTER_CONNECTION(p)->closetime) { int freed = 0; pool_debug("pool_backend_timer_handler: expire time: %ld", MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time); if (now >= (MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time)) { /* discard expired connection */ pool_debug("pool_backend_timer_handler: expires user %s database %s", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_send_frontend_exits(p); for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j)) continue; if (!freed) { pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp); freed = 1; } pool_close(CONNECTION(p, j)); free(CONNECTION_SLOT(p, j)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); /* prepare to shutdown connections to system db */ if(pool_config->system_db_dynamic_connection && (pool_config->parallel_mode || pool_config->enable_query_cache)) { if (system_db_info->pgconn) pool_close_libpq_connection(); if (pool_system_db_connection() && pool_system_db_connection()->con) { pool_send_frontend_exit(pool_system_db_connection()->con); pool_close(pool_system_db_connection()->con); } if( system_db_info->connection ) { free( system_db_info->connection ); memset(system_db_info->connection, 0, sizeof(POOL_CONNECTION_POOL_SLOT)); system_db_info->connection = NULL; } } } else { /* look for nearest timer */ if (MASTER_CONNECTION(p)->closetime < nearest) nearest = MASTER_CONNECTION(p)->closetime; } } } /* any remaining timer */ if (nearest != TMINTMAX) { nearest = pool_config->connection_life_time - (now - nearest); if (nearest <= 0) nearest = 1; pool_signal(SIGALRM, pool_backend_timer_handler); alarm(nearest); } POOL_SETMASK(&UnBlockSig); }
/* * create a connection pool by user and database */ POOL_CONNECTION_POOL *pool_create_cp(void) { int i; time_t closetime; POOL_CONNECTION_POOL *oldestp; POOL_CONNECTION_POOL *p = pool_connection_pool; if (p == NULL) { pool_error("pool_create_cp: pool_connection_pool is not initialized"); return NULL; } for (i=0;i<pool_config.max_pool;i++) { if (MASTER_CONNECTION(p) == NULL) return new_connection(p); p++; } pool_debug("no empty connection slot was found"); /* * no empty connection slot was found. look for the oldest connection and discard it. */ oldestp = p = pool_connection_pool; closetime = MASTER_CONNECTION(p)->closetime; for (i=0;i<pool_config.max_pool;i++) { pool_debug("user: %s database: %s closetime: %d", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database, MASTER_CONNECTION(p)->closetime); if (MASTER_CONNECTION(p)->closetime < closetime) { closetime = MASTER_CONNECTION(p)->closetime; oldestp = p; } p++; } p = oldestp; pool_send_frontend_exits(p); pool_debug("discarding old %d th connection. user: %s database: %s", oldestp - pool_connection_pool, MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_free_startup_packet(MASTER_CONNECTION(p)->sp); pool_close(MASTER_CONNECTION(p)->con); free(MASTER_CONNECTION(p)); if (DUAL_MODE) { /* do not free memory! we did not allocate them */ pool_close(SECONDARY_CONNECTION(p)->con); free(SECONDARY_CONNECTION(p)); } memset(p, 0, sizeof(POOL_CONNECTION_POOL)); return new_connection(p); }
void pool_backend_timer(void) { #define TMINTMAX 0x7fffffff POOL_CONNECTION_POOL *p = pool_connection_pool; int i; time_t now; time_t nearest = TMINTMAX; POOL_SETMASK(&BlockSig); now = time(NULL); pool_debug("pool_backend_timer_handler called at %d", now); for (i=0;i<pool_config.max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; /* timer expire? */ if (MASTER_CONNECTION(p)->closetime) { pool_debug("pool_backend_timer_handler: expire time: %d", MASTER_CONNECTION(p)->closetime+pool_config.connection_life_time); if (now >= (MASTER_CONNECTION(p)->closetime+pool_config.connection_life_time)) { /* discard expired connection */ pool_debug("pool_backend_timer_handler: expires user %s database %s", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_send_frontend_exits(p); pool_free_startup_packet(MASTER_CONNECTION(p)->sp); pool_close(MASTER_CONNECTION(p)->con); free(MASTER_CONNECTION(p)); if (DUAL_MODE) { pool_close(SECONDARY_CONNECTION(p)->con); free(SECONDARY_CONNECTION(p)); } memset(p, 0, sizeof(POOL_CONNECTION_POOL)); } else { /* look for nearest timer */ if (MASTER_CONNECTION(p)->closetime < nearest) nearest = MASTER_CONNECTION(p)->closetime; } } } /* any remaining timer */ if (nearest != TMINTMAX) { nearest = pool_config.connection_life_time - (now - nearest); if (nearest <= 0) nearest = 1; pool_signal(SIGALRM, pool_backend_timer_handler); alarm(nearest); } POOL_SETMASK(&UnBlockSig); }
/* * find connection by user and database */ POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int protoMajor, int check_socket) { #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif int i, freed = 0; ConnectionInfo *info; POOL_CONNECTION_POOL *p = pool_connection_pool; if (p == NULL) { pool_error("pool_get_cp: pool_connection_pool is not initialized"); return NULL; } POOL_SETMASK2(&BlockSig, &oldmask); for (i=0;i<pool_config->max_pool;i++) { if (MASTER_CONNECTION(p) && MASTER_CONNECTION(p)->sp && MASTER_CONNECTION(p)->sp->major == protoMajor && MASTER_CONNECTION(p)->sp->user != NULL && strcmp(MASTER_CONNECTION(p)->sp->user, user) == 0 && strcmp(MASTER_CONNECTION(p)->sp->database, database) == 0) { int sock_broken = 0; int j; /* mark this connection is under use */ MASTER_CONNECTION(p)->closetime = 0; for (j=0;j<NUM_BACKENDS;j++) { p->info[j].counter++; } POOL_SETMASK(&oldmask); if (check_socket) { for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j)) continue; if (CONNECTION_SLOT(p, j)) { sock_broken = check_socket_status(CONNECTION(p, j)->fd); if (sock_broken < 0) break; } else { sock_broken = -1; break; } } if (sock_broken < 0) { pool_log("connection closed. retry to create new connection pool."); for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j) || (CONNECTION_SLOT(p, j) == NULL)) continue; if (!freed) { pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp); freed = 1; } pool_close(CONNECTION(p, j)); free(CONNECTION_SLOT(p, j)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL_SLOT)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); POOL_SETMASK(&oldmask); return NULL; } } POOL_SETMASK(&oldmask); pool_index = i; return p; } p++; } POOL_SETMASK(&oldmask); return NULL; }
/* * Reuse existing connection */ static bool connect_using_existing_connection(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, StartupPacket *sp) { int i, freed = 0; /* * Save startup packet info */ for (i = 0; i < NUM_BACKENDS; i++) { if (VALID_BACKEND(i)) { if (!freed) { pool_free_startup_packet(backend->slots[i]->sp); freed = 1; } backend->slots[i]->sp = sp; } } /* Reuse existing connection to backend */ if (pool_do_reauth(frontend, backend)) { pool_close(frontend); connection_count_down(); return false; } if (MAJOR(backend) == 3) { char command_buf[1024]; /* If we have received application_name in the start up * packet, we send SET command to backend. Also we add or * replace existing application_name data. */ if (sp->application_name) { snprintf(command_buf, sizeof(command_buf), "SET application_name TO '%s'", sp->application_name); for (i=0;i<NUM_BACKENDS;i++) { if (VALID_BACKEND(i)) if (do_command(frontend, CONNECTION(backend, i), command_buf, MAJOR(backend), MASTER_CONNECTION(backend)->pid, MASTER_CONNECTION(backend)->key, 0) != POOL_CONTINUE) { pool_error("connect_using_existing_connection: do_command failed. command: %s", command_buf); return false; } } pool_add_param(&MASTER(backend)->params, "application_name", sp->application_name); } if (send_params(frontend, backend)) { pool_close(frontend); connection_count_down(); return false; } } /* Send ReadyForQuery to frontend */ pool_write(frontend, "Z", 1); if (MAJOR(backend) == 3) { int len; char tstate; len = htonl(5); pool_write(frontend, &len, sizeof(len)); tstate = TSTATE(backend, MASTER_NODE_ID); pool_write(frontend, &tstate, 1); } if (pool_flush(frontend) < 0) { pool_close(frontend); connection_count_down(); return false; } return true; }