From 1401e5430917064d4f36269e5d6f71a3d19e9fcc Mon Sep 17 00:00:00 2001 From: Muhammad Usama Date: Mon, 5 Aug 2024 11:54:42 +0500 Subject: [PATCH 1/2] Implement Global Connection Pooling in Pgpool-II This commit introduces a global connection pooling feature to Pgpool-II, addressing several shortcomings of the traditional local connection pooling. Key Points: - Global Connection Pooling: The main process now maintains a global connection pool in shared memory, leasing pooled connections to child processes as needed. - New Configuration Parameters: - `connection_pool_type`: Select between classic and global connection pool strategies. - `max_pool_size`: Configure the size of the global connection pool. - Efficient Data Transfer: Only sockets are transferred via IPC sockets, with other connection data read and written directly in shared memory by child processes. - Minimal Locking: The main process moderates pooled connections, eliminating the need for locks on the connection pool. - Failover Handling: The main process marks affected connections as `need_cleanup`, with cleanup processed by child processes. - New Backend Node Addition: Child processes handle missing backend node connections based on the `backend_status` of each node. - Consistent Interface: The internal connection pool interface remains consistent for both classic and global pooling. - libancillary is used under the hood to transfer the file descriptors https://www.normalesup.org/~george/comp/libancillary/ Additional Changes: - Moved all connection pool-related data to shared memory. - Updated and renamed connection-related data structures for clarity. - Fixed compilation issues with `cassert` enabled. Benefits: - Reduced `max_connections` required on PostgreSQL to serve the same number of application connections, resulting in overall positive performance impact. - Eliminated wasted pooled connections. - Efficiently used pooled connections with precise information about required backend connections. - Architectural support for implementing a transaction-pooling mode. - Potential for pre-warm pool feature implementation. TODOs: - Handle `drop database` command. - Implement recycling of idle database connections. - Restructure SSL context handling for pooled connections. - Complete documentation and remove debugging or dead code. This commit sets the stage for a more efficient and flexible connection pooling mechanism in Pgpool-II, enhancing its performance and capabilities. --- src/Makefile.am | 10 +- src/auth/pool_auth.c | 279 +++--- src/auth/pool_hba.c | 4 +- src/config/pool_config_variables.c | 67 +- src/connection_pool/backend_connection.c | 401 +++++++++ src/connection_pool/classic_connection_pool.c | 641 ++++++++++++++ src/connection_pool/connection_pool.c | 379 +++++++++ src/connection_pool/global_connection_pool.c | 792 ++++++++++++++++++ src/context/pool_process_context.c | 211 +---- src/context/pool_query_context.c | 16 +- src/context/pool_session_context.c | 31 +- src/include/auth/pool_auth.h | 10 +- src/include/auth/pool_hba.h | 1 + .../connection_pool/backend_connection.h | 217 +++++ src/include/connection_pool/connection_pool.h | 114 +++ src/include/context/pool_process_context.h | 15 +- src/include/context/pool_query_context.h | 3 +- src/include/context/pool_session_context.h | 5 +- src/include/main/pgpool_ipc.h | 47 ++ src/include/parser/pool_parser.h | 15 - src/include/pcp/libpcp_ext.h | 14 +- src/include/pool.h | 213 ++--- src/include/pool_config.h | 9 + src/include/pool_config_variables.h | 9 +- src/include/pool_type.h | 17 +- src/include/protocol/pool_connection_pool.h | 28 +- src/include/protocol/pool_pg_utils.h | 16 +- src/include/protocol/pool_process_query.h | 18 +- src/include/protocol/pool_proto_modules.h | 98 +-- src/include/query_cache/pool_memqcache.h | 8 +- src/include/rewrite/pool_lobj.h | 3 +- src/include/rewrite/pool_timestamp.h | 4 +- src/include/utils/ancillary/ancillary.h | 123 +++ src/include/utils/memdebug.h | 29 + src/include/utils/pool_params.h | 16 +- src/include/utils/pool_process_reporting.h | 26 +- src/include/utils/pool_relcache.h | 4 +- src/include/utils/pool_ssl.h | 2 + src/include/utils/pool_stream.h | 4 +- src/include/utils/ps_status.h | 3 +- src/main/health_check.c | 2 +- src/main/pgpool_ipc.c | 447 ++++++++++ src/main/pgpool_main.c | 592 +++++++------ src/parser/Makefile.am | 2 +- src/parser/list.c | 39 +- src/protocol/CommandComplete.c | 8 +- src/protocol/child.c | 438 +++++----- src/protocol/pool_connection_pool.c | 595 ++----------- src/protocol/pool_pg_utils.c | 104 ++- src/protocol/pool_process_query.c | 128 ++- src/protocol/pool_proto2.c | 20 +- src/protocol/pool_proto_modules.c | 118 ++- src/query_cache/pool_memqcache.c | 30 +- src/rewrite/pool_lobj.c | 6 +- src/rewrite/pool_timestamp.c | 14 +- src/sample/pgpool.conf.sample-stream | 15 + src/streaming_replication/pool_worker_child.c | 5 +- .../010.rewrite_timestamp/timestamp/main.c | 8 +- src/utils/ancillary/fd_recv.c | 98 +++ src/utils/ancillary/fd_send.c | 92 ++ src/utils/mmgr/mcxt.c | 3 + src/utils/pool_params.c | 59 +- src/utils/pool_process_reporting.c | 396 ++++----- src/utils/pool_relcache.c | 8 +- src/utils/pool_select_walker.c | 20 +- src/utils/pool_stream.c | 27 +- src/utils/ps_status.c | 4 +- src/watchdog/watchdog.c | 2 + src/watchdog/wd_json_data.c | 8 +- 69 files changed, 5064 insertions(+), 2126 deletions(-) create mode 100644 src/connection_pool/backend_connection.c create mode 100644 src/connection_pool/classic_connection_pool.c create mode 100644 src/connection_pool/connection_pool.c create mode 100644 src/connection_pool/global_connection_pool.c create mode 100644 src/include/connection_pool/backend_connection.h create mode 100644 src/include/connection_pool/connection_pool.h create mode 100644 src/include/main/pgpool_ipc.h create mode 100644 src/include/utils/ancillary/ancillary.h create mode 100644 src/main/pgpool_ipc.c create mode 100644 src/utils/ancillary/fd_recv.c create mode 100644 src/utils/ancillary/fd_send.c diff --git a/src/Makefile.am b/src/Makefile.am index e32c4126..42fe0325 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ pgpool_SOURCES = main/main.c \ main/health_check.c \ main/pool_internal_comms.c \ main/pgpool_logger.c \ + main/pgpool_ipc.c \ config/pool_config.l \ config/pool_config_variables.c \ pcp_con/pcp_child.c \ @@ -34,6 +35,8 @@ pgpool_SOURCES = main/main.c \ streaming_replication/pool_worker_child.c \ rewrite/pool_timestamp.c \ rewrite/pool_lobj.c \ + utils/ancillary/fd_recv.c \ + utils/ancillary/fd_send.c \ utils/pool_select_walker.c \ utils/strlcpy.c \ utils/psprintf.c \ @@ -65,7 +68,12 @@ pgpool_SOURCES = main/main.c \ utils/statistics.c \ utils/pool_health_check_stats.c \ utils/psqlscan.l \ - utils/pgstrcasecmp.c + utils/pgstrcasecmp.c \ + connection_pool/backend_connection.c \ + connection_pool/connection_pool.c \ + connection_pool/global_connection_pool.c \ + connection_pool/classic_connection_pool.c + utils/psqlscan.c: utils/psqlscan.l $(LEX) -o'utils/psqlscan.c' $< diff --git a/src/auth/pool_auth.c b/src/auth/pool_auth.c index 862e2f73..1fa6a17f 100644 --- a/src/auth/pool_auth.c +++ b/src/auth/pool_auth.c @@ -63,7 +63,7 @@ static POOL_STATUS pool_send_backend_key_data(POOL_CONNECTION * frontend, int pid, int key, int protoMajor); static int do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor); -static void pool_send_auth_fail(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp); +static void pool_send_auth_fail(POOL_CONNECTION * frontend, BackendClusterConnection * cp); static int do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor); static int do_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor, char *storedPassword, PasswordType passwordType); @@ -87,11 +87,11 @@ static bool get_auth_password(POOL_CONNECTION * backend, POOL_CONNECTION * front char **password, PasswordType *passwordType); /* - * Do authentication. Assuming the only caller is - * make_persistent_db_connection(). + * Do authentication. Assuming the caller is + * make_persistent_db_connection() when. */ -void -connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) +void connection_do_auth(BackendClusterConnection *backend_con, int slot_no, + BackendNodeConnection *con_slot, POOL_CONNECTION *frontend, char *password, StartupPacket *sp) { char kind; int length; @@ -101,6 +101,9 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) int pid, key; bool keydata_done; + bool pooled_con = false; + PasswordType passwordType = PASSWORD_TYPE_UNKNOWN; + BackendNodeConnection *cp = backend_con ? cp = &backend_con->slots[slot_no] : con_slot; /* * read kind expecting 'R' packet (authentication response) @@ -115,7 +118,7 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) if (kind == 'E' || kind == 'N') { - sts = pool_extract_error_message(false, cp->con, cp->sp->major, false, &msg); + sts = pool_extract_error_message(false, cp->con, sp->major, false, &msg); } if (sts == 1) /* succeeded in extracting error/notice @@ -130,6 +133,52 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) (errmsg("failed to authenticate"), errdetail("invalid authentication message response type, Expecting 'R' and received '%c'", kind))); } + if (backend_con) + { + if (backend_con->backend_end_point->pwd_size <= 0) + { + passwordType = PASSWORD_TYPE_UNKNOWN; + password = ""; + /* Read the password from pool_passwd ?? */ + get_auth_password(cp->con, frontend, 1, + &password, &passwordType); + } + else + { + passwordType = backend_con->backend_end_point->passwordType; + password = backend_con->backend_end_point->password; + } + cp = &backend_con->slots[slot_no]; + + if (passwordType == PASSWORD_TYPE_AES) + { + /* + * decrypt the stored AES password for comparing it + */ + password = get_decrypted_password(backend_con->backend_end_point->password); + if (password == NULL) + ereport(ERROR, + (errmsg("backend:%d authentication failed", slot_no), + errdetail("unable to decrypt password from pool_passwd"), + errhint("verify the valid pool_key exists"))); + /* we have converted the password to plain text */ + passwordType = PASSWORD_TYPE_PLAINTEXT; + } + pooled_con = true; + } + else + { + /* If password is prefixed with md5 treat it as md5 Password */ + if (!strncmp("md5", password, 3) && (strlen(password) - 3) == MD5_PASSWD_LEN) + { + password = password + 3; + passwordType = PASSWORD_TYPE_MD5; + } + else + { + passwordType = PASSWORD_TYPE_PLAINTEXT; + } + } /* read message length */ pool_read_with_error(cp->con, &length, sizeof(length), @@ -153,7 +202,7 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) kind = send_password_packet(cp->con, PROTO_MAJOR_V3, password); if (kind != AUTH_REQ_OK) ereport(ERROR, - (errmsg("password authentication failed for user:%s", cp->sp->user), + (errmsg("password authentication failed for user:%s", sp->user), errdetail("backend replied with invalid kind"))); cp->con->auth_kind = AUTH_REQ_OK; @@ -169,14 +218,14 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) crypt_password = crypt(password, salt); if (crypt_password == NULL) ereport(ERROR, - (errmsg("crypt authentication failed for user:%s", cp->sp->user), + (errmsg("crypt authentication failed for user:%s", sp->user), errdetail("failed to encrypt the password"))); /* Send password packet to backend and receive auth response */ kind = send_password_packet(cp->con, PROTO_MAJOR_V3, crypt_password); if (kind != AUTH_REQ_OK) ereport(ERROR, - (errmsg("crypt authentication failed for user:%s", cp->sp->user), + (errmsg("crypt authentication failed for user:%s", sp->user), errdetail("backend replied with invalid kind"))); cp->con->auth_kind = AUTH_REQ_OK; @@ -194,14 +243,14 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) /* set buffer address for building md5 password */ buf1 = buf + MD5_PASSWD_LEN + 4; - /* - * If the supplied password is already in md5 hash format, we just - * copy it. Otherwise calculate the md5 hash value. - */ - if (!strncmp("md5", password, 3) && (strlen(password) - 3) == MD5_PASSWD_LEN) - memcpy(buf1, password + 3, MD5_PASSWD_LEN + 1); + if (passwordType == PASSWORD_TYPE_PLAINTEXT) + pool_md5_encrypt(password, sp->user, strlen(sp->user), buf1); + else if (passwordType == PASSWORD_TYPE_MD5) + memcpy(buf1, password, MD5_PASSWD_LEN + 1); else - pool_md5_encrypt(password, cp->sp->user, strlen(cp->sp->user), buf1); + ereport(ERROR, + (errmsg("md5 authentication failed for user:%s", sp->user), + errdetail("invalid password type"))); pool_md5_encrypt(buf1, salt, 4, buf + 3); memcpy(buf, "md5", 3); @@ -211,21 +260,26 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) pfree(buf); if (kind != AUTH_REQ_OK) ereport(ERROR, - (errmsg("md5 authentication failed for user:%s", cp->sp->user), + (errmsg("md5 authentication failed for user:%s", sp->user), errdetail("backend replied with invalid kind"))); cp->con->auth_kind = AUTH_REQ_OK; } else if (auth_kind == AUTH_REQ_SASL) { - if (do_SCRAM(NULL, cp->con, PROTO_MAJOR_V3, length, cp->sp->user, password, PASSWORD_TYPE_PLAINTEXT) == false) + if (passwordType != PASSWORD_TYPE_PLAINTEXT) + ereport(ERROR, + (errmsg("SCRAM authentication failed for user:%s", sp->user), + errdetail("invalid password type"))); + + if (do_SCRAM(NULL, cp->con, PROTO_MAJOR_V3, length, sp->user, password, PASSWORD_TYPE_PLAINTEXT) == false) { ereport(ERROR, (errmsg("failed to authenticate with backend"), - errdetail("SCRAM authentication failed for user:%s", cp->sp->user))); + errdetail("SCRAM authentication failed for user:%s", sp->user))); } ereport(DEBUG1, - (errmsg("SCRAM authentication successful for user:%s", cp->sp->user))); + (errmsg("SCRAM authentication successful for user:%s", sp->user))); cp->con->auth_kind = AUTH_REQ_OK; } else @@ -333,7 +387,7 @@ connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password) * 0. */ int -pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) +pool_do_auth(POOL_CONNECTION * frontend, BackendClusterConnection * cp) { signed char kind; int pid; @@ -343,10 +397,8 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) int authkind; int i; int message_length = 0; - StartupPacket *sp; - - protoMajor = MAIN_CONNECTION(cp)->sp->major; + protoMajor = cp->sp->major; kind = pool_read_kind(cp); if (kind < 0) @@ -420,6 +472,9 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) (errmsg("authentication backend"), errdetail("auth kind:%d", authkind))); /* trust? */ + + SaveAuthKindForBackendConnection(authkind); + if (authkind == AUTH_REQ_OK) { int msglen; @@ -434,7 +489,6 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) msglen = htonl(0); pool_write_and_flush(frontend, &msglen, sizeof(msglen)); - MAIN(cp)->auth_kind = AUTH_REQ_OK; } /* clear text password authentication? */ @@ -759,7 +813,6 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) /* * OK, read pid and secret key */ - sp = MAIN_CONNECTION(cp)->sp; pid = -1; for (i = 0; i < NUM_BACKENDS; i++) @@ -773,12 +826,9 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) (errmsg("authentication failed"), errdetail("failed to read pid in slot %d", i))); } - ereport(DEBUG1, - (errmsg("authentication backend"), - errdetail("cp->info[i]:%p pid:%u", &cp->info[i], ntohl(pid)))); - - CONNECTION_SLOT(cp, i)->pid = cp->info[i].pid = pid; + CONNECTION_SLOT(cp, i).pid = pid; + cp->slots[i].pid = pid; /* read key */ if (pool_read(CONNECTION(cp, i), &key, sizeof(key)) < 0) { @@ -786,15 +836,12 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) (errmsg("authentication failed"), errdetail("failed to read key in slot %d", i))); } - CONNECTION_SLOT(cp, i)->key = cp->info[i].key = key; - - cp->info[i].major = sp->major; - cp->info[i].minor = sp->minor; - strlcpy(cp->info[i].database, sp->database, sizeof(cp->info[i].database)); - strlcpy(cp->info[i].user, sp->user, sizeof(cp->info[i].user)); - cp->info[i].counter = 1; - CONNECTION(cp, i)->con_info = &cp->info[i]; - cp->info[i].swallow_termination = 0; + CONNECTION_SLOT(cp, i).key = key; + cp->slots[i].key = key; + + ereport(LOG,(errmsg("pool_id:%d slot:%d pid=%d key=%d backend_endpoint:%p",cp->pool_id, i, pid,key,cp->backend_end_point))); + + cp->backend_end_point->conn_slots[i].swallow_termination = 0; } } @@ -815,19 +862,19 @@ pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) * do re-authentication for reused connection. if success return 0 otherwise throws ereport. */ int -pool_do_reauth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) +pool_do_reauth(POOL_CONNECTION * frontend, BackendClusterConnection * cp) { - int protoMajor; + int protoMajor = MAJOR(cp); + int authkind = GetAuthKindForCurrentPoolBackendConnection(); int msglen; - protoMajor = MAJOR(cp); - /* * if hba is enabled we would already have passed authentication */ if (!frontend->frontend_authenticated) { - switch (MAIN(cp)->auth_kind) + + switch (authkind) { case AUTH_REQ_OK: /* trust */ @@ -854,7 +901,7 @@ pool_do_reauth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) default: ereport(ERROR, (errmsg("authentication failed"), - errdetail("unknown authentication request code %d", MAIN(cp)->auth_kind))); + errdetail("unknown authentication request code %d", authkind))); } } @@ -868,7 +915,7 @@ pool_do_reauth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) msglen = htonl(0); pool_write_and_flush(frontend, &msglen, sizeof(msglen)); - pool_send_backend_key_data(frontend, MAIN_CONNECTION(cp)->pid, MAIN_CONNECTION(cp)->key, protoMajor); + pool_send_backend_key_data(frontend, MAIN_CONNECTION(cp).pid, MAIN_CONNECTION(cp).key, protoMajor); return 0; } @@ -876,7 +923,7 @@ pool_do_reauth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) * send authentication failure message text to frontend */ static void -pool_send_auth_fail(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) +pool_send_auth_fail(POOL_CONNECTION * frontend, BackendClusterConnection * cp) { int messagelen; char *errmessage; @@ -886,11 +933,11 @@ pool_send_auth_fail(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp) protoMajor = MAJOR(cp); - messagelen = strlen(MAIN_CONNECTION(cp)->sp->user) + 100; + messagelen = strlen(cp->sp->user) + 100; errmessage = (char *) palloc(messagelen + 1); snprintf(errmessage, messagelen, "password authentication failed for user \"%s\"", - MAIN_CONNECTION(cp)->sp->user); + cp->sp->user); if (send_error_to_frontend) pool_send_fatal_message(frontend, protoMajor, "XX000", errmessage, "", "", __FILE__, __LINE__); @@ -1083,16 +1130,26 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in /* connection reusing? */ if (reauth) { - if (size != backend->pwd_size) + PooledBackendClusterConnection *backend_endpoint = GetCurrentPooledBackendClusterConnection(); + if (!backend_endpoint || backend_endpoint->pwd_size <= 0) + { ereport(ERROR, (errmsg("clear text password authentication failed"), - errdetail("password size does not match"))); + errdetail("password not found in the pooled connection"))); + } + else + { + if (size != backend_endpoint->pwd_size) + ereport(ERROR, + (errmsg("clear text password authentication failed"), + errdetail("password size does not match"))); - if (memcmp(pwd, backend->password, backend->pwd_size) != 0) - ereport(ERROR, - (errmsg("clear text password authentication failed"), - errdetail("password does not match"))); + if (memcmp(pwd, backend_endpoint->password, backend_endpoint->pwd_size) != 0) + ereport(ERROR, + (errmsg("clear text password authentication failed"), + errdetail("password does not match"))); + } return 0; } @@ -1104,12 +1161,9 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in if (IS_MAIN_NODE_ID(backend->db_node_id)) { send_auth_ok(frontend, protoMajor); + StorePasswordInformation( pwd, size, PASSWORD_TYPE_PLAINTEXT); } - backend->auth_kind = AUTH_REQ_PASSWORD; - backend->pwd_size = size; - memcpy(backend->password, pwd, backend->pwd_size); - backend->passwordType = PASSWORD_TYPE_PLAINTEXT; } return kind; } @@ -1120,7 +1174,7 @@ do_clear_text_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, in static int do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int protoMajor) { - char salt[2]; + char salt[2] = {0,0}; static int size; static char password[MAX_PASSWORD_SIZE]; char response; @@ -1132,9 +1186,9 @@ do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int /* read salt */ pool_read(backend, salt, sizeof(salt)); } - else + else if (backend->pooled_backend_ref) { - memcpy(salt, backend->salt, sizeof(salt)); + memcpy(salt, backend->pooled_backend_ref->salt, sizeof(salt)); } /* main? */ @@ -1183,21 +1237,31 @@ do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int /* connection reusing? */ if (reauth) { - ereport(DEBUG1, - (errmsg("performing crypt authentication"), - errdetail("size: %d saved_size: %d", (ntohl(size) - 4), backend->pwd_size))); - - if ((ntohl(size) - 4) != backend->pwd_size) + PooledBackendClusterConnection *backend_endpoint = GetCurrentPooledBackendClusterConnection(); + if (!backend_endpoint || backend_endpoint->pwd_size <= 0) + { ereport(ERROR, (errmsg("crypt authentication failed"), - errdetail("password size does not match"))); + errdetail("password not found in the pooled connection"))); + } + else + { + ereport(DEBUG1, + (errmsg("performing crypt authentication"), + errdetail("size: %d saved_size: %d", (ntohl(size) - 4), backend_endpoint->pwd_size))); + + if ((ntohl(size) - 4) != backend_endpoint->pwd_size) + ereport(ERROR, + (errmsg("crypt authentication failed"), + errdetail("password size does not match"))); - if (memcmp(password, backend->password, backend->pwd_size) != 0) - ereport(ERROR, - (errmsg("crypt authentication failed"), - errdetail("password does not match"))); + if (memcmp(password, backend_endpoint->password, backend_endpoint->pwd_size) != 0) + ereport(ERROR, + (errmsg("crypt authentication failed"), + errdetail("password does not match"))); + } return 0; } @@ -1256,11 +1320,10 @@ do_crypt(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int msglen = htonl(0); pool_write_and_flush(frontend, &msglen, sizeof(msglen)); + StorePasswordInformation(password, (ntohl(size) - 4), PASSWORD_TYPE_UNKNOWN); + if (backend->pooled_backend_ref) + memcpy(backend->pooled_backend_ref->salt, salt, sizeof(salt)); - backend->auth_kind = 4; - backend->pwd_size = ntohl(size) - 4; - memcpy(backend->password, password, backend->pwd_size); - memcpy(backend->salt, salt, sizeof(salt)); } return kind; } @@ -1650,20 +1713,30 @@ do_md5_single_backend(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int /* connection reusing? compare it with saved password */ if (reauth) { - if (backend->passwordType != PASSWORD_TYPE_MD5) + PooledBackendClusterConnection *backend_endpoint = GetCurrentPooledBackendClusterConnection(); + if (!backend_endpoint || backend_endpoint->pwd_size <= 0) + { ereport(ERROR, (errmsg("md5 authentication failed"), - errdetail("invalid password type"))); + errdetail("password not found in the pooled connection"))); + } + else + { + if (backend_endpoint->passwordType != PASSWORD_TYPE_MD5) + ereport(ERROR, + (errmsg("md5 authentication failed"), + errdetail("invalid password type"))); - if (size != backend->pwd_size) - ereport(ERROR, - (errmsg("md5 authentication failed"), - errdetail("password does not match"))); + if (size != backend_endpoint->pwd_size) + ereport(ERROR, + (errmsg("md5 authentication failed"), + errdetail("password does not match"))); - if (memcmp(password, backend->password, backend->pwd_size) != 0) - ereport(ERROR, - (errmsg("md5 authentication failed"), - errdetail("password does not match"))); + if (memcmp(password, backend_endpoint->password, backend_endpoint->pwd_size) != 0) + ereport(ERROR, + (errmsg("md5 authentication failed"), + errdetail("password does not match"))); + } return 0; } else @@ -1679,11 +1752,9 @@ do_md5_single_backend(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int if (kind == AUTH_REQ_OK) { send_auth_ok(frontend, protoMajor); - backend->passwordType = PASSWORD_TYPE_MD5; - backend->auth_kind = AUTH_REQ_MD5; - backend->pwd_size = size; - memcpy(backend->password, password, backend->pwd_size); - memcpy(backend->salt, salt, sizeof(salt)); + StorePasswordInformation(password,size, PASSWORD_TYPE_MD5); + if (backend->pooled_backend_ref) + memcpy(salt, backend->pooled_backend_ref->salt, sizeof(salt)); } } return kind; @@ -1709,11 +1780,15 @@ get_auth_password(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int rea *passwordType = frontend->passwordType; return true; } - else if (reauth && backend && backend->pwd_size > 0) + else if (reauth) { - *password = backend->password; - *passwordType = backend->passwordType; - return true; + PooledBackendClusterConnection *backend_endpoint = GetCurrentPooledBackendClusterConnection(); + if (backend_endpoint && backend_endpoint->pwd_size > 0) + { + *password = backend_endpoint->password; + *passwordType = backend_endpoint->passwordType; + return true; + } } } else @@ -1800,10 +1875,7 @@ do_md5(POOL_CONNECTION * backend, POOL_CONNECTION * frontend, int reauth, int pr ereport(DEBUG2, (errmsg("MD5 authentication using the password from frontend"))); /* save this password in backend for the re-auth */ - backend->pwd_size = frontend->pwd_size; - memcpy(backend->password, frontend->password, frontend->pwd_size); - backend->password[backend->pwd_size] = 0; /* null terminate */ - backend->passwordType = frontend->passwordType; + StorePasswordInformation(frontend->password, frontend->pwd_size, frontend->passwordType); } else { @@ -2115,7 +2187,7 @@ do_SCRAM(POOL_CONNECTION * frontend, POOL_CONNECTION * backend, int protoMajor, if (frontend && IS_MAIN_NODE_ID(backend->db_node_id) && frontend->frontend_authenticated == false) { /* - * If frontend is not authenticated and do it it first. but if we have + * If frontend is not authenticated and do it first. but if we have * already received the password from frontend using the clear text * auth, we may not need to authenticate it */ @@ -2127,10 +2199,7 @@ do_SCRAM(POOL_CONNECTION * frontend, POOL_CONNECTION * backend, int protoMajor, ereport(DEBUG2, (errmsg("SCRAM authentication using the password from frontend"))); /* save this password in backend for the re-auth */ - backend->pwd_size = frontend->pwd_size; - memcpy(backend->password, frontend->password, frontend->pwd_size); - backend->password[backend->pwd_size] = 0; /* null terminate */ - backend->passwordType = frontend->passwordType; + StorePasswordInformation( frontend->password, frontend->pwd_size, frontend->passwordType); } else { diff --git a/src/auth/pool_hba.c b/src/auth/pool_hba.c index 202195b4..c503a3d1 100644 --- a/src/auth/pool_hba.c +++ b/src/auth/pool_hba.c @@ -1306,7 +1306,7 @@ ClientAuthentication(POOL_CONNECTION * frontend) } PG_CATCH(); { - close_all_backend_connections(); + ReleaseChildConnectionPool(); PG_RE_THROW(); } PG_END_TRY(); @@ -1501,7 +1501,7 @@ auth_failed(POOL_CONNECTION * frontend) frontend->username); break; } - close_all_backend_connections(); + ReleaseChildConnectionPool(); ereport(FATAL, (return_code(2), errmsg("client authentication failed"), diff --git a/src/config/pool_config_variables.c b/src/config/pool_config_variables.c index 770cfc24..8c1ca10d 100644 --- a/src/config/pool_config_variables.c +++ b/src/config/pool_config_variables.c @@ -195,11 +195,11 @@ static char *ShowOption(struct config_generic *record, int index, int elevel); static char *config_enum_get_options(struct config_enum *record, const char *prefix, const char *suffix, const char *separator); -static void send_row_description_for_detail_view(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +static void send_row_description_for_detail_view(POOL_CONNECTION * frontend, BackendClusterConnection * backend); static int send_grouped_type_variable_to_frontend(struct config_grouped_array_var *grouped_record, - POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); + POOL_CONNECTION * frontend, BackendClusterConnection * backend); static int send_array_type_variable_to_frontend(struct config_generic *record, - POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); + POOL_CONNECTION * frontend, BackendClusterConnection * backend); #endif @@ -299,6 +299,12 @@ static const struct config_enum_entry relcache_query_target_options[] = { {NULL, 0, false} }; +static const struct config_enum_entry connection_pool_type_options[] = { + {"global", GLOBAL_CONNECTION_POOL, false}, + {"classic", CLASSIC_CONNECTION_POOL, false}, + {NULL, 0, false} +}; + static const struct config_enum_entry check_temp_table_options[] = { {"catalog", CHECK_TEMP_CATALOG, false}, /* search system catalogs */ {"trace", CHECK_TEMP_TRACE, false}, /* tracing temp tables */ @@ -2067,6 +2073,28 @@ static struct config_int ConfigureNamesInt[] = NULL, NULL, NULL }, + { + {"max_pool_size", CFGCXT_INIT, CONNECTION_POOL_CONFIG, + "Maximum number of global connection pools.", + CONFIG_VAR_TYPE_INT, false, 0 + }, + &g_pool_config.max_pool_size, + 32, + 0, INT_MAX, + NULL, NULL, NULL + }, + + { + {"pool_availability_timeout", CFGCXT_INIT, CONNECTION_POOL_CONFIG, + "Maximum time in seconds a new connection can wait for a pool slot, if the connection pool is full.", + CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S + }, + &g_pool_config.pool_availability_timeout, + 5, + 0, INT_MAX, + NULL, NULL, NULL + }, + { {"sr_check_period", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG, "Time interval in seconds between the streaming replication delay checks.", @@ -2443,6 +2471,17 @@ static struct config_enum ConfigureNamesEnum[] = NULL, NULL, NULL, NULL }, + { + {"connection_pool_type", CFGCXT_INIT, CONNECTION_POOL_CONFIG, + "connection pooling type.", + CONFIG_VAR_TYPE_ENUM, false, 0 + }, + (int *) &g_pool_config.connection_pool_type, + CLASSIC_CONNECTION_POOL, + connection_pool_type_options, + NULL, NULL, NULL, NULL + }, + { {"check_temp_table", CFGCXT_RELOAD, GENERAL_CONFIG, "Enables temporary table check.", @@ -4858,6 +4897,14 @@ config_post_processor(ConfigContext context, int elevel) strcpy(g_pool_config.wd_nodes.wd_node_info[g_pool_config.pgpool_node_id].hostname, localhostname); return true; } + + if (g_pool_config.connection_cache == false && g_pool_config.connection_pool_type == GLOBAL_CONNECTION_POOL) + { + ereport(elevel, + (errmsg("invalid configuration, connection_cache should be enabled when connection_pool_type is set to global"))); + return false; + } + for (i = 0; i < MAX_CONNECTION_SLOTS; i++) { BackendInfo *backend_info = &g_pool_config.backend_desc->backend_info[i]; @@ -5907,7 +5954,7 @@ value_slot_for_config_record_is_empty(struct config_generic *record, int index) } bool -set_config_option_for_session(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *name, const char *value) +set_config_option_for_session(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *name, const char *value) { bool ret; MemoryContext oldCxt = MemoryContextSwitchTo(TopMemoryContext); @@ -5922,7 +5969,7 @@ set_config_option_for_session(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * } bool -reset_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +reset_all_variables(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { int i; int elevel = (frontend == NULL) ? FATAL : FRONTEND_ONLY_ERROR; @@ -5991,7 +6038,7 @@ reset_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) * Handle "pgpool show all" command. */ bool -report_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +report_all_variables(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { int i; int num_rows = 0; @@ -6049,7 +6096,7 @@ report_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) * Handle "pgpool show" command. */ bool -report_config_variable(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *var_name) +report_config_variable(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *var_name) { int index = 0; char *value; @@ -6123,7 +6170,7 @@ report_config_variable(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backen } static int -send_array_type_variable_to_frontend(struct config_generic *record, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +send_array_type_variable_to_frontend(struct config_generic *record, POOL_CONNECTION * frontend, BackendClusterConnection * backend) { if (record->dynamic_array_var) { @@ -6164,7 +6211,7 @@ send_array_type_variable_to_frontend(struct config_generic *record, POOL_CONNECT } static int -send_grouped_type_variable_to_frontend(struct config_grouped_array_var *grouped_record, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +send_grouped_type_variable_to_frontend(struct config_grouped_array_var *grouped_record, POOL_CONNECTION * frontend, BackendClusterConnection * backend) { int k, index; @@ -6215,7 +6262,7 @@ send_grouped_type_variable_to_frontend(struct config_grouped_array_var *grouped_ } static void -send_row_description_for_detail_view(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +send_row_description_for_detail_view(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"item", "value", "description"}; diff --git a/src/connection_pool/backend_connection.c b/src/connection_pool/backend_connection.c new file mode 100644 index 00000000..ae511c7b --- /dev/null +++ b/src/connection_pool/backend_connection.c @@ -0,0 +1,401 @@ +/* -*-backend_connection-c-*- */ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +#include "connection_pool/backend_connection.h" +#include "connection_pool/connection_pool.h" +#include "protocol/pool_connection_pool.h" +#include "protocol/pool_process_query.h" + +#include "context/pool_process_context.h" +#include "main/pool_internal_comms.h" +#include "utils/palloc.h" +#include "utils/memutils.h" +#include "utils/pool_stream.h" +#include "pool_config.h" +#include "utils/elog.h" +#include +#include +#include +#include +#include + +BackendClusterConnection clusterConnection = {.backend_end_point = NULL, .sp = NULL, .lease_type = LEASE_TYPE_FREE, .pool_id = -1}; + +bool +ConnectBackendSocketForBackendCluster(int slot_no); + +void +ResetBackendClusterConnection(void) +{ + memset(&clusterConnection, 0, sizeof(BackendClusterConnection)); + clusterConnection.lease_type = LEASE_TYPE_FREE; + clusterConnection.pool_id = -1; +} + +BackendClusterConnection * +GetBackendClusterConnection(void) +{ + return &clusterConnection; +} + +PooledBackendClusterConnection * +GetCurrentPooledBackendClusterConnection(void) +{ + if (processType != PT_CHILD) + return NULL; + return clusterConnection.backend_end_point; +} + +bool +StorePasswordInformation(char *password, int pwd_size, PasswordType passwordType) +{ + PooledBackendClusterConnection *backend_end_point = GetCurrentPooledBackendClusterConnection(); + if (backend_end_point == NULL) + return false; + backend_end_point->pwd_size = pwd_size; + memcpy(backend_end_point->password, password, pwd_size); + backend_end_point->password[pwd_size] = 0; /* null terminate */ + backend_end_point->passwordType = passwordType; + return true; +} + +bool +SaveAuthKindForBackendConnection(int auth_kind) +{ + PooledBackendClusterConnection *backend_end_point = GetCurrentPooledBackendClusterConnection(); + if (backend_end_point == NULL) + return false; + backend_end_point->auth_kind = auth_kind; + return true; +} + +int +GetAuthKindForCurrentPoolBackendConnection(void) +{ + PooledBackendClusterConnection *backend_end_point = GetCurrentPooledBackendClusterConnection(); + if (backend_end_point == NULL) + return -1; + return backend_end_point->auth_kind; +} + +bool ClearChildPooledConnectionData(void) +{ + PooledBackendClusterConnection *backend_end_point; + + if (processType != PT_CHILD) + return false; + + backend_end_point = GetCurrentPooledBackendClusterConnection(); + + if (backend_end_point == NULL) + { + ereport(LOG, + (errmsg("cannot get backend connection for child process"))); + return false; + } + memset(backend_end_point, 0, sizeof(PooledBackendClusterConnection)); + return true; +} + +/* + * Discard the current backend connection. + */ +bool +DiscardCurrentBackendConnection(void) +{ + BackendClusterConnection *current_backend_con; + int i, pool_id; + + Assert(processType == PT_CHILD); + + current_backend_con = GetBackendClusterConnection(); + + pool_id = current_backend_con->pool_id; + + pool_send_frontend_exits(current_backend_con); + + for (i = 0; i < NUM_BACKENDS; i++) + { + if (current_backend_con->slots[i].con) + { + pool_close(current_backend_con->slots[i].con, true); + current_backend_con->slots[i].con = NULL; + current_backend_con->slots[i].closetime = time(NULL); + current_backend_con->slots[i].state = CONNECTION_SLOT_EMPTY; + current_backend_con->slots[i].key = -1; + current_backend_con->slots[i].pid = -1; + } + } + /* Free Startup Packet */ + if (current_backend_con->sp) + { + if (current_backend_con->sp->database) + pfree(current_backend_con->sp->database); + if (current_backend_con->sp->user) + pfree(current_backend_con->sp->user); + + pfree(current_backend_con->sp->startup_packet); + pfree(current_backend_con->sp); + current_backend_con->sp = NULL; + } + /* Restore the pool_id */ + current_backend_con->pool_id = pool_id; + return true; +} + +/* Should already have the pool entry linked */ +bool SetupNewConnectionIntoChild(StartupPacket *sp) +{ + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + + if (processType != PT_CHILD) + return false; + + ereport(DEBUG1, (errmsg("SetupNewConnectionIntoChild pool_id:%d", current_backend_con->pool_id))); + ImportStartupPacketIntoChild(sp, NULL); + + /* Slots should already have been initialized by Connect backend */ + return true; +} + +void +ImportStartupPacketIntoChild(StartupPacket *sp, char *startup_packet_data) +{ + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + + if (sp->len <= 0 || sp->len >= MAX_STARTUP_PACKET_LENGTH) + ereport(ERROR, + (errmsg("ImportStartupPacketIntoChild: incorrect packet length (%d)", sp->len))); + + current_backend_con->sp = MemoryContextAlloc(TopMemoryContext, sizeof(StartupPacket)); + current_backend_con->sp->len = sp->len; + current_backend_con->sp->startup_packet = MemoryContextAlloc(TopMemoryContext, current_backend_con->sp->len); + + memcpy(current_backend_con->sp->startup_packet, + startup_packet_data ? startup_packet_data : sp->startup_packet, current_backend_con->sp->len); + + current_backend_con->sp->major = sp->major; + current_backend_con->sp->minor = sp->minor; + + current_backend_con->sp->database = pstrdup(sp->database); + current_backend_con->sp->user = pstrdup(sp->user); + + if (sp->major == PROTO_MAJOR_V3 && sp->application_name) + { + /* adjust the application name pointer in new packet */ + current_backend_con->sp->application_name = current_backend_con->sp->startup_packet + (sp->application_name - sp->startup_packet); + } + else + current_backend_con->sp->application_name = NULL; +} +/* + * Create actual connections to backends. + * New connection resides in TopMemoryContext. + */ +bool ConnectBackendClusterSockets(void) +{ + int active_backend_count = 0; + int i; + bool status_changed = false; + volatile BACKEND_STATUS status; + + MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); + + for (i = 0; i < NUM_BACKENDS; i++) + { + ereport(DEBUG1, + (errmsg("creating new connection to backend"), + errdetail("connecting %d backend", i))); + + if (!VALID_BACKEND_RAW(i)) + { + ereport(DEBUG1, + (errmsg("creating new connection to backend"), + errdetail("skipping backend slot %d because backend_status = %d", + i, BACKEND_INFO(i).backend_status))); + continue; + } + + /* + * Make sure that the global backend status in the shared memory + * agrees the local status checked by VALID_BACKEND. It is possible + * that the local status is up, while the global status has been + * changed to down by failover. + */ + status = BACKEND_INFO(i).backend_status; + if (status != CON_UP && status != CON_CONNECT_WAIT) + { + ereport(DEBUG1, + (errmsg("creating new connection to backend"), + errdetail("skipping backend slot %d because global backend_status = %d", + i, BACKEND_INFO(i).backend_status))); + + /* sync local status with global status */ + *(my_backend_status[i]) = status; + continue; + } + + if (ConnectBackendSocketForBackendCluster(i) == false) + { + /* set down status to local status area */ + *(my_backend_status[i]) = CON_DOWN; + pool_get_my_process_info()->need_to_restart = 1; // TODO: check if this is needed + } + else + { + + // p->info[i].client_idle_duration = 0; + // p->slots[i] = s; + + pool_init_params(&clusterConnection.backend_end_point->params); + + if (BACKEND_INFO(i).backend_status != CON_UP) + { + BACKEND_INFO(i).backend_status = CON_UP; + pool_set_backend_status_changed_time(i); + status_changed = true; + } + active_backend_count++; + } + } + + if (status_changed) + (void)write_status_file(); + + MemoryContextSwitchTo(oldContext); + + if (active_backend_count > 0) + { + return true; + } + + return false; +} + +/* + * Create socket connection to backend for a backend connection slot. + */ +bool +ConnectBackendSocketForBackendCluster(int slot_no) +{ + BackendNodeConnection *cp = &clusterConnection.slots[slot_no]; + BackendInfo *b = &pool_config->backend_desc->backend_info[slot_no]; + int fd; + + if (*b->backend_hostname == '/') + { + fd = connect_unix_domain_socket(slot_no, TRUE); + } + else + { + fd = connect_inet_domain_socket(slot_no, TRUE); + } + + if (fd < 0) + { + cp->state = CONNECTION_SLOT_SOCKET_CONNECTION_ERROR; + /* + * If failover_on_backend_error is true, do failover. Otherwise, + * just exit this session or skip next health node. + */ + if (pool_config->failover_on_backend_error) + { + notice_backend_error(slot_no, REQ_DETAIL_SWITCHOVER); + ereport(FATAL, + (errmsg("failed to create a backend connection"), + errdetail("executing failover on backend"))); + } + else + { + /* + * If we are in streaming replication mode and the node is a + * standby node, then we skip this node to avoid fail over. + */ + if (SL_MODE && !IS_PRIMARY_NODE_ID(slot_no)) + { + ereport(LOG, + (errmsg("failed to create a backend %d connection", slot_no), + errdetail("skip this backend because because failover_on_backend_error is off and we are in streaming replication mode and node is standby node"))); + + /* set down status to local status area */ + *(my_backend_status[slot_no]) = CON_DOWN; + + /* if main_node_id is not updated, then update it */ + if (Req_info->main_node_id == slot_no) + { + int old_main = Req_info->main_node_id; + Req_info->main_node_id = get_next_main_node(); + ereport(LOG, + (errmsg("main node %d is down. Update main node to %d", + old_main, Req_info->main_node_id))); + } + return false; + } + else + { + ereport(FATAL, + (errmsg("failed to create a backend %d connection", slot_no), + errdetail("not executing failover because failover_on_backend_error is off"))); + } + } + return false; + } + + cp->con = pool_open(fd, true); + cp->key = -1; + cp->pid = -1; + cp->state = CONNECTION_SLOT_SOCKET_CONNECTION_ONLY; + cp->con->pooled_backend_ref = &clusterConnection.backend_end_point->conn_slots[slot_no]; + cp->con->pooled_backend_ref->create_time = time(NULL); + return true; +} + +/* + * check_socket_status() * RETURN : 0 = > OK + * -1 = > broken socket. + */ + +int check_socket_status(int fd) +{ + fd_set rfds; + int result; + struct timeval t; + + for (;;) + { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + t.tv_sec = t.tv_usec = 0; + + result = select(fd + 1, &rfds, NULL, NULL, &t); + if (result < 0 && errno == EINTR) + { + continue; + } + else + { + return (result == 0 ? 0 : -1); + } + } + + return -1; +} \ No newline at end of file diff --git a/src/connection_pool/classic_connection_pool.c b/src/connection_pool/classic_connection_pool.c new file mode 100644 index 00000000..5ceb6988 --- /dev/null +++ b/src/connection_pool/classic_connection_pool.c @@ -0,0 +1,641 @@ +/* -*-classic_connection_pool-c-*- */ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +#include "pool.h" +#include "connection_pool/connection_pool.h" +#include "connection_pool/backend_connection.h" +#include "context/pool_process_context.h" +#include "context/pool_query_context.h" +#include "protocol/pool_process_query.h" +#include "main/pgpool_ipc.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/pool_stream.h" +#include "pool_config.h" +#include +#include +#include +#include + +typedef struct LPBorrowConnectionRes +{ + LEASE_TYPES lease_type; + int pool_id; + ConnectionPoolEntry *selected_pool; +} LPBorrowConnectionRes; + +extern ConnectionPoolEntry *ConnectionPool; +ConnectionPoolEntry *firstChildConnectionPool = NULL; + +static bool load_pooled_connection_into_child(ConnectionPoolEntry *selected_pool, LEASE_TYPES lease_type); +static bool export_classic_cluster_connection_data_to_pool(void); +static bool copy_classic_cluster_sockets_pool(void); +static void import_pooled_startup_packet_into_child(PooledBackendClusterConnection *backend_end_point); +static ConnectionPoolEntry *get_pool_entry_for_pool_id(int pool_id); + +static LEASE_TYPES pool_get_cp(char *user, char *database, int protoMajor, bool check_socket, ConnectionPoolEntry **selected_pool); +static ConnectionPoolEntry *get_pool_entry_to_discard(void); +static void discard_cp(void); +static void clear_pooled_cluster_connection(PooledBackendClusterConnection *backend_end_point); +static void close_all_pooled_connections(void); + +static int classic_connection_pool_entry_count(void) +{ + return pool_config->max_pool * pool_config->num_init_children; +} +static const char * +LPGetConnectionPoolInfo(void) +{ + return "Classic Connection Pool"; +} +static size_t +LPRequiredSharedMemSize(void) +{ + return sizeof(ConnectionPoolEntry) * classic_connection_pool_entry_count(); +} + +static int +LPGetPoolEntriesCount(void) +{ + return classic_connection_pool_entry_count(); +} + +static void +LPInitializeConnectionPool(void *shared_mem_ptr) +{ + int i; + Assert(processType == PT_MAIN); + ConnectionPool = (ConnectionPoolEntry *)shared_mem_ptr; + memset(ConnectionPool, 0, LPRequiredSharedMemSize()); + + for (i = 0; i < classic_connection_pool_entry_count(); i++) + { + ConnectionPool[i].pool_id = i; + ConnectionPool[i].child_id = -1; + ConnectionPool[i].status = POOL_ENTRY_EMPTY; + ConnectionPool[i].child_pid = -1; + } +} + +/* + * Either return existing connection or + * Discard the victum connection and return + * empty slot + */ +static BorrowConnectionRes * +LPBorrowClusterConnection(char *database, char *user, int major, int minor) +{ + LPBorrowConnectionRes *res = palloc(sizeof(LPBorrowConnectionRes)); + ConnectionPoolEntry *selected_pool = NULL; + Assert(firstChildConnectionPool); + res->lease_type = pool_get_cp(database, user, major, true, &selected_pool); + /* set the pool_id*/ + res->pool_id = selected_pool ? selected_pool->pool_id : -1; + res->selected_pool = selected_pool; + if (selected_pool) + ConnectionPoolRegisterNewLease(selected_pool, res->lease_type, my_proc_id, selected_pool->child_pid); + + return (BorrowConnectionRes*)res; +} + +static void +LPLoadChildConnectionPool(int int_arg) +{ + int i; + int child_id = my_proc_id; + Assert(child_id < pool_config->num_init_children); + firstChildConnectionPool = &ConnectionPool[child_id * pool_config->max_pool]; + elog(DEBUG2, "LoadChildConnectionPool: child_id:%d first id=%d", child_id, firstChildConnectionPool->pool_id); + for (i = 0; i < pool_config->max_pool; i++) + { + firstChildConnectionPool[i].status = POOL_ENTRY_EMPTY; + firstChildConnectionPool[i].child_id = child_id; + firstChildConnectionPool[i].pool_id = i; + firstChildConnectionPool[i].child_pid = getpid(); + } +} + +static bool +LPLoadBorrowedConnection(BorrowConnectionRes *context) +{ + LPBorrowConnectionRes *lp_context = (LPBorrowConnectionRes *)context; + return load_pooled_connection_into_child(lp_context->selected_pool, lp_context->lease_type); +} + +static bool +LPSyncClusterConnectionDataInPool(void) +{ + return export_classic_cluster_connection_data_to_pool(); +} + +static bool +LPPushClusterConnectionToPool(void) +{ + return copy_classic_cluster_sockets_pool(); +} + +static bool +LPReleaseClusterConnection(bool discard) +{ + BackendClusterConnection *current_backend_con; + ConnectionPoolEntry *pool_entry; + + Assert(processType == PT_CHILD); + + current_backend_con = GetBackendClusterConnection(); + pool_entry = get_pool_entry_for_pool_id(current_backend_con->pool_id); + + if (!pool_entry) + return false; + + ConnectionPoolUnregisterLease(pool_entry, my_proc_id, pool_entry->child_pid); + + // if (pool_entry->child_pid != my_proc_id) + // { + // ereport(WARNING, + // (errmsg("child:%d leased:%d is not the borrower of pool_id:%d borrowed by:%d", + // ipc_endpoint->child_pid, + // pro_info->pool_id, + // pool_entry->pool_id, + // pool_entry->child_pid))); + // return false; + // } + + ereport(LOG, + (errmsg("child: released pool_id:%d database:%s user:%s", + pool_entry->pool_id, + pool_entry->endPoint.database, + pool_entry->endPoint.user))); + if (discard) + { + pool_entry->status = POOL_ENTRY_EMPTY; + memset(&pool_entry->endPoint, 0, sizeof(PooledBackendClusterConnection)); + } + + return true; +} + +static bool +LPClusterConnectionNeedPush(void) +{ + BackendClusterConnection *child_connection = GetBackendClusterConnection(); + ConnectionPoolEntry *pool_entry = get_pool_entry_for_pool_id(child_connection->pool_id); + if (!pool_entry) + return false; + if (pool_entry->status == POOL_ENTRY_CONNECTED && child_connection->lease_type == LEASE_TYPE_READY_TO_USE) + return false; + return true; +} + +static void +LPReleaseChildConnectionPool(void) +{ + close_all_pooled_connections(); +} + +static ConnectionPoolEntry * +ClassicConnectionPoolGetPoolEntry(int pool_id, int child_id) +{ + ConnectionPoolEntry *pool_entry; + Assert(child_id < pool_config->num_init_children); + Assert(pool_id < pool_config->max_pool); + + if (ConnectionPool == NULL) + return NULL; + pool_entry = &ConnectionPool[child_id * pool_config->max_pool]; + return &pool_entry[pool_id]; +} + +static void +LPUpdatePooledConnectionCount(void) +{ + int pool; + int count = 0; + ConnectionPoolEntry *connection_pool = firstChildConnectionPool; + + Assert(processType == PT_CHILD); + if (!connection_pool) + return; + + for (pool = 0; pool < pool_config->max_pool; pool++) + { + int i; + ConnectionPoolEntry *pool_entry = &connection_pool[pool]; + PooledBackendClusterConnection *endPoint = &pool_entry->endPoint; + if (pool_entry->status != POOL_ENTRY_EMPTY) + count++; + } + pool_get_my_process_info()->pooled_connections = count; +} + +ConnectionPoolRoutine ClassicConnectionPoolRoutine = { + .RequiredSharedMemSize = LPRequiredSharedMemSize, + .InitializeConnectionPool = LPInitializeConnectionPool, + .LoadChildConnectionPool = LPLoadChildConnectionPool, + .BorrowClusterConnection = LPBorrowClusterConnection, + .LoadBorrowedConnection = LPLoadBorrowedConnection, + .ReleaseClusterConnection = LPReleaseClusterConnection, + .SyncClusterConnectionDataInPool = LPSyncClusterConnectionDataInPool, + .PushClusterConnectionToPool = LPPushClusterConnectionToPool, + .ReleaseChildConnectionPool = LPReleaseChildConnectionPool, + .ClusterConnectionNeedPush = LPClusterConnectionNeedPush, + .GetConnectionPoolInfo = LPGetConnectionPoolInfo, + .GetPoolEntriesCount = LPGetPoolEntriesCount, + .GetConnectionPoolEntry = ClassicConnectionPoolGetPoolEntry, + .UpdatePooledConnectionCount = LPUpdatePooledConnectionCount + }; + +const ConnectionPoolRoutine * +GetClassicConnectionPool(void) +{ + return &ClassicConnectionPoolRoutine; +} + +static ConnectionPoolEntry * +get_pool_entry_for_pool_id(int pool_id) +{ + if (pool_id < 0 || pool_id >= pool_config->max_pool) + return NULL; + return &firstChildConnectionPool[pool_id]; +} + +/* + * We should already have the sockets imported from the global pool + */ +static bool +load_pooled_connection_into_child(ConnectionPoolEntry *selected_pool, LEASE_TYPES lease_type) +{ + int i; + PooledBackendClusterConnection *backend_end_point; + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + + Assert(selected_pool); + backend_end_point = &selected_pool->endPoint; + + ereport(DEBUG2, + (errmsg("load_pooled_connection_into_child pool_id:%d backend_end_point:%p LeaseType:%d", selected_pool->pool_id, backend_end_point, lease_type))); + + current_backend_con->pool_id = selected_pool->pool_id; + current_backend_con->backend_end_point = backend_end_point; + current_backend_con->lease_type = lease_type; + + if (lease_type == LEASE_TYPE_EMPTY_SLOT_RESERVED) + return true; + + import_pooled_startup_packet_into_child(backend_end_point); + + for (i = 0; i < NUM_BACKENDS; i++) + { + if (! VALID_BACKEND(i)) + continue; + // current_backend_con->slots[i].socket = backend_end_point->conn_slots[i].socket; + current_backend_con->slots[i].con = pool_open(backend_end_point->conn_slots[i].socket, true); + current_backend_con->slots[i].con->pooled_backend_ref = &backend_end_point->conn_slots[i]; + + current_backend_con->slots[i].key = backend_end_point->conn_slots[i].key; + current_backend_con->slots[i].pid = backend_end_point->conn_slots[i].pid; + current_backend_con->slots[i].state = CONNECTION_SLOT_LOADED_FROM_BACKEND; + } + + return true; +} + +static bool +copy_classic_cluster_sockets_pool(void) +{ + int i; + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + PooledBackendClusterConnection *backend_end_point = current_backend_con->backend_end_point; + int pool_id = current_backend_con->pool_id; + + Assert(backend_end_point); + + for (i=0; i< NUM_BACKENDS; i++) + { + if (!VALID_BACKEND(i)) + continue; + if (current_backend_con->slots[i].con == NULL) + backend_end_point->conn_slots[i].socket = -1; + else + backend_end_point->conn_slots[i].socket = current_backend_con->slots[i].con->fd; + } + + get_pool_entry_for_pool_id(pool_id)->status = POOL_ENTRY_CONNECTED; + return true; +} + +static bool +export_classic_cluster_connection_data_to_pool(void) +{ + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + StartupPacket *sp = current_backend_con->sp; + int pool_id = current_backend_con->pool_id; + int i, sock_index; + ConnectionPoolEntry *pool_entry = get_pool_entry_for_pool_id(pool_id); + PooledBackendClusterConnection *backend_end_point; + + if (pool_entry == NULL) + return false; + backend_end_point = &pool_entry->endPoint; + + if (backend_end_point == NULL) + return false; + + /* verify the length first */ + if (sp->len <= 0 || sp->len >= MAX_STARTUP_PACKET_LENGTH) + { + ereport(ERROR, + (errmsg("incorrect startup packet length (%d)", sp->len))); + return false; + } + + current_backend_con->backend_end_point = backend_end_point; + memcpy(backend_end_point->startup_packet_data, sp->startup_packet, sp->len); + backend_end_point->sp.len = sp->len; + backend_end_point->sp.startup_packet = backend_end_point->startup_packet_data; + + backend_end_point->sp.major = sp->major; + backend_end_point->sp.minor = sp->minor; + + StrNCpy(backend_end_point->database, sp->database, sizeof(backend_end_point->database)); + StrNCpy(backend_end_point->user, sp->user, sizeof(backend_end_point->user)); + + backend_end_point->sp.database = backend_end_point->database; + backend_end_point->sp.user = backend_end_point->user; + + if (sp->major == PROTO_MAJOR_V3 && sp->application_name) + { + /* adjust the application name pointer in new packet */ + backend_end_point->sp.application_name = backend_end_point->sp.startup_packet + (sp->application_name - sp->startup_packet); + } + else + backend_end_point->sp.application_name = NULL; + + sock_index = 0; + for (i = 0; i < NUM_BACKENDS; i++) + { + if (VALID_BACKEND(i)) + { + backend_end_point->conn_slots[i].key = current_backend_con->slots[i].key; + backend_end_point->conn_slots[i].pid = current_backend_con->slots[i].pid; + backend_end_point->backend_ids[sock_index++] = i; + } + else + { + backend_end_point->conn_slots[i].key = -1; + backend_end_point->conn_slots[i].pid = -1; + backend_end_point->conn_slots[i].socket = -1; + } + } + backend_end_point->num_sockets = sock_index; + get_pool_entry_for_pool_id(pool_id)->status = POOL_ENTRY_LOADED; + return true; +} + +static void +import_pooled_startup_packet_into_child(PooledBackendClusterConnection *backend_end_point) +{ + + if (backend_end_point->sp.len <= 0 || backend_end_point->sp.len >= MAX_STARTUP_PACKET_LENGTH) + ereport(ERROR, + (errmsg("incorrect packet length (%d)", backend_end_point->sp.len))); + + ImportStartupPacketIntoChild(&backend_end_point->sp, backend_end_point->startup_packet_data); +} + +/* + * find connection by user and database + */ +static LEASE_TYPES +pool_get_cp(char *user, char *database, int protoMajor, bool check_socket, ConnectionPoolEntry **selected_pool) +{ + pool_sigset_t oldmask; + int i; + ConnectionPoolEntry *first_empty_entry = NULL; + ConnectionPoolEntry *connection_pool = firstChildConnectionPool; + + Assert(processType == PT_CHILD); + Assert(connection_pool); + + POOL_SETMASK2(&BlockSig, &oldmask); + + for (i = 0; i < pool_config->max_pool; i++) + { + PooledBackendClusterConnection *endPoint = &connection_pool[i].endPoint; + *selected_pool = &connection_pool[i]; + if (connection_pool[i].status == POOL_ENTRY_EMPTY) + { + if (!first_empty_entry) + first_empty_entry = &connection_pool[i]; + continue; + } + if (strcmp(endPoint->user, user) == 0 && + strcmp(endPoint->database, database) == 0 && + endPoint->sp.major == protoMajor) + { + int sock_broken = 0; + int j; + + /* mark this connection is under use */ + endPoint->client_connected = true; + POOL_SETMASK(&oldmask); + + if (check_socket) + { + for (j = 0; j < NUM_BACKENDS; j++) + { + PooledBackendNodeConnection *pooled_backend_node = &endPoint->conn_slots[j]; + if (!VALID_BACKEND(j)) + continue; + if (pooled_backend_node->socket > 0) + { + sock_broken = check_socket_status(pooled_backend_node->socket); + if (sock_broken < 0) + break; + } + else + { + sock_broken = -1; + break; + } + } + + if (sock_broken < 0) + { + ereport(LOG, + (errmsg("connection closed."), + errdetail("retry to create new connection pool"))); + /* + * It is possible that one of backend just broke. sleep 1 + * second to wait for failover occurres, then wait for the + * failover finishes. + */ + sleep(1); + wait_for_failover_to_finish(); + + for (j = 0; j < NUM_BACKENDS; j++) + { + PooledBackendNodeConnection *pooled_backend_node = &endPoint->conn_slots[j]; + if (!VALID_BACKEND(j) || pooled_backend_node->socket <= 0) + continue; + close(pooled_backend_node->socket); + pooled_backend_node->socket = -1; + } + clear_pooled_cluster_connection(endPoint); + POOL_SETMASK(&oldmask); + + connection_pool[i].status = POOL_ENTRY_EMPTY; + return LEASE_TYPE_EMPTY_SLOT_RESERVED; + } + } + POOL_SETMASK(&oldmask); + + return LEASE_TYPE_READY_TO_USE; + } + } + POOL_SETMASK(&oldmask); + if (first_empty_entry) + { + *selected_pool = first_empty_entry; + elog(DEBUG2, "pool_get_cp: empty slot reserved LEASE_TYPE_EMPTY_SLOT_RESERVED at id %d", first_empty_entry->pool_id); + return LEASE_TYPE_EMPTY_SLOT_RESERVED; + } + + *selected_pool = get_pool_entry_to_discard(); + elog(DEBUG2, "pool_get_cp: discard and create LEASE_TYPE_DISCART_AND_CREATE"); + + return LEASE_TYPE_DISCART_AND_CREATE; +} + +/* + * create a connection pool by user and database + */ +static ConnectionPoolEntry * +get_pool_entry_to_discard(void) +{ + int i; + time_t closetime = TMINTMAX; + ConnectionPoolEntry *oldestp; + ConnectionPoolEntry *connection_pool = firstChildConnectionPool; + + Assert(processType == PT_CHILD); + Assert(connection_pool); + + /* + * no empty connection slot was found. look for the oldest connection and + * discard it. + */ + oldestp = connection_pool; + + for (i = 0; i < pool_config->max_pool; i++) + { + + // ereport(DEBUG1, + // (errmsg("creating connection pool"), + // errdetail("user: %s database: %s closetime: %ld", + // CONNECTION_SLOT(p, main_node_id)->sp->user, + // CONNECTION_SLOT(p, main_node_id)->sp->database, + // CONNECTION_SLOT(p, main_node_id)->closetime))); + + if (connection_pool[i].last_returned_time < closetime) + { + closetime = connection_pool[i].last_returned_time; + oldestp = &connection_pool[i]; + } + } + return oldestp; +} + +/* + * disconnect and release a connection to the database + */ +static void +discard_cp(void) +{ + int i; + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + + Assert(processType == PT_CHILD); + Assert(current_backend_con); + + pool_send_frontend_exits(current_backend_con); + + for (i = 0; i < NUM_BACKENDS; i++) + { + if (!VALID_BACKEND(i)) + continue; + pool_close(current_backend_con->slots[i].con, true); + current_backend_con->slots[i].state = CONNECTION_SLOT_EMPTY; + } +} + +static void +clear_pooled_cluster_connection(PooledBackendClusterConnection *backend_end_point) +{ + memset(&backend_end_point->conn_slots, 0, sizeof(PooledBackendNodeConnection) * MAX_NUM_BACKENDS); + backend_end_point->num_sockets = 0; +} + +/* + * Disconnect to all backend 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 +close_all_pooled_connections(void) +{ + int pool; + int current_pool_id = -1; + ConnectionPoolEntry *connection_pool = firstChildConnectionPool; + BackendClusterConnection *current_backend_con; + + Assert(processType == PT_CHILD); + if (!connection_pool) + return; + + current_backend_con = GetBackendClusterConnection(); + current_pool_id = current_backend_con->pool_id; + pool_send_frontend_exits(current_backend_con); + + for (pool = 0; pool < pool_config->max_pool; pool++) + { + int i; + ConnectionPoolEntry *pool_entry = &connection_pool[pool]; + PooledBackendClusterConnection *endPoint = &pool_entry->endPoint; + if (pool_entry->status == POOL_ENTRY_EMPTY) + continue; + if (current_pool_id != pool) + { + for (i = 0; i < NUM_BACKENDS; i++) + { + if (endPoint->conn_slots[i].socket > 0) + { + close(endPoint->conn_slots[i].socket); + endPoint->conn_slots[i].socket = -1; + } + } + } + memset(&pool_entry->endPoint, 0, sizeof(PooledBackendClusterConnection)); + pool_entry->status = POOL_ENTRY_EMPTY; + } + ResetBackendClusterConnection(); +} diff --git a/src/connection_pool/connection_pool.c b/src/connection_pool/connection_pool.c new file mode 100644 index 00000000..c7a37a4d --- /dev/null +++ b/src/connection_pool/connection_pool.c @@ -0,0 +1,379 @@ +/* -*-connection_pool-c-*- */ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +#include "pool.h" +#include "pool_config.h" +#include "connection_pool/connection_pool.h" +#include "utils/elog.h" +#include +#include +#include + +static const ConnectionPoolRoutine *activeConnectionPool = NULL; +ConnectionPoolEntry *ConnectionPool = NULL; + +static PooledBackendClusterConnection* get_backend_connection_for_cancel_packer(CancelPacket *cp); +static PooledBackendNodeConnection *get_backend_node_connection_for_backend_pid(int backend_pid, int *backend_node_id); + +void +InstallConnectionPool(const ConnectionPoolRoutine *ConnectionPoolRoutine) +{ + Assert(processType == PT_MAIN); + if (activeConnectionPool) + { + ereport(ERROR, + (errmsg("Connection pool routine already installed"))); + } + activeConnectionPool = ConnectionPoolRoutine; +} + +size_t +ConnectionPoolRequiredSharedMemSize(void) +{ + Assert(activeConnectionPool); + Assert(processType == PT_MAIN); + if (activeConnectionPool->RequiredSharedMemSize) + return activeConnectionPool->RequiredSharedMemSize(); + return 0; +} +int +GetPoolEntriesCount(void) +{ + Assert(activeConnectionPool); + return activeConnectionPool->GetPoolEntriesCount(); +} + +const char* +GetConnectionPoolInfo(void) +{ + if (! activeConnectionPool) + return "No connection pool installed"; + Assert(activeConnectionPool->GetConnectionPoolInfo); + return activeConnectionPool->GetConnectionPoolInfo(); +} + +void +InitializeConnectionPool(void *shared_mem_ptr) +{ + Assert(activeConnectionPool); + Assert(processType == PT_MAIN); + activeConnectionPool->InitializeConnectionPool(shared_mem_ptr); +} + +void +LoadChildConnectionPool(int intarg) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + activeConnectionPool->LoadChildConnectionPool(intarg); +} + +BorrowConnectionRes* +BorrowClusterConnection(char *database, char *user, int major, int minor) +{ + BackendClusterConnection *backend_connection; + BorrowConnectionRes *res; + + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + + backend_connection = GetBackendClusterConnection(); + + res = activeConnectionPool->BorrowClusterConnection(database, user, major, minor); + if (backend_connection && res) + backend_connection->lease_type = LEASE_TYPE_FREE; + return res; +} + +bool +LoadBorrowedConnection(BorrowConnectionRes *context) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + return activeConnectionPool->LoadBorrowedConnection(context); +} + +bool +ReleaseClusterConnection(bool discard) +{ + BackendClusterConnection *backend_connection; + + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + + backend_connection = GetBackendClusterConnection(); + if (backend_connection) + backend_connection->lease_type = LEASE_TYPE_FREE; + return activeConnectionPool->ReleaseClusterConnection(discard); +} + +bool +PushClusterConnectionToPool(void) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + return activeConnectionPool->PushClusterConnectionToPool(); +} + +bool +SyncClusterConnectionDataInPool(void) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + return activeConnectionPool->SyncClusterConnectionDataInPool(); +} + +void +ReleaseChildConnectionPool(void) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + activeConnectionPool->ReleaseChildConnectionPool(); +} + +PooledBackendNodeConnection * +GetBackendNodeConnectionForBackendPID(int backend_pid, int *backend_node_id) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + if (activeConnectionPool->GetBackendNodeConnectionForBackendPID) + return activeConnectionPool->GetBackendNodeConnectionForBackendPID(backend_pid, backend_node_id); + return get_backend_node_connection_for_backend_pid(backend_pid, backend_node_id); +} +PooledBackendClusterConnection * +GetBackendEndPointForCancelPacket(CancelPacket *cp) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + + if (activeConnectionPool->GetBackendEndPointForCancelPacket) + return activeConnectionPool->GetBackendEndPointForCancelPacket(cp); + + return get_backend_connection_for_cancel_packer(cp); +} + +void +UpdatePooledConnectionCount(void) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + activeConnectionPool->UpdatePooledConnectionCount(); +} + +bool +ClusterConnectionNeedPush(void) +{ + Assert(activeConnectionPool); + Assert(processType == PT_CHILD); + return activeConnectionPool->ClusterConnectionNeedPush(); +} + +ConnectionPoolEntry * +GetConnectionPoolEntryAtIndex(int pool_idx) +{ + Assert(activeConnectionPool); + Assert(ConnectionPool); + if (pool_idx < 0 || pool_idx >= GetPoolEntriesCount()) + return NULL; + return &ConnectionPool[pool_idx]; +} + +ConnectionPoolEntry * +GetConnectionPoolEntry(int pool_id, int child_id) +{ + Assert(activeConnectionPool); + Assert(ConnectionPool); + return activeConnectionPool->GetConnectionPoolEntry(pool_id, child_id); +} + +bool +ConnectionPoolUnregisterLease(ConnectionPoolEntry* pool_entry, int child_id, pid_t child_pid) +{ + ProcessInfo *child_proc_info; + + Assert(child_id >= 0 && child_id < pool_config->num_init_children); + Assert(pool_entry); + + if (pool_entry->child_pid > 0 && pool_entry->child_pid != child_pid) + { + ereport(LOG, + (errmsg("failed to free lease of pooled connection: pool_id:%d is leased to different child:%d", pool_entry->pool_id, pool_entry->child_pid))); + return false; + } + + ereport(DEBUG1, + (errmsg("pool_id:%d, is released from child:%d", pool_entry->pool_id, child_pid))); + + child_proc_info = &process_info[child_id]; + child_proc_info->pool_id = -1; + + pool_entry->endPoint.client_disconnection_time = time(NULL); + pool_entry->last_returned_time = time(NULL); + pool_entry->endPoint.client_disconnection_count++; + + return true; +} + +bool +ConnectionPoolRegisterNewLease(ConnectionPoolEntry *pool_entry, LEASE_TYPES lease_type, int child_id, pid_t child_pid) +{ + ProcessInfo *child_proc_info; + Assert(pool_entry); + Assert(child_id >= 0 && child_id < pool_config->num_init_children); + + + child_proc_info = &process_info[child_id]; + child_proc_info->pool_id = pool_entry->pool_id; + + pool_entry->last_returned_time = 0; + + if (pool_entry->status == POOL_ENTRY_CONNECTED) + { + pool_entry->leased_count++; + pool_entry->leased_time = time(NULL); + } + ereport(LOG, + (errmsg("pool_id:%d, leased to child:[ID:%d, PID:%d]", pool_entry->pool_id, child_id, child_pid))); + return true; +} + +/* Functions that work on any installed connection pool */ +static PooledBackendClusterConnection * +get_backend_connection_for_cancel_packer(CancelPacket *cp) +{ + int i; + Assert(activeConnectionPool); + Assert(ConnectionPool); + Assert(processType == PT_CHILD); + + for (i = 0; i < GetPoolEntriesCount(); i++) + { + int con_slot; + if (ConnectionPool[i].status == POOL_ENTRY_EMPTY) + { + elog(DEBUG2, "get_backend_connection_for_cancel_packer: empty entry [%d] child_id:%d, pool_id%d, status:%d, child_pid:%d", i, + ConnectionPool[i].child_id, + ConnectionPool[i].pool_id, + ConnectionPool[i].status, + ConnectionPool[i].child_pid); + continue; + } + + for (con_slot = 0; con_slot < NUM_BACKENDS; con_slot++) + { + PooledBackendNodeConnection *c = &ConnectionPool[i].endPoint.conn_slots[con_slot]; + + if (!VALID_BACKEND(con_slot)) + continue; + + ereport(LOG, + (errmsg("processing cancel request"), + errdetail("connection info: database:%s user:%s pid:%d key:%d i:%d", + ConnectionPool[i].endPoint.database, ConnectionPool[i].endPoint.user, + ntohl(c->pid), ntohl(c->key), con_slot))); + if (c->pid == cp->pid && c->key == cp->key) + { + ereport(DEBUG1, + (errmsg("processing cancel request"), + errdetail("found pid:%d key:%d i:%d", ntohl(c->pid), ntohl(c->key), con_slot))); + return &ConnectionPool[i].endPoint; + } + } + } + return NULL; +} + +/* + * locate and return the shared memory BackendConnection having the + * backend connection with the pid + * If the connection is found the *backend_node_id contains the backend node id + * of the backend node that has the connection + */ +static PooledBackendNodeConnection * +get_backend_node_connection_for_backend_pid(int backend_pid, int *backend_node_id) +{ + int i; + Assert(activeConnectionPool); + Assert(ConnectionPool); + Assert(processType == PT_CHILD); + + for (i = 0; i < GetPoolEntriesCount(); i++) + { + int con_slot; + if (ConnectionPool[i].status == POOL_ENTRY_EMPTY) + continue; + for (con_slot = 0; con_slot < NUM_BACKENDS; con_slot++) + { + if (!VALID_BACKEND(con_slot)) + continue; + + if (ConnectionPool[i].endPoint.conn_slots[con_slot].pid == backend_pid) + { + *backend_node_id = i; + return &ConnectionPool[i].endPoint.conn_slots[con_slot]; + } + } + } + return NULL; +} + +int +InvalidateAllPooledConnections(char *database) +{ + int i; + int count = 0; + Assert(ConnectionPool); + + for (i = 0; i < GetPoolEntriesCount(); i++) + { + if (ConnectionPool[i].status == POOL_ENTRY_EMPTY) + continue; + if (!database || strcmp(ConnectionPool[i].endPoint.database, database) == 0) + { + ConnectionPool[i].need_cleanup = true; + count++; + } + } + return count; +} + + +int +InvalidateNodeInPooledConnections(int node_id) +{ + int i; + int count = 0; + Assert(ConnectionPool); + + for (i = 0; i < GetPoolEntriesCount(); i++) + { + if (ConnectionPool[i].status == POOL_ENTRY_EMPTY) + continue; + if (ConnectionPool[i].endPoint.conn_slots[node_id].socket > 0) + { + ereport(DEBUG1, + (errmsg("InvalidateNodeInPooledConnections: node_id:%d for pool-entry:%d need reconnection", node_id,i))); + ConnectionPool[i].endPoint.conn_slots[node_id].need_reconnect = true; + count++; + } + } + return count; +} \ No newline at end of file diff --git a/src/connection_pool/global_connection_pool.c b/src/connection_pool/global_connection_pool.c new file mode 100644 index 00000000..ee3fca2f --- /dev/null +++ b/src/connection_pool/global_connection_pool.c @@ -0,0 +1,792 @@ +/* -*-global_connection_pool-c-*- */ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +#include "pool.h" +#include "connection_pool/connection_pool.h" +#include "connection_pool/backend_connection.h" +#include "context/pool_process_context.h" +#include "main/pgpool_ipc.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/pool_stream.h" +#include "pool_config.h" +#include +#include +#include +#include + +typedef struct GPBorrowConnectionRes +{ + LEASE_TYPES lease_type; + int pool_id; + int sockets[MAX_NUM_BACKENDS]; + int sock_count; +} GPBorrowConnectionRes; + +extern ConnectionPoolEntry *ConnectionPool; + +int ParentLinkFd = -1; + +static bool import_pooled_connection_into_child(int pool_id, int *sockets, LEASE_TYPES lease_type); +static bool export_local_cluster_connection_data_to_pool(void); +static bool export_local_cluster_sockets_pool(void); +static void import_pooled_startup_packet_into_child(PooledBackendClusterConnection *backend_end_point); +static ConnectionPoolEntry* get_pool_entry_for_pool_id(int pool_id); +static int get_sockets_array(PooledBackendClusterConnection *backend_endpoint, int **sockets, int *num_sockets, bool pooled_socks); + +static int get_pooled_connection_for_lending(char *user, char *database, int protoMajor, LEASE_TYPES *lease_type); +static bool register_new_lease(int pool_id, LEASE_TYPES lease_type, IPC_Endpoint *ipc_endpoint); +static bool unregister_lease(int pool_id, IPC_Endpoint *ipc_endpoint); + +static int +GPGetPoolEntriesCount(void) +{ + return pool_config->max_pool_size; +} + +static size_t +GPRequiredSharedMemSize(void) +{ + return sizeof(ConnectionPoolEntry) * GPGetPoolEntriesCount(); +} + +static const char* +GPGetConnectionPoolInfo(void) +{ + return "Global Connection Pool"; +} + +static void +GPInitializeConnectionPool(void *shared_mem_ptr) +{ + int i; + ConnectionPool = (ConnectionPoolEntry *)shared_mem_ptr; + memset(ConnectionPool, 0, GPRequiredSharedMemSize()); + + for (i = 0; i < GPGetPoolEntriesCount(); i++) + { + ConnectionPool[i].pool_id = i; + ConnectionPool[i].child_id = -1; + } +} + +static BorrowConnectionRes* +GPBorrowClusterConnection(char *database, char *user, int major, int minor) +{ + GPBorrowConnectionRes* res = palloc(sizeof(GPBorrowConnectionRes)); + ProcessInfo *pro_info = pool_get_my_process_info(); + + Assert(ParentLinkFd != -1); + res->lease_type = BorrowBackendConnection(ParentLinkFd, database, user, major, minor, &res->sock_count, res->sockets); + /* set the pool_id*/ + res->pool_id = pro_info->pool_id; + return (BorrowConnectionRes*)res; +} + +static void +GPLoadChildConnectionPool(int intarg) +{ + ParentLinkFd = intarg; +} + +static bool +GPLoadBorrowedConnection(BorrowConnectionRes *context) +{ + GPBorrowConnectionRes* gp_context = (GPBorrowConnectionRes*)context; + return import_pooled_connection_into_child(gp_context->pool_id, gp_context->sockets, gp_context->lease_type); +} + +static bool +GPSyncClusterConnectionDataInPool(void) +{ + return export_local_cluster_connection_data_to_pool(); +} + +static bool +GPPushClusterConnectionToPool(void) +{ + return export_local_cluster_sockets_pool(); +} + +static bool +GPReleaseClusterConnection(bool discard) +{ + return ReleasePooledConnectionFromChild(ParentLinkFd, discard); +} + +static bool +GPClusterConnectionNeedPush(void) +{ + BackendClusterConnection *child_connection = GetBackendClusterConnection(); + ConnectionPoolEntry *pool_entry = get_pool_entry_for_pool_id(child_connection->pool_id); + if (!pool_entry) + return false; + if (child_connection->need_push_back) + return true; + if (pool_entry->status == POOL_ENTRY_CONNECTED && child_connection->lease_type == LEASE_TYPE_READY_TO_USE) + return false; + return true; +} + +static ConnectionPoolEntry * +GlobalConnectionPoolGetPoolEntry(int pool_id, int child_id) +{ + Assert(pool_id < GPGetPoolEntriesCount()); + + if (ConnectionPool == NULL) + return NULL; + return &ConnectionPool[pool_id]; +} + +static void +GPReleaseChildConnectionPool(void) +{ + /* Just discard the currently pooled connection */ + ReleasePooledConnectionFromChild(ParentLinkFd, true); + +} + +static void +GPUpdatePooledConnectionCount(void) +{ + int pool; + int current_pool_id = -1; + BackendClusterConnection *current_backend_con; + + Assert(processType == PT_CHILD); + + current_backend_con = GetBackendClusterConnection(); + if (current_backend_con->pool_id < 0) + pool_get_my_process_info()->pooled_connections = 1; + else + pool_get_my_process_info()->pooled_connections = 0; +} + +ConnectionPoolRoutine GlobalConnectionPoolRoutine = { + .RequiredSharedMemSize = GPRequiredSharedMemSize, + .InitializeConnectionPool = GPInitializeConnectionPool, + .LoadChildConnectionPool = GPLoadChildConnectionPool, + .BorrowClusterConnection = GPBorrowClusterConnection, + .LoadBorrowedConnection = GPLoadBorrowedConnection, + .ReleaseClusterConnection = GPReleaseClusterConnection, + .SyncClusterConnectionDataInPool = GPSyncClusterConnectionDataInPool, + .PushClusterConnectionToPool = GPPushClusterConnectionToPool, + .ReleaseChildConnectionPool = GPReleaseChildConnectionPool, + .ClusterConnectionNeedPush = GPClusterConnectionNeedPush, + .GetConnectionPoolInfo = GPGetConnectionPoolInfo, + .GetPoolEntriesCount = GPGetPoolEntriesCount, + .GetConnectionPoolEntry = GlobalConnectionPoolGetPoolEntry, + .UpdatePooledConnectionCount = GPUpdatePooledConnectionCount +}; + +const ConnectionPoolRoutine* +GetGlobalConnectionPool(void) +{ + return &GlobalConnectionPoolRoutine; +} + +static ConnectionPoolEntry * +get_pool_entry_for_pool_id(int pool_id) +{ + if (pool_id < 0 || pool_id > GPGetPoolEntriesCount()) + return NULL; + return &ConnectionPool[pool_id]; +} + +/* TODO: Handle disconnected socktes */ +static int +get_sockets_array(PooledBackendClusterConnection *backend_endpoint, int **sockets, int *num_sockets, bool pooled_socks) +{ + int i; + int *socks = NULL; + + if (!backend_endpoint || backend_endpoint->num_sockets <= 0) + return -1; + + *num_sockets = backend_endpoint->num_sockets; + socks = palloc(sizeof(int) * *num_sockets); + + for (i = 0; i < *num_sockets; i++) + { + int sock_index = backend_endpoint->backend_ids[i]; + if (pooled_socks) + { + // if (backend_endpoint->conn_slots[sock_index].socket > 0) + socks[i] = backend_endpoint->conn_slots[sock_index].socket; + ereport(DEBUG1, + (errmsg("Adding pooled node:%d socket:%d at sock_index:%d with backend_status:%d sockets to push list", i, socks[i], sock_index, backend_endpoint->backend_status[sock_index]))); + } + else + { + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + socks[i] = current_backend_con->slots[sock_index].con->fd; + ereport(DEBUG1, + (errmsg("Adding node:%d socket:%d at sock_index:%d with backend_status:%d child_backend_status:%d sockets to push list", i, socks[i], sock_index, + current_backend_con->backend_end_point->backend_status[sock_index], *(my_backend_status[sock_index])))); + } + } + + *sockets = socks; + ereport(DEBUG1, (errmsg("we have %d %s sockets to push", *num_sockets, pooled_socks ? "pooled" : "child"))); + return *num_sockets; +} + +/* + * We should already have the sockets imported from the global pool + */ +static bool +import_pooled_connection_into_child(int pool_id, int *sockets, LEASE_TYPES lease_type) +{ + int i; + int num_sockets; + int *backend_ids; + PooledBackendClusterConnection *backend_end_point = GetGlobalPooledBackendClusterConnection(pool_id); + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + + if (backend_end_point == NULL) + return false; + + ereport(DEBUG2, + (errmsg("import_pooled_connection_into_child pool_id:%d backend_end_point:%p LeaseType:%d", pool_id, backend_end_point, lease_type))); + + num_sockets = backend_end_point->num_sockets; + backend_ids = backend_end_point->backend_ids; + + current_backend_con->pool_id = pool_id; + current_backend_con->backend_end_point = backend_end_point; + current_backend_con->lease_type = lease_type; + + if (lease_type == LEASE_TYPE_EMPTY_SLOT_RESERVED) + return true; + + import_pooled_startup_packet_into_child(backend_end_point); + + for (i = 0; i < num_sockets; i++) + { + int slot_no = backend_ids[i]; + if (BACKEND_INFO(slot_no).backend_status == CON_DOWN || BACKEND_INFO(slot_no).backend_status == CON_UNUSED) + { + /* + * Although we have received the socket for backend slot + * but the global status of slot indicates that it is marked + * as down. Probably because of failover. + * So we do not want to use it + * We also want to mark the slot as down in the global pool + */ + close(sockets[i]); + backend_end_point->conn_slots[slot_no].socket = -1; + backend_end_point->backend_status[slot_no] = BACKEND_INFO(slot_no).backend_status; + continue; + } + + if (backend_end_point->conn_slots[slot_no].need_reconnect) + { + close(sockets[i]); + backend_end_point->conn_slots[slot_no].socket = -1; + backend_end_point->backend_status[slot_no] = CON_DOWN; + backend_end_point->conn_slots[slot_no].need_reconnect = false; + continue; + } + + current_backend_con->slots[slot_no].con = pool_open(sockets[i], true); + current_backend_con->slots[slot_no].con->pooled_backend_ref = &backend_end_point->conn_slots[slot_no]; + + current_backend_con->slots[slot_no].key = backend_end_point->conn_slots[slot_no].key; + current_backend_con->slots[slot_no].pid = backend_end_point->conn_slots[slot_no].pid; + current_backend_con->slots[slot_no].state = CONNECTION_SLOT_LOADED_FROM_BACKEND; + } + + /* Now take care of backends that were attached after the pool was created + * and adjust the status of backend end point to reflect the new reality */ + + backend_end_point->num_sockets = 0; + + for (i = 0; i < NUM_BACKENDS; i++) + { + if (backend_end_point->backend_status[i] != BACKEND_INFO(i).backend_status) + { + if ((backend_end_point->backend_status[i] == CON_DOWN || backend_end_point->backend_status[i] == CON_UNUSED) && + (BACKEND_INFO(i).backend_status == CON_UP || BACKEND_INFO(i).backend_status == CON_CONNECT_WAIT)) + { + /* + * Backend was down when the pool was created but now it is up + * We need to update connect the backend, push back the socket to global pool + * and also sync the status in the global pool + */ + ereport(LOG, + (errmsg("Backend %d was down when pool was created but now it is up :%d", i, current_backend_con->slots[i].state))); + if (ConnectBackendSocketForBackendCluster(i) == false) + { + /* set down status to local status area */ + *(my_backend_status[i]) = CON_DOWN; + pool_get_my_process_info()->need_to_restart = 1; // TODO: check if this is needed + } + else + { + /* Connection was successfull */ + ereport(LOG, (errmsg("Backend %d was down when pool was created. Sock successfully connected:%d", i, + current_backend_con->slots[i].state))); + *(my_backend_status[i]) = CON_UP; + } + backend_end_point->backend_status[i] = *(my_backend_status[i]); + current_backend_con->need_push_back = true; + } + } + else + *(my_backend_status[i]) = BACKEND_INFO(i).backend_status; + /* Also adjust the socket array and count in backend end point */ + if (backend_end_point->backend_status[i] == CON_UP) + backend_end_point->backend_ids[backend_end_point->num_sockets++] = i; + } + return true; +} + +static bool +export_local_cluster_sockets_pool(void) +{ + int *sockets = NULL; + int num_sockets = 0; + + get_sockets_array(GetBackendClusterConnection()->backend_end_point, &sockets, &num_sockets, false); + if (sockets && num_sockets > 0) + { + bool ret; + ret = SendBackendSocktesToMainPool(ParentLinkFd, num_sockets, sockets); + pfree(sockets); + return ret; + } + else + ereport(LOG, + (errmsg("No socket found to transfer to to global connection pool"))); + + return true; +} + +static bool +export_local_cluster_connection_data_to_pool(void) +{ + BackendClusterConnection *current_backend_con = GetBackendClusterConnection(); + StartupPacket *sp = current_backend_con->sp; + int pool_id = current_backend_con->pool_id; + int i, sock_index; + PooledBackendClusterConnection *backend_end_point = GetGlobalPooledBackendClusterConnection(pool_id); + + if (backend_end_point == NULL) + return false; + + /* verify the length first */ + if (sp->len <= 0 || sp->len >= MAX_STARTUP_PACKET_LENGTH) + { + ereport(ERROR, + (errmsg("incorrect packet length (%d)", sp->len))); + return false; + } + + current_backend_con->backend_end_point = backend_end_point; + memcpy(backend_end_point->startup_packet_data, sp->startup_packet, sp->len); + backend_end_point->sp.len = sp->len; + backend_end_point->sp.startup_packet = backend_end_point->startup_packet_data; + + backend_end_point->sp.major = sp->major; + backend_end_point->sp.minor = sp->minor; + + StrNCpy(backend_end_point->database, sp->database, sizeof(backend_end_point->database)); + StrNCpy(backend_end_point->user, sp->user, sizeof(backend_end_point->user)); + + backend_end_point->sp.database = backend_end_point->database; + backend_end_point->sp.user = backend_end_point->user; + + if (sp->major == PROTO_MAJOR_V3 && sp->application_name) + { + /* adjust the application name pointer in new packet */ + backend_end_point->sp.application_name = backend_end_point->sp.startup_packet + (sp->application_name - sp->startup_packet); + } + else + backend_end_point->sp.application_name = NULL; + + sock_index = 0; + for (i = 0; i < NUM_BACKENDS; i++) + { + if (VALID_BACKEND_RAW(i)) + { + backend_end_point->conn_slots[i].key = current_backend_con->slots[i].key; + backend_end_point->conn_slots[i].pid = current_backend_con->slots[i].pid; + backend_end_point->backend_status[i] = CON_UP; + backend_end_point->backend_ids[sock_index++] = i; + } + else + { + backend_end_point->conn_slots[i].key = -1; + backend_end_point->conn_slots[i].pid = -1; + backend_end_point->conn_slots[i].socket = -1; + backend_end_point->backend_status[i] = *(my_backend_status[i]); + } + } + backend_end_point->num_sockets = sock_index; + get_pool_entry_for_pool_id(pool_id)->status = POOL_ENTRY_LOADED; + return true; +} + + +static void +import_pooled_startup_packet_into_child(PooledBackendClusterConnection *backend_end_point) +{ + + if (backend_end_point->sp.len <= 0 || backend_end_point->sp.len >= MAX_STARTUP_PACKET_LENGTH) + ereport(ERROR, + (errmsg("incorrect packet length (%d)", backend_end_point->sp.len))); + + ImportStartupPacketIntoChild(&backend_end_point->sp, backend_end_point->startup_packet_data); +} + + + + +/* + *********************************************** + * Global connection pool server side functions + *********************************************** + */ + +PooledBackendClusterConnection * +GetGlobalPooledBackendClusterConnection(int pool_id) +{ + ConnectionPoolEntry *pool_entry = get_pool_entry_for_pool_id(pool_id); + if (!pool_entry) + return NULL; + return &pool_entry->endPoint; +} + +bool +InstallSocketsInConnectionPool(ConnectionPoolEntry *pool_entry, int *sockets) +{ + int i; + + Assert(processType == PT_MAIN); + + if (!pool_entry) + return false; + + for (i = 0; i < pool_entry->endPoint.num_sockets; i++) + { + int slot_no = pool_entry->endPoint.backend_ids[i]; + pool_entry->endPoint.conn_slots[slot_no].socket = sockets[i]; + } + return true; +} + +bool +GlobalPoolReleasePooledConnection(ConnectionPoolEntry *pool_entry, IPC_Endpoint *ipc_endpoint, bool need_cleanup, bool discard) +{ + ProcessInfo *pro_info = NULL; + + if (processType != PT_MAIN) + { + ereport(ERROR, + (errmsg("only main process can unregister new pool lease"))); + return false; + } + + pro_info = pool_get_process_info_from_IPC_Endpoint(ipc_endpoint); + + if (pool_entry->child_pid != ipc_endpoint->child_pid) + { + ereport(WARNING, + (errmsg("child:%d leased:%d is not the borrower of pool_id:%d borrowed by:%d", + ipc_endpoint->child_pid, + pro_info->pool_id, + pool_entry->pool_id, + pool_entry->child_pid))); + return false; + } + if (discard) + { + pool_entry->status = POOL_ENTRY_EMPTY; + memset(&pool_entry->endPoint, 0, sizeof(PooledBackendClusterConnection)); + } + else + pool_entry->need_cleanup = need_cleanup; + + unregister_lease(pool_entry->pool_id, ipc_endpoint); + ereport(LOG, + (errmsg("child:%d released pool_id:%d database:%s user:%s", ipc_endpoint->child_pid, + pool_entry->pool_id, + pool_entry->endPoint.database, + pool_entry->endPoint.user))); + return true; +} + +void +GlobalPoolChildProcessDied(int child_id, pid_t child_pid) +{ + int i; + Assert(processType == PT_MAIN); + + for (i = 0; i < GPGetPoolEntriesCount(); i++) + { + if (ConnectionPool[i].child_pid == child_pid) + { + ConnectionPool[i].child_pid = -1; + ConnectionPool[i].child_id = -1; + ConnectionPool[i].need_cleanup = true; + ConnectionPool[i].status = POOL_ENTRY_EMPTY; + ConnectionPool[i].last_returned_time = time(NULL); + ConnectionPool[i].status = POOL_ENTRY_EMPTY; + memset(&ConnectionPool[i].endPoint, 0, sizeof(PooledBackendClusterConnection)); + break; /* Only one connection can be leased to any child */ + } + } +} + +/* If communication fails in this function. + * we will close the socket and hope child process + * will act sain +*/ +bool +GlobalPoolLeasePooledConnectionToChild(IPC_Endpoint *ipc_endpoint) +{ + ProcessInfo *child_proc_info; + + LEASE_TYPES lease_type = LEASE_TYPE_LEASE_FAILED; + int pool_id = -1; + bool ret; + + Assert (processType == PT_MAIN); + + if (ipc_endpoint->proc_info_id < 0 || ipc_endpoint->proc_info_id >= pool_config->num_init_children) + return false; + child_proc_info = &process_info[ipc_endpoint->proc_info_id]; + + if (child_proc_info->pid != ipc_endpoint->child_pid) + return false; + + pool_id = get_pooled_connection_for_lending(child_proc_info->user, + child_proc_info->database, + child_proc_info->major, + &lease_type); + if (pool_id >= 0) + { + ereport(DEBUG2, + (errmsg("pool_id:%d with lease type:%d reserved for child:%d", pool_id, lease_type, ipc_endpoint->child_pid))); + + ret = register_new_lease(pool_id, lease_type, ipc_endpoint); + if (ret == false) + { + ereport(LOG, + (errmsg("Failed to register (pool_id:%d) lease type:%d to child:%d", pool_id, lease_type, ipc_endpoint->child_pid))); + lease_type = LEASE_TYPE_LEASE_FAILED; + } + } + else + { + ereport(LOG, + (errmsg("No pooled connection for user:%s database:%s protoMajor:%d", child_proc_info->user, + child_proc_info->database, + child_proc_info->major))); + } + + ret = InformLeaseStatusToChild(ipc_endpoint->child_link, lease_type); + if (ret == false) + { + ereport(LOG, + (errmsg("Failed to send (pool_id:%d) lease type:%d to child:%d", pool_id, lease_type, ipc_endpoint->child_pid))); + unregister_lease(pool_id, ipc_endpoint); + close(ipc_endpoint->child_link); + ipc_endpoint->child_link = -1; + return false; + } + + if (lease_type == LEASE_TYPE_READY_TO_USE || lease_type == LEASE_TYPE_DISCART_AND_CREATE) + { + int *sockets = NULL; + int num_sockets = 0; + get_sockets_array(&ConnectionPool[pool_id].endPoint, &sockets, &num_sockets, true); + if (sockets && num_sockets > 0) + { + ret = TransferSocketsBetweenProcesses(ipc_endpoint->child_link, num_sockets, sockets); + pfree(sockets); + if (ret == false) + { + close(ipc_endpoint->child_link); + ipc_endpoint->child_link = -1; + unregister_lease(pool_id, ipc_endpoint); + } + return ret; + } + else + ereport(LOG, + (errmsg("No socket found to transfer to to child:%d", ipc_endpoint->child_pid))); + } + + return ret; +} + +static int +get_pooled_connection_for_lending(char *user, char *database, int protoMajor, LEASE_TYPES *lease_type) +{ + int i; + int free_pool_slot = -1; + int discard_pool_slot = -1; + bool found_good_victum = false; + time_t closetime = TMINTMAX; + + ereport(DEBUG2, + (errmsg("Finding for user:%s database:%s protoMajor:%d", user, database, protoMajor))); + + for (i = 0; i < GPGetPoolEntriesCount(); i++) + { + ereport(DEBUG2, + (errmsg("POOL:%d, STATUS:%d [%s:%s]", i, ConnectionPool[i].status, + ConnectionPool[i].endPoint.database, + ConnectionPool[i].endPoint.user))); + + if (ConnectionPool[i].status == POOL_ENTRY_CONNECTED && + ConnectionPool[i].child_pid <= 0 && + ConnectionPool[i].endPoint.sp.major == protoMajor) + { + if (strcmp(ConnectionPool[i].endPoint.user, user) == 0 && + strcmp(ConnectionPool[i].endPoint.database, database) == 0) + { + if (ConnectionPool[i].need_cleanup) + *lease_type = LEASE_TYPE_DISCART_AND_CREATE; + else + *lease_type = LEASE_TYPE_READY_TO_USE; + return i; + } + } + + if (free_pool_slot == -1 && ConnectionPool[i].status == POOL_ENTRY_EMPTY && ConnectionPool[i].child_pid <= 0) + free_pool_slot = i; + /* Also try calculating the victum in case we failed to find even a free connection */ + if (free_pool_slot == -1 && found_good_victum == false) + { + if (ConnectionPool[i].status == POOL_ENTRY_CONNECTED && + ConnectionPool[i].child_pid <= 0) + { + if (ConnectionPool[i].need_cleanup) + { + discard_pool_slot = i; + found_good_victum = true; + } + else if (ConnectionPool[i].last_returned_time < closetime) + { + closetime = ConnectionPool[i].last_returned_time; + discard_pool_slot = i; + } + } + } + } + + /* No pooled connection found. Return empty slot */ + if (free_pool_slot > -1) + { + *lease_type = LEASE_TYPE_EMPTY_SLOT_RESERVED; + return free_pool_slot; + } + else if (discard_pool_slot > -1) + { + *lease_type = LEASE_TYPE_DISCART_AND_CREATE; + return discard_pool_slot; + } + /* TODO Find the pooled entry that can be discarded and re-used */ + ereport(LOG, + (errmsg("No free slot available for user:%s database:%s protoMajor:%d", user, database, protoMajor))); + + /* Nothing found */ + *lease_type = LEASE_TYPE_NO_AVAILABLE_SLOT; + return -1; +} + + +static bool +unregister_lease(int pool_id, IPC_Endpoint *ipc_endpoint) +{ + ProcessInfo *child_proc_info; + ConnectionPoolEntry *pool_entry; + + if (processType != PT_MAIN) + { + ereport(ERROR, + (errmsg("only main process can unregister new pool lease"))); + return false; + } + if (pool_id < 0 || pool_id >= GPGetPoolEntriesCount()) + { + ereport(ERROR, + (errmsg("pool_id:%d is out of range", pool_id))); + return false; + } + pool_entry = &ConnectionPool[pool_id]; + + if (pool_entry->child_pid > 0 && pool_entry->child_pid != ipc_endpoint->child_pid) + { + ereport(ERROR, + (errmsg("pool_id:%d is leased to different child:%d", pool_id, pool_entry->child_pid))); + return false; + } + child_proc_info = &process_info[ipc_endpoint->proc_info_id]; + child_proc_info->pool_id = -1; + + ereport(DEBUG1, + (errmsg("pool_id:%d, is released from child:%d", pool_id, pool_entry->child_pid))); + + ConnectionPoolUnregisterLease(pool_entry, ipc_endpoint->proc_info_id, ipc_endpoint->child_pid); + + pool_entry->child_pid = -1; + pool_entry->child_id = -1; + + return true; +} + +static bool +register_new_lease(int pool_id, LEASE_TYPES lease_type, IPC_Endpoint *ipc_endpoint) +{ + ProcessInfo *child_proc_info; + ConnectionPoolEntry * pool_entry; + + if (processType != PT_MAIN) + { + ereport(ERROR, + (errmsg("only main process can register new pool lease"))); + return false; + } + if (pool_id < 0 || pool_id >= GPGetPoolEntriesCount()) + { + ereport(WARNING, + (errmsg("pool_id:%d is out of range", pool_id))); + return false; + } + pool_entry = &ConnectionPool[pool_id]; + + if (pool_entry->child_pid > 0 && pool_entry->child_pid != ipc_endpoint->child_pid) + { + ereport(WARNING, + (errmsg("pool_id:%d is already leased to child:%d", pool_id, pool_entry->child_pid))); + return false; + } + + ConnectionPoolRegisterNewLease(pool_entry, lease_type, ipc_endpoint->proc_info_id, ipc_endpoint->child_pid); + pool_entry->child_pid = ipc_endpoint->child_pid; + pool_entry->child_id = ipc_endpoint->proc_info_id; + ereport(LOG, + (errmsg("pool_id:%d, leased to child:%d", pool_id, pool_entry->child_pid))); + return true; +} diff --git a/src/context/pool_process_context.c b/src/context/pool_process_context.c index 00a04ff8..3b57dbcc 100644 --- a/src/context/pool_process_context.c +++ b/src/context/pool_process_context.c @@ -102,229 +102,56 @@ pool_increment_local_session_id(void) p->local_session_id++; } -/* - * Return byte size of connection info(ConnectionInfo) on shmem. - */ -size_t -pool_coninfo_size(void) -{ - size_t size; - - size = pool_config->num_init_children * - pool_config->max_pool * - MAX_NUM_BACKENDS * - sizeof(ConnectionInfo); - - ereport(DEBUG1, - (errmsg("pool_coninfo_size: num_init_children (%d) * max_pool (%d) * MAX_NUM_BACKENDS (%d) * sizeof(ConnectionInfo) (%zu) = %zu bytes requested for shared memory", - pool_config->num_init_children, - pool_config->max_pool, - MAX_NUM_BACKENDS, - sizeof(ConnectionInfo), - size))); - return size; -} - -/* - * Return number of elements of connection info(ConnectionInfo) on shmem. - */ -int -pool_coninfo_num(void) -{ - int nelm; - - nelm = pool_config->num_init_children * - pool_config->max_pool * - MAX_NUM_BACKENDS; - - return nelm; -} - -/* - * Return pointer to i th child, j th connection pool and k th backend - * of connection info on shmem. - */ -ConnectionInfo * -pool_coninfo(int child, int connection_pool, int backend) -{ - if (child < 0 || child >= pool_config->num_init_children) - { - ereport(WARNING, - (errmsg("failed to get connection info, invalid child number: %d", child))); - return NULL; - } - - if (connection_pool < 0 || connection_pool >= pool_config->max_pool) - { - ereport(WARNING, - (errmsg("failed to get connection info, invalid connection pool number: %d", connection_pool))); - return NULL; - } - - if (backend < 0 || backend >= MAX_NUM_BACKENDS) - { - ereport(WARNING, - (errmsg("failed to get connection info, invalid backend number: %d", backend))); - return NULL; - } - - return &con_info[child * pool_config->max_pool * MAX_NUM_BACKENDS + - connection_pool * MAX_NUM_BACKENDS + - backend]; -} - -/* - * Return pointer to child which has OS process id pid, j th connection - * pool and k th backend of connection info on shmem. - */ -ConnectionInfo * -pool_coninfo_pid(int pid, int connection_pool, int backend) -{ - int child = -1; - int i; - - for (i = 0; i < pool_config->num_init_children; i++) - { - if (process_info[i].pid == pid) - { - child = i; - break; - } - } - - if (child < 0) - elog(ERROR, "failed to get child pid, invalid child PID:%d", pid); - - if (child < 0 || child >= pool_config->num_init_children) - elog(ERROR, "failed to get child pid, invalid child no:%d", child); - - if (connection_pool < 0 || connection_pool >= pool_config->max_pool) - elog(ERROR, "failed to get child pid, invalid connection pool no:%d", connection_pool); - - if (backend < 0 || backend >= MAX_NUM_BACKENDS) - elog(ERROR, "failed to get child pid, invalid backend no:%d", backend); - - return &con_info[child * pool_config->max_pool * MAX_NUM_BACKENDS + - connection_pool * MAX_NUM_BACKENDS + - backend]; -} - -/* - * locate and return the shared memory ConnectionInfo having the - * backend connection with the pid - * if the connection is found the *backend_node_id contains the backend node id - * of the backend node that has the connection - */ -ConnectionInfo * -pool_coninfo_backend_pid(int backend_pid, int *backend_node_id) -{ - int child; - - /* - * look for the child process that has the backend with the pid - */ - - ereport(DEBUG1, - (errmsg("searching for the connection with backend pid:%d", backend_pid))); - - for (child = 0; child < pool_config->num_init_children; child++) - { - int pool; - - if (process_info[child].pid) - { - ProcessInfo *pi = pool_get_process_info(process_info[child].pid); - - for (pool = 0; pool < pool_config->max_pool; pool++) - { - int backend_id; - - for (backend_id = 0; backend_id < NUM_BACKENDS; backend_id++) - { - int poolBE = pool * MAX_NUM_BACKENDS + backend_id; - - if (ntohl(pi->connection_info[poolBE].pid) == backend_pid) - { - ereport(DEBUG1, - (errmsg("found the connection with backend pid:%d on backend node %d", backend_pid, backend_id))); - *backend_node_id = backend_id; - return &pi->connection_info[poolBE]; - } - } - } - } - - } - return NULL; -} - /* * sets the flag to mark that the connection will be terminated by the * backend and it should not be considered as a backend node failure. * This flag is used to handle pg_terminate_backend() */ void -pool_set_connection_will_be_terminated(ConnectionInfo * connInfo) +pool_set_connection_will_be_terminated(PooledBackendNodeConnection *backend_connection) { - connInfo->swallow_termination = 1; + backend_connection->swallow_termination = true; } void -pool_unset_connection_will_be_terminated(ConnectionInfo * connInfo) +pool_unset_connection_will_be_terminated(PooledBackendNodeConnection *backend_connection) { - connInfo->swallow_termination = 0; + backend_connection->swallow_termination = false; } /* * Set frontend connected flag */ void -pool_coninfo_set_frontend_connected(int proc_id, int pool_index) +pool_coninfo_set_frontend_connected(void) { - ConnectionInfo *con; - int i; - - for (i = 0; i < NUM_BACKENDS; i++) + BackendClusterConnection *backend_con = GetBackendClusterConnection(); + if (!backend_con->backend_end_point) { - if (!VALID_BACKEND(i)) - continue; - - con = pool_coninfo(proc_id, pool_index, i); - - if (con == NULL) - { - elog(WARNING, "failed to get connection info while marking the frontend is connected for pool"); + elog(WARNING, "failed to get pooled connection for child process while marking the frontend is connected for pool"); return; - } - con->connected = true; - con->client_connection_time = time(NULL); } + + backend_con->backend_end_point->client_connected = true; + backend_con->backend_end_point->client_connection_time = time(NULL); + backend_con->backend_end_point->client_connection_count++; } /* * Unset frontend connected flag */ void -pool_coninfo_unset_frontend_connected(int proc_id, int pool_index) +pool_coninfo_unset_frontend_connected(void) { - ConnectionInfo *con; - int i; - - for (i = 0; i < NUM_BACKENDS; i++) + BackendClusterConnection *backend_con = GetBackendClusterConnection(); + if (!backend_con->backend_end_point) { - if (!VALID_BACKEND(i)) - continue; - - con = pool_coninfo(proc_id, pool_index, i); - - if (con == NULL) - { - elog(WARNING, "failed to get connection info while marking the frontend is not connected for pool"); + elog(WARNING, "failed to get pooled connection for child process while marking the frontend is not connected for pool"); return; - } - con->connected = false; - con->client_disconnection_time = time(NULL); } + backend_con->backend_end_point->client_connected = false; + backend_con->backend_end_point->client_disconnection_time = time(NULL); + backend_con->backend_end_point->client_disconnection_count++; } /* diff --git a/src/context/pool_query_context.c b/src/context/pool_query_context.c index f484b77a..5394029b 100644 --- a/src/context/pool_query_context.c +++ b/src/context/pool_query_context.c @@ -538,7 +538,7 @@ pool_send_and_wait(POOL_QUERY_CONTEXT * query_context, { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION *frontend; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; bool is_commit; bool is_begin_read_write; int i; @@ -660,8 +660,8 @@ pool_send_and_wait(POOL_QUERY_CONTEXT * query_context, wait_for_query_response_with_trans_cleanup(frontend, CONNECTION(backend, i), MAJOR(backend), - MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key); + MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key); /* * Check if some error detected. If so, emit log. This is useful when @@ -695,7 +695,7 @@ pool_extended_send_and_wait(POOL_QUERY_CONTEXT * query_context, { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION *frontend; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; bool is_commit; bool is_begin_read_write; int i; @@ -877,8 +877,8 @@ pool_extended_send_and_wait(POOL_QUERY_CONTEXT * query_context, wait_for_query_response_with_trans_cleanup(frontend, CONNECTION(backend, i), MAJOR(backend), - MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key); + MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key); /* * Check if some error detected. If so, emit log. This is useful @@ -1973,7 +1973,7 @@ where_to_send_main_replica(POOL_QUERY_CONTEXT * query_context, char *query, Node { POOL_DEST dest; POOL_SESSION_CONTEXT *session_context; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; dest = send_to_where(node); session_context = pool_get_session_context(false); @@ -2179,7 +2179,7 @@ static void where_to_send_native_replication(POOL_QUERY_CONTEXT * query_context, char *query, Node *node) { POOL_SESSION_CONTEXT *session_context; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; session_context = pool_get_session_context(false); backend = session_context->backend; diff --git a/src/context/pool_session_context.c b/src/context/pool_session_context.c index f87eee07..ee40cd52 100644 --- a/src/context/pool_session_context.c +++ b/src/context/pool_session_context.c @@ -52,12 +52,11 @@ static int Elevel = DEBUG2; * Initialize per session context */ void -pool_init_session_context(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +pool_init_session_context(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { session_context = &session_context_d; ProcessInfo *process_info; int node_id; - int i; /* Clear session context memory */ memset(&session_context_d, 0, sizeof(session_context_d)); @@ -107,11 +106,7 @@ pool_init_session_context(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * bac session_context->load_balance_node_id = node_id; - for (i = 0; i < NUM_BACKENDS; i++) - { - pool_coninfo(session_context->process_context->proc_id, - pool_pool_index(), i)->load_balancing_node = node_id; - } + backend->backend_end_point->load_balancing_node = node_id; ereport(DEBUG5, (errmsg("initializing session context"), @@ -200,6 +195,28 @@ pool_session_context_destroy(void) session_context = NULL; } +void +pool_select_new_load_balance_node(bool noerror) +{ + POOL_SESSION_CONTEXT *session_context = pool_get_session_context(noerror); + if (session_context) + { + int node_id; + PooledBackendClusterConnection *backend_end_point = GetCurrentPooledBackendClusterConnection(); + if (!RAW_MODE && pool_config->load_balance_mode) + { + node_id = select_load_balancing_node(); + } + else + { + node_id = SL_MODE ? PRIMARY_NODE_ID : MAIN_NODE_ID; + } + + session_context->load_balance_node_id = node_id; + if (backend_end_point) + backend_end_point->load_balancing_node = node_id; + } +} /* * Return session context */ diff --git a/src/include/auth/pool_auth.h b/src/include/auth/pool_auth.h index 08a21c8a..c670a018 100644 --- a/src/include/auth/pool_auth.h +++ b/src/include/auth/pool_auth.h @@ -22,9 +22,13 @@ #ifndef pool_auth_h #define pool_auth_h -extern void connection_do_auth(POOL_CONNECTION_POOL_SLOT * cp, char *password); -extern int pool_do_auth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern int pool_do_reauth(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * cp); +#include "connection_pool/connection_pool.h" + +extern void connection_do_auth(BackendClusterConnection *backend_con, int slot_no, + BackendNodeConnection *con_slot, POOL_CONNECTION *frontend, char *password, StartupPacket *sp); + +extern int pool_do_auth(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern int pool_do_reauth(POOL_CONNECTION * frontend, BackendClusterConnection * cp); extern void authenticate_frontend(POOL_CONNECTION * frontend); extern void pool_random_salt(char *md5Salt); diff --git a/src/include/auth/pool_hba.h b/src/include/auth/pool_hba.h index a6e0a153..d7d428f7 100644 --- a/src/include/auth/pool_hba.h +++ b/src/include/auth/pool_hba.h @@ -28,6 +28,7 @@ #include "parser/pg_list.h" #include "pool.h" +#include "connection_pool/backend_connection.h" #ifdef USE_LDAP #define LDAP_DEPRECATED 1 diff --git a/src/include/connection_pool/backend_connection.h b/src/include/connection_pool/backend_connection.h new file mode 100644 index 00000000..fc1bdcf9 --- /dev/null +++ b/src/include/connection_pool/backend_connection.h @@ -0,0 +1,217 @@ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +#ifndef BACKEND_CONNECTION_H +#define BACKEND_CONNECTION_H + +#include "pool.h" + +typedef struct PooledBackendNodeConnection +{ + // ConnectionInfo conn_info; + int pid; /* backend process id */ + int key; /* cancel key */ + int counter; /* used counter */ + time_t create_time; /* connection creation time */ + int socket; + bool connected; + volatile bool need_reconnect; + char salt[4]; /* password salt */ + + volatile bool swallow_termination; +} PooledBackendNodeConnection; + +typedef struct PooledBackendClusterConnection +{ + char database[SM_DATABASE]; /* Database name */ + char user[SM_USER]; /* User name */ + ParamStatus params; + int load_balancing_node; + char startup_packet_data[MAX_STARTUP_PACKET_LENGTH]; /* startup packet info */ + StartupPacket sp; /* startup packet info */ + // int key; /* cancel key */ + /* + * following are used to remember when re-use the authenticated connection + */ + int auth_kind; /* 3: clear text password, 4: crypt password, + * 5: md5 password */ + int pwd_size; /* password (sent back from frontend) size in + * host order */ + char password[MAX_PASSWORD_SIZE + 1]; /* password (sent back + * from frontend) */ + PasswordType passwordType; + + time_t client_connection_time; + time_t client_disconnection_time; + time_t create_time; + bool client_connected; + int client_connection_count; + int client_disconnection_count; + + int num_sockets; + int backend_ids[MAX_NUM_BACKENDS]; + PooledBackendNodeConnection conn_slots[MAX_NUM_BACKENDS]; + BACKEND_STATUS backend_status[MAX_NUM_BACKENDS]; /* Backend status of each node*/ + int primary_node_id; /* the primary node id in streaming + * replication mode at the time of connection*/ + volatile sig_atomic_t node_status_changed; + time_t node_status_last_changed_time; + +} PooledBackendClusterConnection; + +/* + * stream connection structure + */ +typedef struct +{ + int fd; /* fd for connection */ + + char *wbuf; /* write buffer for the connection */ + int wbufsz; /* write buffer size */ + int wbufpo; /* buffer offset */ + +#ifdef USE_SSL + SSL_CTX *ssl_ctx; /* SSL connection context */ + SSL *ssl; /* SSL connection */ + X509 *peer; + char *cert_cn; /* common in the ssl certificate presented by + * frontend connection Used for cert + * authentication */ + bool client_cert_loaded; + +#endif + int ssl_active; /* SSL is failed if < 0, off if 0, on if > 0 */ + + char *hp; /* pending data buffer head address */ + int po; /* pending data offset */ + int bufsz; /* pending data buffer size */ + int len; /* pending data length */ + + char *sbuf; /* buffer for pool_read_string */ + int sbufsz; /* its size in bytes */ + + char *buf2; /* buffer for pool_read2 */ + int bufsz2; /* its size in bytes */ + + char *buf3; /* buffer for pool_push/pop */ + int bufsz3; /* its size in bytes */ + + int isbackend; /* this connection is for backend if non 0 */ + int db_node_id; /* DB node id for this connection */ + + char tstate; /* Transaction state (V3 only) 'I' if idle + * (not in a transaction block); 'T' if in a + * transaction block; or 'E' if in a failed + * transaction block */ + + /* True if an internal transaction has already started */ + bool is_internal_transaction_started; + + /* + * following are used to remember when re-use the authenticated connection + */ + int auth_kind; /* 3: clear text password, 4: crypt password, + * 5: md5 password */ + int pwd_size; /* password (sent back from frontend) size in + * host order */ + char password[MAX_PASSWORD_SIZE + 1]; /* password (sent back + * from frontend) */ + char salt[4]; /* password salt */ + PasswordType passwordType; + + /* + * following are used to remember current session parameter status. + * re-used connection will need them (V3 only) + */ + // ParamStatus params; + + int no_forward; /* if non 0, do not write to frontend */ + + char kind; /* kind cache */ + + /* true if remote end closed the connection */ + POOL_SOCKET_STATE socket_state; + + /* + * frontend info needed for hba + */ + int protoVersion; + SockAddr raddr; + HbaLine *pool_hba; + char *database; + char *username; + char *remote_hostname; + int remote_hostname_resolv; + bool frontend_authenticated; + PasswordMapping *passwordMapping; + PooledBackendNodeConnection *pooled_backend_ref; /* points to shared mem */ +} POOL_CONNECTION; + +typedef enum BACKEND_CONNECTION_STATES +{ + CONNECTION_SLOT_EMPTY = 0, + CONNECTION_SLOT_LOADED_FROM_BACKEND, + CONNECTION_SLOT_SOCKET_CONNECTION_ERROR, + CONNECTION_SLOT_SOCKET_CONNECTION_ONLY, + CONNECTION_SLOT_VALID_LOCAL_CONNECTION, + CONNECTION_SLOT_AUTHENTICATION_OK +} BACKEND_CONNECTION_STATES; + +typedef struct BackendNodeConnection +{ + int pid; /* backend pid */ + int key; /* cancel key */ + time_t closetime; /* absolute time in second when the connection + * closed if 0, that means the connection is + * under use. */ + BACKEND_CONNECTION_STATES state; + POOL_CONNECTION *con; +} BackendNodeConnection; + + +typedef struct BackendClusterConnection +{ + StartupPacket *sp; /* startup packet info */ + LEASE_TYPES lease_type; /* lease type */ + PooledBackendClusterConnection *backend_end_point; /* Reference to global pool end point in shared mem */ + int pool_id; /* global pool id */ + bool need_push_back; /* true if this connection needs to be pushed back to global pool */ + BackendNodeConnection slots[MAX_NUM_BACKENDS]; +} BackendClusterConnection; + +extern BackendClusterConnection* GetBackendClusterConnection(void); +extern bool ConnectBackendClusterSockets(void); +extern void ResetBackendClusterConnection(void); +extern PooledBackendClusterConnection* GetCurrentPooledBackendClusterConnection(void); +extern bool StorePasswordInformation(char *password, int pwd_size, PasswordType passwordType); +extern bool SaveAuthKindForBackendConnection(int auth_kind); +extern int GetAuthKindForCurrentPoolBackendConnection(void); +extern void ImportStartupPacketIntoChild(StartupPacket *sp, char *startup_packet_data); +extern bool ConnectBackendSocketForBackendCluster(int slot_no); +extern bool DiscardCurrentBackendConnection(void); + +extern int check_socket_status(int fd); + /* in pgpool_main.c */ + extern POOL_NODE_STATUS * + verify_backend_node_status(BackendNodeConnection **slots); +/* worker_child*/ +extern int get_query_result(BackendNodeConnection **slots, int backend_id, char *query, POOL_SELECT_RESULT **res); + +#endif // BACKEND_CONNECTION_H diff --git a/src/include/connection_pool/connection_pool.h b/src/include/connection_pool/connection_pool.h new file mode 100644 index 00000000..a78f15ae --- /dev/null +++ b/src/include/connection_pool/connection_pool.h @@ -0,0 +1,114 @@ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +#ifndef CONNECTION_POOL_H +#define CONNECTION_POOL_H + +#include "pool.h" +#include "connection_pool/backend_connection.h" + +#define TMINTMAX 0x7fffffff + +typedef enum POOL_ENTRY_STATUS +{ + POOL_ENTRY_EMPTY = 0, + POOL_ENTRY_LOADED, /* Intermediate status when pool info is populated + * by child process but sockets are yet to be transfered */ + POOL_ENTRY_CONNECTED +} POOL_ENTRY_STATUS; + +/* This structure crossponds to the backend connection + * in the global connection pool. + */ +typedef struct ConnectionPoolEntry +{ + PooledBackendClusterConnection endPoint; + POOL_ENTRY_STATUS status; + pid_t child_pid; + int pool_id; + int child_id; + bool need_cleanup; + time_t leased_time; + time_t last_returned_time; + int leased_count; +} ConnectionPoolEntry; + +typedef struct BorrowConnectionRes +{ + LEASE_TYPES lease_type; + int pool_id; + void *context; +} BorrowConnectionRes; + +typedef struct ConnectionPoolRoutine +{ + size_t (*RequiredSharedMemSize)(void); + void (*InitializeConnectionPool) (void* shared_mem_ptr); + void (*LoadChildConnectionPool)(int intarg); + BorrowConnectionRes* (*BorrowClusterConnection)(char *database, char *user, int major, int minor); + bool (*LoadBorrowedConnection)(BorrowConnectionRes *context); + bool (*ReleaseClusterConnection) (bool discard); + bool (*SyncClusterConnectionDataInPool) (void); + bool (*PushClusterConnectionToPool) (void); + void (*ReleaseChildConnectionPool) (void); + PooledBackendNodeConnection *(*GetBackendNodeConnectionForBackendPID)(int backend_pid, int *backend_node_id); /* Optional overload */ + PooledBackendClusterConnection* (*GetBackendEndPointForCancelPacket) (CancelPacket *cp); /* optional overload */ + bool (*ClusterConnectionNeedPush)(void); + const char *(*GetConnectionPoolInfo)(void); + void (*UpdatePooledConnectionCount)(void); + int (*GetPoolEntriesCount)(void); + ConnectionPoolEntry *(*GetConnectionPoolEntry)(int pool_idx, int child_id); +} ConnectionPoolRoutine; + +extern void InstallConnectionPool(const ConnectionPoolRoutine *ConnectionPoolRoutine); +extern int GetPoolEntriesCount(void); +extern size_t ConnectionPoolRequiredSharedMemSize(void); +extern const char *GetConnectionPoolInfo(void); +extern void InitializeConnectionPool(void *shared_mem_ptr); +extern void LoadChildConnectionPool(int intarg); +extern BorrowConnectionRes *BorrowClusterConnection(char *database, char *user, int major, int minor); +extern bool LoadBorrowedConnection(BorrowConnectionRes *context); +extern bool ReleaseClusterConnection(bool discard); +extern bool PushClusterConnectionToPool(void); +extern bool SyncClusterConnectionDataInPool(void); +extern void ReleaseChildConnectionPool(void); +extern bool ClusterConnectionNeedPush(void); +extern PooledBackendNodeConnection *GetBackendNodeConnectionForBackendPID(int backend_pid, int *backend_node_id); +extern PooledBackendClusterConnection *GetBackendEndPointForCancelPacket(CancelPacket *cp); +extern ConnectionPoolEntry *GetConnectionPoolEntryAtIndex(int pool_idx); +extern ConnectionPoolEntry *GetConnectionPoolEntry(int pool_id, int child_id); +extern void UpdatePooledConnectionCount(void); + +extern bool ConnectionPoolRegisterNewLease(ConnectionPoolEntry *pool_entry, LEASE_TYPES lease_type, int child_id, pid_t child_pid); +extern bool ConnectionPoolUnregisterLease(ConnectionPoolEntry* pool_entry, int child_id, pid_t child_pid); + +extern int InvalidateAllPooledConnections(char *database); +extern int InvalidateNodeInPooledConnections(int node_id); + +/* from global_connection_pool.c */ +extern const ConnectionPoolRoutine *GetGlobalConnectionPool(void); +extern bool InstallSocketsInConnectionPool(ConnectionPoolEntry *pool_entry, int *sockets); +extern PooledBackendClusterConnection *GetGlobalPooledBackendClusterConnection(int pool_id); +extern bool GlobalPoolReleasePooledConnection(ConnectionPoolEntry *pool_entry, IPC_Endpoint *ipc_endpoint, bool need_cleanup, bool discard); +extern bool GlobalPoolLeasePooledConnectionToChild(IPC_Endpoint *ipc_endpoint); +extern void GlobalPoolChildProcessDied(int child_id, pid_t child_pid); +/* from classic_connection_pool.c */ +extern const ConnectionPoolRoutine *GetClassicConnectionPool(void); +#endif // CONNECTION_POOL_H diff --git a/src/include/context/pool_process_context.h b/src/include/context/pool_process_context.h index a9f9e100..421ece38 100644 --- a/src/include/context/pool_process_context.h +++ b/src/include/context/pool_process_context.h @@ -29,7 +29,7 @@ //#include "pool.h" #include "pcp/libpcp_ext.h" #include "utils/pool_signal.h" - +#include "connection_pool/connection_pool.h" /* * Child process context: @@ -63,16 +63,11 @@ extern void pool_init_process_context(void); extern POOL_PROCESS_CONTEXT * pool_get_process_context(void); extern ProcessInfo * pool_get_my_process_info(void); extern void pool_increment_local_session_id(void); -extern size_t pool_coninfo_size(void); -extern int pool_coninfo_num(void); -extern ConnectionInfo * pool_coninfo(int child, int connection_pool, int backend); -extern ConnectionInfo * pool_coninfo_pid(int pid, int connection_pool, int backend); -extern void pool_coninfo_set_frontend_connected(int proc_id, int pool_index); -extern void pool_coninfo_unset_frontend_connected(int proc_id, int pool_index); +extern void pool_coninfo_set_frontend_connected(void); +extern void pool_coninfo_unset_frontend_connected(void); -extern ConnectionInfo * pool_coninfo_backend_pid(int backend_pid, int *backend_node_id); -extern void pool_set_connection_will_be_terminated(ConnectionInfo * connInfo); -extern void pool_unset_connection_will_be_terminated(ConnectionInfo * connInfo); +extern void pool_set_connection_will_be_terminated(PooledBackendNodeConnection *backend_connection); +extern void pool_unset_connection_will_be_terminated(PooledBackendNodeConnection *backend_connection); extern void pool_alarm(pool_sighandler_t handler, unsigned int second); extern void pool_undo_alarm(void); diff --git a/src/include/context/pool_query_context.h b/src/include/context/pool_query_context.h index d35c0498..ac44f956 100644 --- a/src/include/context/pool_query_context.h +++ b/src/include/context/pool_query_context.h @@ -28,6 +28,7 @@ #include "pool.h" #include "pool_process_context.h" +#include "connection_pool/connection_pool.h" #include "parser/nodes.h" #include "parser/parsenodes.h" #include "utils/palloc.h" @@ -77,7 +78,7 @@ typedef struct * query. */ int num_original_params; /* number of parameters in original * query */ - ConnectionInfo *pg_terminate_backend_conn; + PooledBackendNodeConnection *pg_terminate_backend_conn; /* * pointer to the shared memory connection info object referred by diff --git a/src/include/context/pool_session_context.h b/src/include/context/pool_session_context.h index 192ce2d6..02f38179 100644 --- a/src/include/context/pool_session_context.h +++ b/src/include/context/pool_session_context.h @@ -178,7 +178,7 @@ typedef struct { POOL_PROCESS_CONTEXT *process_context; /* belonging process */ POOL_CONNECTION *frontend; /* connection to frontend */ - POOL_CONNECTION_POOL *backend; /* connection to backends */ + BackendClusterConnection *backend; /* connection to backends */ /* * If true, we are waiting for backend response. For SELECT this flags @@ -322,9 +322,10 @@ typedef struct * multi-statement-query */ } POOL_SESSION_CONTEXT; -extern void pool_init_session_context(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +extern void pool_init_session_context(POOL_CONNECTION * frontend, BackendClusterConnection * backend); extern void pool_session_context_destroy(void); extern POOL_SESSION_CONTEXT * pool_get_session_context(bool noerror); +extern void pool_select_new_load_balance_node(bool noerror); extern int pool_get_local_session_id(void); extern bool pool_is_query_in_progress(void); extern void pool_set_query_in_progress(void); diff --git a/src/include/main/pgpool_ipc.h b/src/include/main/pgpool_ipc.h new file mode 100644 index 00000000..b5d55414 --- /dev/null +++ b/src/include/main/pgpool_ipc.h @@ -0,0 +1,47 @@ +/* + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + */ + + +#ifndef POOL_IPC_H +#define POOL_IPC_H + +#include "pool.h" +typedef enum +{ + OPERATION_SUCCESS = 0, + OPERATION_FAILED, + CONNECTION_FAILED, + SOCKETS_RECEIVED, + DISCARD_AND_RECREATE, + EMPTY_POOL_SLOT_RESERVED, + NO_POOL_SLOT_AVAILABLE, + NON_POOL_CONNECTION, + INVALID_OPERATION, + INVALID_RESPONSE +} POOL_IPC_RETURN_CODES; + +extern LEASE_TYPES BorrowBackendConnection(int parent_link, char* database, + char* user, int major, int minor, int *count, int* sockets); +extern bool ProcessChildRequestOnMain(IPC_Endpoint* ipc_endpoint); +extern bool TransferSocketsBetweenProcesses(int process_link, int count, int *sockets); +extern bool InformLeaseStatusToChild(int child_link, LEASE_TYPES lease_type); +extern bool ReleasePooledConnectionFromChild(int parent_link, bool discard); +extern bool SendBackendSocktesToMainPool(int parent_link, int count, int *sockets); +#endif /* POOL_IPC_H */ diff --git a/src/include/parser/pool_parser.h b/src/include/parser/pool_parser.h index b0045d3f..df157329 100644 --- a/src/include/parser/pool_parser.h +++ b/src/include/parser/pool_parser.h @@ -30,21 +30,6 @@ extern int server_version_num; /* from include/postgresql_ext.h */ #define InvalidOid ((Oid) 0) -/* from include/c.h */ - -/* - * CppAsString - * Convert the argument to a string, using the C preprocessor. - * CppConcat - * Concatenate two arguments together, using the C preprocessor. - * - * Note: There used to be support here for pre-ANSI C compilers that didn't - * support # and ##. Nowadays, these macros are just for clarity and/or - * backward compatibility with existing PostgreSQL code. - */ -#define CppAsString(identifier) #identifier -#define CppConcat(x, y) x##y - /* * Index * Index into any memory resident array. diff --git a/src/include/pcp/libpcp_ext.h b/src/include/pcp/libpcp_ext.h index 32dfcb00..1b114d66 100644 --- a/src/include/pcp/libpcp_ext.h +++ b/src/include/pcp/libpcp_ext.h @@ -176,8 +176,6 @@ typedef struct time_t start_time; /* fork() time */ char connected; /* if not 0 this process is already used*/ int wait_for_connect; /* waiting time for client connection (s) */ - ConnectionInfo *connection_info; /* head of the connection info for - * this process */ int client_connection_count; /* how many times clients used this process */ ProcessStatus status; bool need_to_restart; /* If non 0, exit this child process as @@ -187,6 +185,18 @@ typedef struct bool exit_if_idle; int pooled_connections; /* Total number of pooled connections * by this child */ + /* Following fields are used to request connection from main process */ + char database[SM_DATABASE]; /* Database name */ + char user[SM_USER]; /* User name */ + int major; /* protocol major version */ + int minor; /* protocol minor version */ + /* + * Following are used by main process to return the info to child + * We do not lock these fields and make sure the client only read + * this after receiving the indication from main process + */ + int pool_id; + } ProcessInfo; /* diff --git a/src/include/pool.h b/src/include/pool.h index 8fa429fa..5b5df410 100644 --- a/src/include/pool.h +++ b/src/include/pool.h @@ -4,7 +4,7 @@ * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * - * Copyright (c) 2003-2023 PgPool Global Development Group + * Copyright (c) 2003-2024 PgPool Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -84,6 +84,12 @@ #define RETRY (-2) #define OPERATION_TIMEOUT (-3) +typedef struct +{ + int child_link; + pid_t child_pid; + int proc_info_id; +} IPC_Endpoint; typedef enum { @@ -103,13 +109,18 @@ typedef enum POOL_SOCKET_EOF } POOL_SOCKET_STATE; -/* protocol major version numbers */ -#define PROTO_MAJOR_V2 2 -#define PROTO_MAJOR_V3 3 +#define MAX_PASSWORD_SIZE 1024 -/* Cancel packet proto major */ -#define PROTO_CANCEL 80877102 +#define NODE_STATUS_SYNC 0x00 +#define NODE_STATUS_NODE_REMOVED 0x02 +#define NODE_STATUS_NODE_ATTACHED 0x04 +#define NODE_STATUS_NODE_PRIMARY_CHANGED 0x08 +/* + * HbaLines is declared in pool_hba.h + * we use forward declaration here + */ +typedef struct HbaLine HbaLine; /* * In protocol 3.0 and later, the startup packet length is not fixed, but * we set an arbitrary limit on it anyway. This is just to prevent simple @@ -118,161 +129,60 @@ typedef enum */ #define MAX_STARTUP_PACKET_LENGTH 10000 +/* protocol major version numbers */ +#define PROTO_MAJOR_V2 2 +#define PROTO_MAJOR_V3 3 + +/* Cancel packet proto major */ +#define PROTO_CANCEL 80877102 typedef struct StartupPacket_v2 { - int protoVersion; /* Protocol version */ - char database[SM_DATABASE]; /* Database name */ - char user[SM_USER]; /* User name */ - char options[SM_OPTIONS]; /* Optional additional args */ - char unused[SM_UNUSED]; /* Unused */ - char tty[SM_TTY]; /* Tty for debug output */ -} StartupPacket_v2; + int protoVersion; /* Protocol version */ + char database[SM_DATABASE]; /* Database name */ + char user[SM_USER]; /* User name */ + char options[SM_OPTIONS]; /* Optional additional args */ + char unused[SM_UNUSED]; /* Unused */ + char tty[SM_TTY]; /* Tty for debug output */ +} StartupPacket_v2; /* startup packet info */ typedef struct { - char *startup_packet; /* raw startup packet without packet length - * (malloced area) */ - int len; /* raw startup packet length */ - int major; /* protocol major version */ - int minor; /* protocol minor version */ - char *database; /* database name in startup_packet (malloced - * area) */ - char *user; /* user name in startup_packet (malloced area) */ - char *application_name; /* not malloced. pointing to in - * startup_packet */ + char *startup_packet; /* raw startup packet without packet length + * (malloced area) */ + int len; /* raw startup packet length */ + int major; /* protocol major version */ + int minor; /* protocol minor version */ + char *database; /* database name in startup_packet (malloced + * area) */ + char *user; /* user name in startup_packet (malloced area) */ + char *application_name; /* not malloced. pointing to in + * startup_packet */ } StartupPacket; typedef struct CancelPacket { - int protoVersion; /* Protocol version */ - int pid; /* backend process id */ - int key; /* cancel key */ -} CancelPacket; - -#define MAX_PASSWORD_SIZE 1024 - + int protoVersion; /* Protocol version */ + int pid; /* backend process id */ + int key; /* cancel key */ +} CancelPacket; -/* - * HbaLines is declared in pool_hba.h - * we use forward declaration here - */ -typedef struct HbaLine HbaLine; - - -/* - * stream connection structure - */ -typedef struct -{ - int fd; /* fd for connection */ - - char *wbuf; /* write buffer for the connection */ - int wbufsz; /* write buffer size */ - int wbufpo; /* buffer offset */ - -#ifdef USE_SSL - SSL_CTX *ssl_ctx; /* SSL connection context */ - SSL *ssl; /* SSL connection */ - X509 *peer; - char *cert_cn; /* common in the ssl certificate presented by - * frontend connection Used for cert - * authentication */ - bool client_cert_loaded; - -#endif - int ssl_active; /* SSL is failed if < 0, off if 0, on if > 0 */ - - char *hp; /* pending data buffer head address */ - int po; /* pending data offset */ - int bufsz; /* pending data buffer size */ - int len; /* pending data length */ - - char *sbuf; /* buffer for pool_read_string */ - int sbufsz; /* its size in bytes */ - - char *buf2; /* buffer for pool_read2 */ - int bufsz2; /* its size in bytes */ - - char *buf3; /* buffer for pool_push/pop */ - int bufsz3; /* its size in bytes */ - - int isbackend; /* this connection is for backend if non 0 */ - int db_node_id; /* DB node id for this connection */ - - char tstate; /* Transaction state (V3 only) 'I' if idle - * (not in a transaction block); 'T' if in a - * transaction block; or 'E' if in a failed - * transaction block */ - - /* True if an internal transaction has already started */ - bool is_internal_transaction_started; - - /* - * following are used to remember when re-use the authenticated connection - */ - int auth_kind; /* 3: clear text password, 4: crypt password, - * 5: md5 password */ - int pwd_size; /* password (sent back from frontend) size in - * host order */ - char password[MAX_PASSWORD_SIZE + 1]; /* password (sent back - * from frontend) */ - char salt[4]; /* password salt */ - PasswordType passwordType; - - /* - * following are used to remember current session parameter status. - * re-used connection will need them (V3 only) - */ - ParamStatus params; - - int no_forward; /* if non 0, do not write to frontend */ - - char kind; /* kind cache */ - - /* true if remote end closed the connection */ - POOL_SOCKET_STATE socket_state; - - /* - * frontend info needed for hba - */ - int protoVersion; - SockAddr raddr; - HbaLine *pool_hba; - char *database; - char *username; - char *remote_hostname; - int remote_hostname_resolv; - bool frontend_authenticated; - PasswordMapping *passwordMapping; - ConnectionInfo *con_info; /* shared memory coninfo used for handling the - * query containing pg_terminate_backend */ -} POOL_CONNECTION; - -/* - * connection pool structure - */ -typedef struct +typedef enum LEASE_TYPES { - StartupPacket *sp; /* startup packet info */ - int pid; /* backend pid */ - int key; /* cancel key */ - POOL_CONNECTION *con; - time_t closetime; /* absolute time in second when the connection - * closed if 0, that means the connection is - * under use. */ -} POOL_CONNECTION_POOL_SLOT; - -typedef struct -{ - ConnectionInfo *info; /* connection info on shmem */ - POOL_CONNECTION_POOL_SLOT *slots[MAX_NUM_BACKENDS]; -} POOL_CONNECTION_POOL; - + LEASE_TYPE_INVALID, + LEASE_TYPE_FREE, + LEASE_TYPE_READY_TO_USE, + LEASE_TYPE_DISCART_AND_CREATE, + LEASE_TYPE_EMPTY_SLOT_RESERVED, + LEASE_TYPE_NO_AVAILABLE_SLOT, + LEASE_TYPE_NON_POOL_CONNECTION, + LEASE_TYPE_LEASE_FAILED +} LEASE_TYPES; /* Defined in pool_session_context.h */ -extern int pool_get_major_version(void); +extern int +pool_get_major_version(void); /* NUM_BACKENDS now always returns actual number of backends */ #define NUM_BACKENDS (pool_config->backend_desc->num_backends) @@ -304,7 +214,7 @@ extern int my_main_node_id; (*(my_backend_status[(backend_id)]) == CON_CONNECT_WAIT)) #define CONNECTION_SLOT(p, slot) ((p)->slots[(slot)]) -#define CONNECTION(p, slot) (CONNECTION_SLOT(p, slot)->con) +#define CONNECTION(p, slot) (CONNECTION_SLOT(p, slot).con) /* * The first DB node id appears in pgpool.conf or the first "live" DB @@ -335,7 +245,7 @@ extern int my_main_node_id; #define MAIN_NODE_ID (pool_virtual_main_db_node_id()) #define IS_MAIN_NODE_ID(node_id) (MAIN_NODE_ID == (node_id)) #define MAIN_CONNECTION(p) ((p)->slots[MAIN_NODE_ID]) -#define MAIN(p) MAIN_CONNECTION(p)->con +#define MAIN(p) MAIN_CONNECTION(p).con /* * Backend node status in streaming replication mode. @@ -567,7 +477,6 @@ extern volatile sig_atomic_t health_check_timer_expired; /* non 0 if health chec * timer expired */ extern int my_proc_id; /* process table id (!= UNIX's PID) */ extern ProcessInfo * process_info; /* shmem process information table */ -extern ConnectionInfo * con_info; /* shmem connection info table */ extern POOL_REQUEST_INFO * Req_info; extern volatile sig_atomic_t *InRecovery; extern volatile sig_atomic_t got_sighup; @@ -600,7 +509,7 @@ extern void pcp_main(int *fds); /*child.c*/ -extern void do_child(int *fds); +extern void do_child(int *fds, int ipc_fd); extern void child_exit(int code); extern void cancel_request(CancelPacket * sp); @@ -624,13 +533,13 @@ extern BackendInfo * pool_get_node_info(int node_number); extern int pool_get_node_count(void); extern int *pool_get_process_list(int *array_size); extern ProcessInfo * pool_get_process_info(pid_t pid); +extern ProcessInfo * pool_get_process_info_from_IPC_Endpoint(IPC_Endpoint *endpoint); extern void pool_sleep(unsigned int second); extern int PgpoolMain(bool discard_status, bool clear_memcache_oidmaps); extern int pool_send_to_frontend(char *data, int len, bool flush); extern int pool_frontend_exists(void); extern pid_t pool_waitpid(int *status); extern int write_status_file(void); -extern POOL_NODE_STATUS * verify_backend_node_status(POOL_CONNECTION_POOL_SLOT * *slots); extern POOL_NODE_STATUS * pool_get_node_status(void); extern void pool_set_backend_status_changed_time(int backend_id); extern int get_next_main_node(void); @@ -644,7 +553,7 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz); #endif /* pool_worker_child.c */ + extern void do_worker_child(void); -extern int get_query_result(POOL_CONNECTION_POOL_SLOT * *slots, int backend_id, char *query, POOL_SELECT_RESULT * *res); #endif /* POOL_H */ diff --git a/src/include/pool_config.h b/src/include/pool_config.h index 7782d50c..48912059 100644 --- a/src/include/pool_config.h +++ b/src/include/pool_config.h @@ -132,6 +132,12 @@ typedef enum CHECK_TEMP_TABLE_OPTION CHECK_TEMP_OFF, } CHECK_TEMP_TABLE_OPTION; +typedef enum CONNECTION_POOL_TYPE_OPTION +{ + GLOBAL_CONNECTION_POOL = 1, + CLASSIC_CONNECTION_POOL +} CONNECTION_POOL_TYPE_OPTION; + /* * Flags for backendN_flag */ @@ -254,6 +260,8 @@ typedef struct int authentication_timeout; /* maximum time in seconds to complete * client authentication */ int max_pool; /* max # of connection pool per child */ + int max_pool_size; /* maximum number of pooled backend connections */ + int pool_availability_timeout; /* timeout in second to wait for pool connection to be available*/ char *logdir; /* logging directory */ char *log_destination_str; /* log destination: stderr and/or * syslog */ @@ -450,6 +458,7 @@ typedef struct bool check_unlogged_table; /* enable unlogged table check */ bool enable_shared_relcache; /* If true, relation cache stored in memory cache */ RELQTARGET_OPTION relcache_query_target; /* target node to send relcache queries */ + CONNECTION_POOL_TYPE_OPTION connection_pool_type; /* connection pool type */ /* * followings are for regex support and do not exist in the configuration diff --git a/src/include/pool_config_variables.h b/src/include/pool_config_variables.h index 7aaa6505..0d76a1ba 100644 --- a/src/include/pool_config_variables.h +++ b/src/include/pool_config_variables.h @@ -24,6 +24,7 @@ #ifndef POOL_CONFIG_VARIABLES_H #define POOL_CONFIG_VARIABLES_H +#include "connection_pool/connection_pool.h" typedef enum { @@ -342,10 +343,10 @@ extern bool set_config_options(ConfigVariable *head_p, #ifndef POOL_PRIVATE -extern bool report_config_variable(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *var_name); -extern bool report_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern bool set_config_option_for_session(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *name, const char *value); -bool reset_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +extern bool report_config_variable(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *var_name); +extern bool report_all_variables(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern bool set_config_option_for_session(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *name, const char *value); +bool reset_all_variables(POOL_CONNECTION * frontend, BackendClusterConnection * backend); #endif #endif /* POOL_CONFIG_VARIABLES_H */ diff --git a/src/include/pool_type.h b/src/include/pool_type.h index 43f29a59..62838805 100644 --- a/src/include/pool_type.h +++ b/src/include/pool_type.h @@ -58,6 +58,21 @@ typedef char bool; #endif /* not C++ */ #endif /* __BEOS__ */ +/* from include/c.h */ + +/* + * CppAsString + * Convert the argument to a string, using the C preprocessor. + * CppConcat + * Concatenate two arguments together, using the C preprocessor. + * + * Note: There used to be support here for pre-ANSI C compilers that didn't + * support # and ##. Nowadays, these macros are just for clarity and/or + * backward compatibility with existing PostgreSQL code. + */ +#define CppAsString(identifier) #identifier +#define CppConcat(x, y) x##y + /* ---------------------------------------------------------------- * Section 5: offsetof, lengthof, endof, alignment * ---------------------------------------------------------------- @@ -279,7 +294,7 @@ typedef void (*pg_on_exit_callback) (int code, Datum arg); #define Trap(condition, errorType) #define TrapMacro(condition, errorType) (true) -#elif defined(FRONTEND) +#elif defined(POOL_PRIVATE) #include #define Assert(p) assert(p) #define AssertMacro(p) ((void) assert(p)) diff --git a/src/include/protocol/pool_connection_pool.h b/src/include/protocol/pool_connection_pool.h index b7f35ce7..7aa74aa3 100644 --- a/src/include/protocol/pool_connection_pool.h +++ b/src/include/protocol/pool_connection_pool.h @@ -22,22 +22,36 @@ #ifndef pool_connection_pool_h #define pool_connection_pool_h -extern POOL_CONNECTION_POOL * pool_connection_pool; /* connection pool */ +#include "pool.h" -extern int pool_init_cp(void); -extern POOL_CONNECTION_POOL * pool_create_cp(void); -extern POOL_CONNECTION_POOL * pool_get_cp(char *user, char *database, int protoMajor, int check_socket); +extern void pool_init_cp(int parent_link_fd); + +bool ClearChildPooledConnectionData(void); + +extern BackendClusterConnection *GetBackendClusterConnection(void); extern void pool_discard_cp(char *user, char *database, int protoMajor); extern void pool_backend_timer(void); -extern void pool_connection_pool_timer(POOL_CONNECTION_POOL * backend); +extern void pool_connection_pool_timer(BackendClusterConnection * backend); extern RETSIGTYPE pool_backend_timer_handler(int sig); + extern int connect_inet_domain_socket(int slot, bool retry); extern int connect_unix_domain_socket(int slot, bool retry); extern int connect_inet_domain_socket_by_port(char *host, int port, bool retry); extern int connect_unix_domain_socket_by_port(int port, char *socket_dir, bool retry); + extern int pool_pool_index(void); extern void close_all_backend_connections(void); -extern void update_pooled_connection_count(void); -extern int in_use_backend_id(POOL_CONNECTION_POOL *pool); +extern int in_use_backend_id(BackendClusterConnection *pool); + + +/* Global connection pool */ + +extern size_t get_global_connection_pool_shared_mem_size(void); +extern void init_global_connection_pool(void); +extern bool SetupNewConnectionIntoChild(StartupPacket* sp); +extern int GetPooledConnectionForLending(char *user, char *database, int protoMajor, LEASE_TYPES *lease_type); +extern PooledBackendClusterConnection* GetPooledBackendClusterConnection(int pool_id); + +extern PooledBackendClusterConnection* GetPooledBackendClusterConnectionForCancelPacket(CancelPacket* cp); #endif /* pool_connection_pool_h */ diff --git a/src/include/protocol/pool_pg_utils.h b/src/include/protocol/pool_pg_utils.h index bd949357..0a5630cf 100644 --- a/src/include/protocol/pool_pg_utils.h +++ b/src/include/protocol/pool_pg_utils.h @@ -23,6 +23,7 @@ #define pool_pg_utils_h #include "pool.h" +#include "connection_pool/backend_connection.h" #define MAX_PG_VERSION_STRING 512 @@ -40,18 +41,17 @@ typedef struct char version_string[MAX_PG_VERSION_STRING+1]; /* original version string */ } PGVersion; - -extern void send_startup_packet(POOL_CONNECTION_POOL_SLOT * cp); +extern void send_startup_packet(BackendNodeConnection *cp, StartupPacket *sp); extern void pool_free_startup_packet(StartupPacket *sp); -extern POOL_CONNECTION_POOL_SLOT * make_persistent_db_connection( - int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry); -extern POOL_CONNECTION_POOL_SLOT * make_persistent_db_connection_noerror( - int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry); -extern void discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT * cp); +extern BackendNodeConnection *make_persistent_db_connection( + int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry); +extern BackendNodeConnection *make_persistent_db_connection_noerror( + int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry); +extern void discard_persistent_db_connection(BackendNodeConnection *cp); extern int select_load_balancing_node(void); -extern PGVersion *Pgversion(POOL_CONNECTION_POOL * backend); +extern PGVersion *Pgversion(BackendClusterConnection * backend); /* pool_pg_utils.c */ extern bool si_snapshot_prepared(void); diff --git a/src/include/protocol/pool_process_query.h b/src/include/protocol/pool_process_query.h index 7f91dbbc..45db6b76 100644 --- a/src/include/protocol/pool_process_query.h +++ b/src/include/protocol/pool_process_query.h @@ -30,7 +30,7 @@ extern void reset_variables(void); extern void reset_connection(void); -extern void per_node_statement_log(POOL_CONNECTION_POOL * backend, +extern void per_node_statement_log(BackendClusterConnection * backend, int node_id, char *query); extern int pool_extract_error_message(bool read_kind, POOL_CONNECTION * backend, int major, bool unread, char **message); @@ -39,15 +39,15 @@ extern POOL_STATUS do_command(POOL_CONNECTION * frontend, POOL_CONNECTION * back extern void do_query(POOL_CONNECTION * backend, char *query, POOL_SELECT_RESULT * *result, int major); extern void free_select_result(POOL_SELECT_RESULT * result); extern int compare(const void *p1, const void *p2); -extern void do_error_execute_command(POOL_CONNECTION_POOL * backend, int node_id, int major); -extern POOL_STATUS pool_discard_packet_contents(POOL_CONNECTION_POOL * cp); +extern void do_error_execute_command(BackendClusterConnection * backend, int node_id, int major); +extern POOL_STATUS pool_discard_packet_contents(BackendClusterConnection * cp); extern void pool_dump_valid_backend(int backend_id); extern bool pool_push_pending_data(POOL_CONNECTION * backend); -extern void pool_send_frontend_exits(POOL_CONNECTION_POOL * backend); +extern void pool_send_frontend_exits(BackendClusterConnection * backend); extern POOL_STATUS ParameterStatus(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern void pool_send_error_message(POOL_CONNECTION * frontend, int protoMajor, char *code, @@ -72,13 +72,13 @@ extern void pool_send_severity_message(POOL_CONNECTION * frontend, int protoMajo char *severity, int line); -extern POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int len, char *contents); +extern POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS pool_process_query(POOL_CONNECTION * frontend, -POOL_CONNECTION_POOL * backend, +BackendClusterConnection * backend, int reset_request); -extern bool is_backend_cache_empty(POOL_CONNECTION_POOL * backend); +extern bool is_backend_cache_empty(BackendClusterConnection * backend); extern void pool_send_readyforquery(POOL_CONNECTION * frontend); extern char *extract_error_kind(char *message, int major); diff --git a/src/include/protocol/pool_proto_modules.h b/src/include/protocol/pool_proto_modules.h index dc531af8..a4d8c77f 100644 --- a/src/include/protocol/pool_proto_modules.h +++ b/src/include/protocol/pool_proto_modules.h @@ -49,104 +49,104 @@ extern char *parsed_query; * modules defined in pool_proto_modules.c */ extern POOL_STATUS SimpleQuery(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS Execute(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS Parse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS Bind(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS Describe(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS Close(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS FunctionCall3(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents); extern POOL_STATUS ReadyForQuery(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, bool send_ready, bool cache_commit); + BackendClusterConnection * backend, bool send_ready, bool cache_commit); extern POOL_STATUS ParseComplete(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS BindComplete(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS CloseComplete(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS ParameterDescription(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS ErrorResponse3(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS CopyInResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS CopyOutResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS CopyDataRows(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, int copyin); + BackendClusterConnection * backend, int copyin); extern POOL_STATUS FunctionCall(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS ProcessFrontendResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS ProcessBackendResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int *state, short *num_fields); -extern void handle_query_context(POOL_CONNECTION_POOL * backend);; +extern void handle_query_context(BackendClusterConnection * backend);; extern void pool_emit_log_for_message_length_diff(int *length_array, char *name); -extern void per_node_statement_notice(POOL_CONNECTION_POOL * backend, int node_id, char *query); +extern void per_node_statement_notice(BackendClusterConnection * backend, int node_id, char *query); /* * modules defined in pool_proto2.c */ extern POOL_STATUS AsciiRow(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, short num_fields); extern POOL_STATUS BinaryRow(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, short num_fields); extern POOL_STATUS CompletedResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS CursorResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern void EmptyQueryResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS FunctionResultResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern POOL_STATUS NotificationResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern int RowDescription(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, short *result); extern void wait_for_query_response_with_trans_cleanup(POOL_CONNECTION * frontend, POOL_CONNECTION * backend, int protoVersion, int pid, int key); @@ -158,57 +158,57 @@ extern bool is_commit_or_rollback_query(Node *node); extern bool is_rollback_to_query(Node *node); extern bool is_strict_query(Node *node); /* returns non 0 if this is strict * query */ -extern int need_insert_lock(POOL_CONNECTION_POOL * backend, char *query, Node *node); -extern POOL_STATUS insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *query, InsertStmt *node, int lock_kind); +extern int need_insert_lock(BackendClusterConnection * backend, char *query, Node *node); +extern POOL_STATUS insert_lock(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *query, InsertStmt *node, int lock_kind); extern char *parse_copy_data(char *buf, int len, char delimiter, int col_id); extern int check_copy_from_stdin(Node *node); /* returns non 0 if this is a * COPY FROM STDIN */ -extern void query_ps_status(char *query, POOL_CONNECTION_POOL * backend); /* show ps status */ -extern POOL_STATUS start_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, Node *node); -extern POOL_STATUS end_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +extern void query_ps_status(char *query, BackendClusterConnection * backend); /* show ps status */ +extern POOL_STATUS start_internal_transaction(POOL_CONNECTION * frontend, BackendClusterConnection * backend, Node *node); +extern POOL_STATUS end_internal_transaction(POOL_CONNECTION * frontend, BackendClusterConnection * backend); extern int detect_deadlock_error(POOL_CONNECTION * backend, int major); extern int detect_serialization_error(POOL_CONNECTION * backend, int major, bool unread); extern int detect_active_sql_transaction_error(POOL_CONNECTION * backend, int major); extern int detect_query_cancel_error(POOL_CONNECTION * backend, int major); extern int detect_idle_in_transaction_session_timeout_error(POOL_CONNECTION * backend, int major); extern int detect_idle_session_timeout_error(POOL_CONNECTION * backend, int major); -extern bool is_partition_table(POOL_CONNECTION_POOL * backend, Node *node); -extern POOL_STATUS pool_discard_packet(POOL_CONNECTION_POOL * cp); +extern bool is_partition_table(BackendClusterConnection * backend, Node *node); +extern POOL_STATUS pool_discard_packet(BackendClusterConnection * cp); extern void query_cache_register(char kind, POOL_CONNECTION * frontend, char *database, char *data, int data_len); extern int is_drop_database(Node *node); /* returns non 0 if this is a DROP * DATABASE command */ extern void send_simplequery_message(POOL_CONNECTION * backend, int len, char *string, int major); -extern POOL_STATUS send_extended_protocol_message(POOL_CONNECTION_POOL * backend, +extern POOL_STATUS send_extended_protocol_message(BackendClusterConnection * backend, int node_id, char *kind, int len, char *string); extern int synchronize(POOL_CONNECTION * cp); -extern void read_kind_from_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *decided_kind); -extern void read_kind_from_one_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *kind, int node); +extern void read_kind_from_backend(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *decided_kind); +extern void read_kind_from_one_backend(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *kind, int node); extern void do_error_command(POOL_CONNECTION * backend, int major); -extern void raise_intentional_error_if_need(POOL_CONNECTION_POOL * backend); +extern void raise_intentional_error_if_need(BackendClusterConnection * backend); -extern void pool_at_command_success(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +extern void pool_at_command_success(POOL_CONNECTION * frontend, BackendClusterConnection * backend); /* * modules defined in CommandComplete.c */ -extern POOL_STATUS CommandComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, bool command_complete); +extern POOL_STATUS CommandComplete(POOL_CONNECTION * frontend, BackendClusterConnection * backend, bool command_complete); -extern int pool_read_message_length(POOL_CONNECTION_POOL * cp); -extern int *pool_read_message_length2(POOL_CONNECTION_POOL * cp); -extern signed char pool_read_kind(POOL_CONNECTION_POOL * cp); -extern int pool_read_int(POOL_CONNECTION_POOL * cp); +extern int pool_read_message_length(BackendClusterConnection * cp); +extern int *pool_read_message_length2(BackendClusterConnection * cp); +extern signed char pool_read_kind(BackendClusterConnection * cp); +extern int pool_read_int(BackendClusterConnection * cp); /* pool_proto2.c */ extern POOL_STATUS ErrorResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); extern void NoticeResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); -extern char per_node_error_log(POOL_CONNECTION_POOL * backend, int node_id, +extern char per_node_error_log(BackendClusterConnection * backend, int node_id, char *query, char *prefix, bool unread); #endif diff --git a/src/include/query_cache/pool_memqcache.h b/src/include/query_cache/pool_memqcache.h index 5206e2e5..acbcacfe 100644 --- a/src/include/query_cache/pool_memqcache.h +++ b/src/include/query_cache/pool_memqcache.h @@ -249,11 +249,11 @@ extern int pool_hash_delete(POOL_QUERY_HASH * key); extern uint32 hash_any(unsigned char *k, int keylen); extern POOL_STATUS pool_fetch_from_memory_cache(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, char *contents, bool *foundp); -extern int pool_fetch_cache(POOL_CONNECTION_POOL * backend, const char *query, char **buf, size_t *len); -extern int pool_catalog_commit_cache(POOL_CONNECTION_POOL * backend, char *query, char *data, size_t datalen); +extern int pool_fetch_cache(BackendClusterConnection * backend, const char *query, char **buf, size_t *len); +extern int pool_catalog_commit_cache(BackendClusterConnection * backend, char *query, char *data, size_t datalen); extern bool pool_is_likely_select(char *query); extern bool pool_is_table_in_unsafe_list(const char *table_name); @@ -277,7 +277,7 @@ extern POOL_QUERY_CACHE_ARRAY * pool_create_query_cache_array(void); extern void pool_discard_query_cache_array(POOL_QUERY_CACHE_ARRAY * cache_array); extern POOL_TEMP_QUERY_CACHE * pool_create_temp_query_cache(char *query); -extern void pool_handle_query_cache(POOL_CONNECTION_POOL * backend, char *query, Node *node, char state); +extern void pool_handle_query_cache(BackendClusterConnection * backend, char *query, Node *node, char state); extern int pool_init_memqcache_stats(void); extern POOL_QUERY_CACHE_STATS * pool_get_memqcache_stats(void); diff --git a/src/include/rewrite/pool_lobj.h b/src/include/rewrite/pool_lobj.h index edd1373e..aa41e8fb 100644 --- a/src/include/rewrite/pool_lobj.h +++ b/src/include/rewrite/pool_lobj.h @@ -26,7 +26,8 @@ #ifndef POOL_LOBJ_H #define POOL_LOBJ_H #include "pool.h" +#include "connection_pool/backend_connection.h" -extern char *pool_rewrite_lo_creat(char kind, char *packet, int packet_len, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int *len); +extern char *pool_rewrite_lo_creat(char kind, char *packet, int packet_len, POOL_CONNECTION * frontend, BackendClusterConnection * backend, int *len); #endif /* POOL_LOBJ_H */ diff --git a/src/include/rewrite/pool_timestamp.h b/src/include/rewrite/pool_timestamp.h index e58ddfb0..ea33988b 100644 --- a/src/include/rewrite/pool_timestamp.h +++ b/src/include/rewrite/pool_timestamp.h @@ -31,8 +31,8 @@ #include "parser/nodes.h" #include "context/pool_session_context.h" -extern char *rewrite_timestamp(POOL_CONNECTION_POOL * backend, Node *node, bool rewrite_to_params, POOL_SENT_MESSAGE * message); -extern char *bind_rewrite_timestamp(POOL_CONNECTION_POOL * backend, POOL_SENT_MESSAGE * message, const char *orig_msg, int *len); +extern char *rewrite_timestamp(BackendClusterConnection * backend, Node *node, bool rewrite_to_params, POOL_SENT_MESSAGE * message); +extern char *bind_rewrite_timestamp(BackendClusterConnection * backend, POOL_SENT_MESSAGE * message, const char *orig_msg, int *len); extern bool isSystemType(Node *node, const char *name); #endif /* POOL_TIMESTAMP_H */ diff --git a/src/include/utils/ancillary/ancillary.h b/src/include/utils/ancillary/ancillary.h new file mode 100644 index 00000000..e298fce9 --- /dev/null +++ b/src/include/utils/ancillary/ancillary.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * ancillary.c - public header + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANCILLARY_H__ +#define ANCILLARY_H__ + +/*************************************************************************** + * Start of the readable part. + ***************************************************************************/ + +#define ANCIL_MAX_N_FDS 960 +/* + * Maximum number of fds that can be sent or received using the "esay" + * functions; this is so that all can fit in one page. + */ + +extern int +ancil_send_fds_with_buffer(int, const int *, unsigned, void *); +/* + * ancil_send_fds_with_buffer(sock, n_fds, fds, buffer) + * + * Sends the file descriptors in the array pointed by fds, of length n_fds + * on the socket sock. + * buffer is a writeable memory area large enough to hold the required data + * structures. + * Returns: -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fds_with_buffer(int, int *, unsigned, void *); +/* + * ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer) + * + * Receives *n_fds file descriptors into the array pointed by fds + * from the socket sock. + * buffer is a writeable memory area large enough to hold the required data + * structures. + * Returns: -1 and errno in case of error, the actual number of received fd + * in case of success + */ + +#define ANCIL_FD_BUFFER(n) \ + struct { \ + struct cmsghdr h; \ + int fd[n]; \ + } +/* ANCIL_FD_BUFFER(n) + * + * A structure type suitable to be used as buffer for n file descriptors. + * Requires . + * Example: + * ANCIL_FD_BUFFER(42) buffer; + * ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer); + */ + +extern int +ancil_send_fds(int, const int *, unsigned); +/* + * ancil_send_fds(sock, n_fds, fds) + * + * Sends the file descriptors in the array pointed by fds, of length n_fds + * on the socket sock. + * n_fds must not be greater than ANCIL_MAX_N_FDS. + * Returns: -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fds(int, int *, unsigned); +/* + * ancil_recv_fds(sock, n_fds, fds) + * + * Receives *n_fds file descriptors into the array pointed by fds + * from the socket sock. + * *n_fds must not be greater than ANCIL_MAX_N_FDS. + * Returns: -1 and errno in case of error, the actual number of received fd + * in case of success. + */ + + +extern int +ancil_send_fd(int, int); +/* ancil_recv_fd(sock, fd); + * + * Sends the file descriptor fd on the socket sock. + * Returns : -1 and errno in case of error, 0 in case of success. + */ + +extern int +ancil_recv_fd(int, int *); +/* ancil_send_fd(sock, &fd); + * + * Receives the file descriptor fd from the socket sock. + * Returns : -1 and errno in case of error, 0 in case of success. + */ + +#endif /* ANCILLARY_H__ */ diff --git a/src/include/utils/memdebug.h b/src/include/utils/memdebug.h index f28341d2..e872f15a 100644 --- a/src/include/utils/memdebug.h +++ b/src/include/utils/memdebug.h @@ -17,6 +17,8 @@ #ifndef MEMDEBUG_H #define MEMDEBUG_H +#include + #ifdef USE_VALGRIND #include #else @@ -44,4 +46,31 @@ wipe_mem(void *ptr, size_t size) #endif /* CLOBBER_FREED_MEMORY */ +#ifdef MEMORY_CONTEXT_CHECKING + +static inline void +set_sentinel(void *base, Size offset) +{ + char *ptr = (char *)base + offset; + + VALGRIND_MAKE_MEM_UNDEFINED(ptr, 1); + *ptr = 0x7E; + VALGRIND_MAKE_MEM_NOACCESS(ptr, 1); +} + +static inline bool +sentinel_ok(const void *base, Size offset) +{ + const char *ptr = (const char *)base + offset; + bool ret; + + VALGRIND_MAKE_MEM_DEFINED(ptr, 1); + ret = *ptr == 0x7E; + VALGRIND_MAKE_MEM_NOACCESS(ptr, 1); + + return ret; +} + +#endif /* MEMORY_CONTEXT_CHECKING */ + #endif /* MEMDEBUG_H */ diff --git a/src/include/utils/pool_params.h b/src/include/utils/pool_params.h index c50eca60..2e7d3df2 100644 --- a/src/include/utils/pool_params.h +++ b/src/include/utils/pool_params.h @@ -21,13 +21,23 @@ #ifndef pool_params_h #define pool_params_h +#define MAX_PARAM_NAME_LEN 64 +#define MAX_PARAM_VALUE_LEN 128 +#define MAX_PARAM_ITEMS 64 + +typedef struct PGParam +{ + char name[MAX_PARAM_NAME_LEN]; + char value[MAX_PARAM_VALUE_LEN]; +} PGParam; + typedef struct { - int num; /* number of entries */ - char **names; /* parameter names */ - char **values; /* values */ + int num; /* number of entries */ + PGParam params[MAX_PARAM_ITEMS]; } ParamStatus; + extern int pool_init_params(ParamStatus * params); extern void pool_discard_params(ParamStatus * params); extern char *pool_find_name(ParamStatus * params, char *name, int *pos); diff --git a/src/include/utils/pool_process_reporting.h b/src/include/utils/pool_process_reporting.h index daed4e62..1cb5f72a 100644 --- a/src/include/utils/pool_process_reporting.h +++ b/src/include/utils/pool_process_reporting.h @@ -26,9 +26,11 @@ #ifndef POOL_PROCESS_REPORTING_H #define POOL_PROCESS_REPORTING_H -extern void send_row_description(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +#include "connection_pool/connection_pool.h" + +extern void send_row_description(POOL_CONNECTION * frontend, BackendClusterConnection * backend, short num_fields, char **field_names); -extern void send_complete_and_ready(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *message, const int num_rows); +extern void send_complete_and_ready(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *message, const int num_rows); extern POOL_REPORT_CONFIG * get_config(int *nrows); extern POOL_REPORT_POOLS * get_pools(int *nrows); extern POOL_REPORT_PROCESSES * get_processes(int *nrows); @@ -37,18 +39,18 @@ extern POOL_REPORT_VERSION * get_version(void); extern POOL_HEALTH_CHECK_STATS *get_health_check_stats(int *nrows); extern POOL_BACKEND_STATS *get_backend_stats(int *nrows); -extern void config_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void pools_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void processes_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void nodes_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void version_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void cache_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void show_health_check_stats(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); -extern void show_backend_stats(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +extern void config_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void pools_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void processes_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void nodes_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void version_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void cache_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void show_health_check_stats(POOL_CONNECTION * frontend, BackendClusterConnection * backend); +extern void show_backend_stats(POOL_CONNECTION * frontend, BackendClusterConnection * backend); -extern void send_config_var_detail_row(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *name, const char *value, const char *description); -extern void send_config_var_value_only_row(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *value); +extern void send_config_var_detail_row(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *name, const char *value, const char *description); +extern void send_config_var_value_only_row(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *value); extern char *get_backend_status_string(BACKEND_STATUS status); extern int * pool_report_pools_offsets(int *n); diff --git a/src/include/utils/pool_relcache.h b/src/include/utils/pool_relcache.h index 70b7d314..35bdffcb 100644 --- a/src/include/utils/pool_relcache.h +++ b/src/include/utils/pool_relcache.h @@ -26,6 +26,8 @@ #ifndef POOL_RELCACHE_H #define POOL_RELCACHE_H +#include "connection_pool/backend_connection.h" + /* ------------------------ * Relation cache structure *------------------------- @@ -75,7 +77,7 @@ extern POOL_RELCACHE * pool_create_relcache(int cachesize, char *sql, func_ptr register_func, func_ptr unregister_func, bool issessionlocal); extern void pool_discard_relcache(POOL_RELCACHE * relcache); -extern void *pool_search_relcache(POOL_RELCACHE * relcache, POOL_CONNECTION_POOL * backend, char *table); +extern void *pool_search_relcache(POOL_RELCACHE * relcache, BackendClusterConnection * backend, char *table); extern char *remove_quotes_and_schema_from_relname(char *table); extern void *int_register_func(POOL_SELECT_RESULT * res); extern void *int_unregister_func(void *data); diff --git a/src/include/utils/pool_ssl.h b/src/include/utils/pool_ssl.h index 8ea6398c..a3abd056 100644 --- a/src/include/utils/pool_ssl.h +++ b/src/include/utils/pool_ssl.h @@ -23,6 +23,8 @@ #ifndef pool_ssl_h #define pool_ssl_h +#include "connection_pool/backend_connection.h" + #ifdef USE_SSL /* * Hardcoded DH parameters, used in ephemeral DH keying. diff --git a/src/include/utils/pool_stream.h b/src/include/utils/pool_stream.h index 8ddba327..6d7bd23b 100644 --- a/src/include/utils/pool_stream.h +++ b/src/include/utils/pool_stream.h @@ -27,7 +27,7 @@ #define POOL_STREAM_H #include "utils/socket_stream.h" - +#include "connection_pool/connection_pool.h" #define READBUFSZ 1024 #define WRITEBUFSZ 8192 @@ -45,7 +45,7 @@ } while (0) extern POOL_CONNECTION * pool_open(int fd, bool backend_connection); -extern void pool_close(POOL_CONNECTION * cp); +extern void pool_close(POOL_CONNECTION * cp, bool close_socket); extern int pool_read(POOL_CONNECTION * cp, void *buf, int len); extern void pool_read_with_error(POOL_CONNECTION * cp, void *buf, int len, const char *err_context); diff --git a/src/include/utils/ps_status.h b/src/include/utils/ps_status.h index a8fb3f1a..93ebf830 100644 --- a/src/include/utils/ps_status.h +++ b/src/include/utils/ps_status.h @@ -22,6 +22,7 @@ #define ps_status_h #include "pool.h" +#include "connection_pool/connection_pool.h" #include extern char remote_ps_data[NI_MAXHOST + NI_MAXSERV + 2]; /* used for set_ps_display */ @@ -31,7 +32,7 @@ extern void init_ps_display(const char *username, const char *dbname, const char *host_info, const char *initial_str); extern void set_ps_display(const char *activity, bool force); extern const char *get_ps_display(int *displen); -extern void pool_ps_idle_display(POOL_CONNECTION_POOL * backend); +extern void pool_ps_idle_display(BackendClusterConnection * backend); #endif /* ps_status_h */ diff --git a/src/main/health_check.c b/src/main/health_check.c index d6551f4b..ee93bf42 100644 --- a/src/main/health_check.c +++ b/src/main/health_check.c @@ -70,7 +70,7 @@ volatile POOL_HEALTH_CHECK_STATISTICS *health_check_stats; /* health check stats area in shared memory */ -static POOL_CONNECTION_POOL_SLOT * slot; +static BackendNodeConnection *slot; static volatile sig_atomic_t reload_config_request = 0; static volatile sig_atomic_t restart_request = 0; volatile POOL_HEALTH_CHECK_STATISTICS *stats; diff --git a/src/main/pgpool_ipc.c b/src/main/pgpool_ipc.c new file mode 100644 index 00000000..c9e1202f --- /dev/null +++ b/src/main/pgpool_ipc.c @@ -0,0 +1,447 @@ +/* -*-pgsql-c-*- */ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2024 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + */ + +/* + * libancillary is used under the hood to transfer the file descriptors + *https://www.normalesup.org/~george/comp/libancillary/ + */ + +/* + * Transfering and returning the connections should be kept as fast + * as possible since this directly dictates the speed of client connection + */ + +#include "pool.h" +#include "pool_config.h" +#include "main/pgpool_ipc.h" +#include "pool_config_variables.h" +#include "context/pool_process_context.h" +#include "connection_pool/connection_pool.h" +#include "utils/socket_stream.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/memutils.h" +#include "utils/ancillary/ancillary.h" + + +#include +#include +#include +#include +#include +#include +#include + + +#define IPC_READY_TO_USE_CONNECTION_MESSAGE 'S' /* Followd by sockets transfer */ +#define IPC_DISCARD_AND_REUSE_MESSAGE 'D' /* Followd by sockets transfer */ +#define IPC_NO_CONN_AVAILABLE_MESSAGE 'F' +#define IPC_CONNECT_AND_PROCEED_MESSAGE 'E' +#define IPC_NON_POOL_CONNECT_MESSAGE 'N' + +#define IPC_BORROW_CONNECTION_REQUEST 'B' +#define IPC_RELEASE_CONNECTION_REQUEST 'R' +#define IPC_DISCARD_CONNECTION_REQUEST 'D' +#define IPC_PUSH_CONNECTION_TO_POOL 'P' + +#define MAX_WAIT_FOR_PARENT_RESPONSE 5 /* TODO should it be configurable ?*/ + +static bool receive_sockets(int fd, int count, int *sockets); +static bool process_borrow__connection_request(IPC_Endpoint* ipc_endpoint); + +static bool process_release_connection_request(IPC_Endpoint* ipc_endpoint); +static bool process_discard_connection_request(IPC_Endpoint* ipc_endpoint); + +static bool process_push_connection_to_pool(IPC_Endpoint* ipc_endpoint); + +/* + * To keep the packet small we use ProcessInfo for communicating the + * required connection credentials */ +LEASE_TYPES +BorrowBackendConnection(int parent_link, char* database, char* user, int major, int minor, int *count, int* sockets) +{ + char type = IPC_BORROW_CONNECTION_REQUEST; + + if (processType != PT_CHILD) + return LEASE_TYPE_INVALID; + + /* write the information in procInfo*/ + ProcessInfo *pro_info = pool_get_my_process_info(); + StrNCpy(pro_info->database, database, SM_DATABASE); + StrNCpy(pro_info->user, user, SM_USER); + pro_info->major = major; + pro_info->minor = minor; + /* Send the message to main process */ + + ereport(DEBUG2, + (errmsg("Asking pooled connection for:%s :%s ", pro_info->database, pro_info->user))); + + if (write(parent_link, &type, 1) != 1) + { + close(parent_link); + ereport(FATAL, + (errmsg("failed to write IPC packet type:%c to parent:%d", type, parent_link))); + return LEASE_TYPE_LEASE_FAILED; + } + + /* + * Read the lease type response from main process + */ + if (socket_read(parent_link, &type, 1, MAX_WAIT_FOR_PARENT_RESPONSE) != 1) + { + close(parent_link); + ereport(FATAL, + (errmsg("failed to read IPC packet type:%c from parent:%d", type, parent_link))); + return LEASE_TYPE_LEASE_FAILED; + } + /* + * In case of a successful lease. main process should already have updated the pool_id + * in process info for this child + */ + if (type == IPC_READY_TO_USE_CONNECTION_MESSAGE || type == IPC_DISCARD_AND_REUSE_MESSAGE) + { + PooledBackendClusterConnection *backend_end_point = GetGlobalPooledBackendClusterConnection(pro_info->pool_id); + if (backend_end_point == NULL) + { + ereport(WARNING, + (errmsg("failed to get backend end point for pool_id:%d", pro_info->pool_id))); + return LEASE_TYPE_LEASE_FAILED; + } + /* ProcessInfo should already have the socket count */ + *count = backend_end_point->num_sockets; + if(receive_sockets(parent_link, *count, sockets)) + { + if (type == IPC_READY_TO_USE_CONNECTION_MESSAGE) + return LEASE_TYPE_READY_TO_USE; + else if (type == IPC_DISCARD_AND_REUSE_MESSAGE) + return LEASE_TYPE_DISCART_AND_CREATE; + } + return LEASE_TYPE_LEASE_FAILED; + } + else if (type == IPC_NO_CONN_AVAILABLE_MESSAGE) + return LEASE_TYPE_NO_AVAILABLE_SLOT; + else if (type == IPC_CONNECT_AND_PROCEED_MESSAGE) + return LEASE_TYPE_EMPTY_SLOT_RESERVED; + return LEASE_TYPE_INVALID; +} + +bool +ProcessChildRequestOnMain(IPC_Endpoint* ipc_endpoint) +{ + char type; + int ret = 0; + if (processType != PT_MAIN) + return false; + + ereport(DEBUG2, + (errmsg("New request received from from child:%d", ipc_endpoint->child_pid))); + + ret = socket_read(ipc_endpoint->child_link, &type, 1, MAX_WAIT_FOR_PARENT_RESPONSE); + if (ret != 1) + { + ereport(LOG, + (errmsg("failed to read IPC packet type:%c from child:%d", type, ipc_endpoint->child_pid))); + /* Child could've been dead so we might have got remote end closed the connection */ + if (ret == 0) + { + ereport(LOG, + (errmsg("remote end closed the connection for child:%d", ipc_endpoint->child_pid), + errdetail("closing the connection for child:%d", ipc_endpoint->child_pid))); + close(ipc_endpoint->child_link); + ipc_endpoint->child_link = -1; + return false; + } + return false; + } + ereport(DEBUG2, + (errmsg("Processing request type:%c from child:%d", type, ipc_endpoint->child_pid))); + if (type == IPC_BORROW_CONNECTION_REQUEST) + return process_borrow__connection_request(ipc_endpoint); + else if (type == IPC_RELEASE_CONNECTION_REQUEST) + return process_release_connection_request(ipc_endpoint); + else if (type == IPC_DISCARD_CONNECTION_REQUEST) + return process_discard_connection_request(ipc_endpoint); + else if (type == IPC_PUSH_CONNECTION_TO_POOL) + return process_push_connection_to_pool(ipc_endpoint); + else + + ereport(LOG, + (errmsg("failed to process unsupported IPC packet type:%c from child:%d", type, ipc_endpoint->child_pid))); + + return false; +} + +bool +InformLeaseStatusToChild(int child_link, LEASE_TYPES lease_type) +{ + char type; + switch (lease_type) + { + case LEASE_TYPE_READY_TO_USE: + type = IPC_READY_TO_USE_CONNECTION_MESSAGE; + break; + case LEASE_TYPE_DISCART_AND_CREATE: + type = IPC_DISCARD_AND_REUSE_MESSAGE; + break; + case LEASE_TYPE_EMPTY_SLOT_RESERVED: + type = IPC_CONNECT_AND_PROCEED_MESSAGE; + break; + case LEASE_TYPE_NO_AVAILABLE_SLOT: + type = IPC_NO_CONN_AVAILABLE_MESSAGE; + break; + case LEASE_TYPE_NON_POOL_CONNECTION: + type = IPC_NON_POOL_CONNECT_MESSAGE; + break; + case LEASE_TYPE_INVALID: + default: + ereport(WARNING, + (errmsg("unsupported lease_type:%d", lease_type))); + return false; + break; + } + + if (write(child_link, &type, 1) != 1) + { + ereport(WARNING, + (errmsg("failed to write IPC packet type:%c to child", type))); + return false; + } + return true; +} + +bool +SendBackendSocktesToMainPool(int parent_link, int count, int *sockets) +{ + char type = IPC_PUSH_CONNECTION_TO_POOL; + Assert(processType == PT_CHILD); + if (write(parent_link, &type, 1) != 1) + { + close(parent_link); + ereport(FATAL, + (errmsg("failed to write IPC packet type:%c to parent:%d", type, parent_link))); + return false; + } + if (TransferSocketsBetweenProcesses(parent_link, count, sockets) == false) + ereport(FATAL, + (errmsg("failed to push sockets to parent:%d", parent_link))); + + return true; +} + +bool +ReleasePooledConnectionFromChild(int parent_link, bool discard) +{ + char type = discard?IPC_DISCARD_CONNECTION_REQUEST : IPC_RELEASE_CONNECTION_REQUEST; + + if (processType != PT_CHILD) + return false; + + if (write(parent_link, &type, 1) != 1) + { + close(parent_link); + ereport(FATAL, + (errmsg("failed to write IPC packet type:%c to parent:%d", type, parent_link))); + return false; + } + return true; +} + +bool +TransferSocketsBetweenProcesses(int process_link, int count, int *sockets) +{ + ereport(DEBUG1, + (errmsg("Sending %d sockets through socket-descriptor:%d", count, process_link))); + if (ancil_send_fds(process_link, sockets, count) == -1) + { + ereport(WARNING, + (errmsg("ancil_send_fds failed %m"))); + return false; + } + return true; +} + +static bool +process_borrow__connection_request(IPC_Endpoint* ipc_endpoint) +{ + return GlobalPoolLeasePooledConnectionToChild(ipc_endpoint); +} + +static bool +receive_sockets(int fd, int count, int *sockets) +{ + #define MAX_WAIT_FOR_FDS_RECV 2 + fd_set rmask; + int ret; + struct timeval timeout; + timeout.tv_sec = MAX_WAIT_FOR_FDS_RECV; + timeout.tv_usec = 0; + + FD_ZERO(&rmask); + FD_SET(fd, &rmask); + /* we could have called recv too early while sender would still be + * preparing the socket list. + * So we need to wait for data to be available + */ + ret = select(fd + 1, &rmask, NULL, NULL, &timeout); + if (ret == 0) + { + ereport(WARNING, + (errmsg("Timeout while waiting for sockets from fd:%d", fd))); + return false; + } + else if (ret == -1) + { + ereport(WARNING, + (errmsg("select failed while waiting for sockets from fd:%d", fd), + errdetail("%m"))); + return false; + } + if (ancil_recv_fds(fd, sockets, count) == -1) + { + ereport(WARNING, + (errmsg("ancil_recv_fds failed"), + errdetail("%m"))); + return false; + } + return true; +} + +static bool +process_discard_connection_request(IPC_Endpoint* ipc_endpoint) +{ + ProcessInfo *pro_info = NULL; + ConnectionPoolEntry* pool_entry; + + if (processType != PT_MAIN) + return false; + + ereport(DEBUG2, + (errmsg("Processing Discard connection to pool from child:%d", ipc_endpoint->child_pid))); + + pro_info = pool_get_process_info_from_IPC_Endpoint(ipc_endpoint); + if (!pro_info) + { + ereport(WARNING, + (errmsg("failed to get process info for child:%d", ipc_endpoint->child_pid))); + return false; + } + pool_entry = GetConnectionPoolEntry(pro_info->pool_id, ipc_endpoint->proc_info_id); + if (!pool_entry) + { + ereport(WARNING, + (errmsg("failed to get pool entry for pool_id:%d", pro_info->pool_id))); + return false; + } + return GlobalPoolReleasePooledConnection(pool_entry, ipc_endpoint, false, true); +} + +static bool +process_release_connection_request(IPC_Endpoint* ipc_endpoint) +{ + ProcessInfo *pro_info = NULL; + ConnectionPoolEntry* pool_entry; + + if (processType != PT_MAIN) + return false; + + ereport(LOG, + (errmsg("Processing Release connection to pool from child:%d", ipc_endpoint->child_pid))); + + pro_info = pool_get_process_info_from_IPC_Endpoint(ipc_endpoint); + if (!pro_info) + { + ereport(WARNING, + (errmsg("failed to get process info for child:%d", ipc_endpoint->child_pid))); + return false; + } + pool_entry = GetConnectionPoolEntry(pro_info->pool_id, ipc_endpoint->proc_info_id); + if (!pool_entry) + { + ereport(WARNING, + (errmsg("failed to get pool entry for pool_id:%d", pro_info->pool_id))); + return false; + } + return GlobalPoolReleasePooledConnection(pool_entry, ipc_endpoint, false, false); +} + +static bool +process_push_connection_to_pool(IPC_Endpoint* ipc_endpoint) +{ + ProcessInfo *pro_info = NULL; + ConnectionPoolEntry* pool_entry; + int sockets[MAX_NUM_BACKENDS]; + + if (processType != PT_MAIN) + return false; + + ereport(LOG, + (errmsg("Processing push connection to pool from child:%d", ipc_endpoint->child_pid))); + + pro_info = pool_get_process_info_from_IPC_Endpoint(ipc_endpoint); + if (!pro_info) + { + ereport(WARNING, + (errmsg("failed to get process info for child:%d", ipc_endpoint->child_pid))); + return false; + } + pool_entry = GetConnectionPoolEntry(pro_info->pool_id, ipc_endpoint->proc_info_id); + if (!pool_entry) + { + ereport(WARNING, + (errmsg("failed to get pool entry for pool_id:%d", pro_info->pool_id))); + return false; + } + + ereport(DEBUG2, + (errmsg("Expecting to receive %d socktes for pool_id:%d",pool_entry->endPoint.num_sockets, pro_info->pool_id))); + + if (receive_sockets(ipc_endpoint->child_link, pool_entry->endPoint.num_sockets, sockets) == true) + { + bool ret ; + ereport(LOG, + (errmsg("received %d sockets for pool_id:%d from child:%d", pool_entry->endPoint.num_sockets, pro_info->pool_id,ipc_endpoint->child_pid))); + ret = InstallSocketsInConnectionPool(pool_entry, sockets); + if (ret) + { + pool_entry->status = POOL_ENTRY_CONNECTED; + } + else + { + /* Mark this entry as empty, as we can't do anything witout socktes */ + pool_entry->status = POOL_ENTRY_EMPTY; + ereport(LOG, + (errmsg("InstallSocketsInConnectionPool failed for pool_id:%d from child:%d failed", pro_info->pool_id,ipc_endpoint->child_pid))); + ret = GlobalPoolReleasePooledConnection(pool_entry, ipc_endpoint, !ret, false); + if (!ret) + ereport(LOG, + (errmsg("GlobalPoolReleasePooledConnection failed for pool_id:%d from child:%d failed", pro_info->pool_id, ipc_endpoint->child_pid))); + } + return ret; + } + else + { + ereport(WARNING, + (errmsg("failed to receive %d sockets for pool_id:%d from child:%d", pool_entry->endPoint.num_sockets, pro_info->pool_id,ipc_endpoint->child_pid))); + GlobalPoolReleasePooledConnection(pool_entry, ipc_endpoint, true, true); + } + + return false; +} diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c index 60bc675b..aceeb686 100644 --- a/src/main/pgpool_main.c +++ b/src/main/pgpool_main.c @@ -46,14 +46,18 @@ #include "main/health_check.h" #include "main/pool_internal_comms.h" #include "main/pgpool_logger.h" +#include "main/pgpool_ipc.h" +#include "connection_pool/backend_connection.h" #include "utils/elog.h" #include "utils/palloc.h" #include "utils/memutils.h" #include "utils/statistics.h" #include "utils/pool_ipc.h" +#include "utils/socket_stream.h" #include "context/pool_process_context.h" #include "protocol/pool_process_query.h" #include "protocol/pool_pg_utils.h" +#include "protocol/pool_connection_pool.h" #include "auth/pool_passwd.h" #include "auth/pool_hba.h" #include "query_cache/pool_memqcache.h" @@ -118,7 +122,7 @@ typedef struct User1SignalSlot #define PGPOOLMAXLITSENQUEUELENGTH 10000 #define MAX_ONE_SHOT_KILLS 8 - +#define SERVICE_CHILD_RASTER_SECONDS 5 #define UNIXSOCK_PATH_BUFLEN sizeof(((struct sockaddr_un *) NULL)->sun_path) @@ -182,9 +186,9 @@ static void system_will_go_down(int code, Datum arg); static char *process_name_from_pid(pid_t pid); static void sync_backend_from_watchdog(void); static void update_backend_quarantine_status(void); -static int get_server_version(POOL_CONNECTION_POOL_SLOT * *slots, int node_id); +static int get_server_version(BackendNodeConnection **slots, int node_id); static void get_info_from_conninfo(char *conninfo, char *host, int hostlen, char *port, int portlen); - +static void child_process_exits(int child_id, pid_t child_pid); /* * Subroutines of failover() */ @@ -197,10 +201,12 @@ static int exec_follow_primary_command(FAILOVER_CONTEXT *failover_context, int n static void save_node_info(FAILOVER_CONTEXT *failover_context, int new_primary_node_id, int new_main_node_id); static void exec_child_restart(FAILOVER_CONTEXT *failover_context, int node_id); static void exec_notice_pcp_child(FAILOVER_CONTEXT *failover_context); +static void conform_children_to_node_failure(int failed_node_id, bool need_total_reset, bool prepare_only); -static void check_requests(void); + static void check_requests(void); static void print_signal_member(sigset_t *sig); static void service_child_processes(void); +static bool handle_child_ipc_requests(void); static int select_victim_processes(int *process_info_idxs, int count); static struct sockaddr_un *un_addrs; /* unix domain socket path */ @@ -210,6 +216,7 @@ volatile User1SignalSlot *user1SignalSlot = NULL; /* User 1 signal slot on * shmem */ int current_child_process_count; +IPC_Endpoint *ipc_endpoints = NULL; /* IPC endpoints for child processes */ struct timeval random_start_time; /* @@ -221,14 +228,6 @@ static pid_t health_check_pids[MAX_NUM_BACKENDS]; * Private copy of backend status */ BACKEND_STATUS private_backend_status[MAX_NUM_BACKENDS]; - -/* - * shmem connection info table - * this is a three dimension array. i.e.: - * con_info[pool_config->num_init_children][pool_config->max_pool][MAX_NUM_BACKENDS] - */ -ConnectionInfo *con_info; - static int *fds = NULL; /* listening file descriptors (UNIX socket, * inet domain sockets) */ @@ -294,6 +293,7 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) int *pcp_inet_fds; int i; char unix_domain_socket_path[UNIXSOCK_PATH_BUFLEN + 1024]; + struct timeval last_child_service_time = {0,0}; sigjmp_buf local_sigjmp_buf; @@ -339,8 +339,8 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) if (num_unix_fds == 0) { - ereport(FATAL, - (errmsg("could not create any Unix-domain sockets"))); + ereport(LOG, + (errmsg("could not create any Unix-domain sockets %m"))); } /* set unix domain socket path for pgpool PCP communication */ @@ -387,6 +387,11 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) close(syslogPipe[0]); syslogPipe[0] = -1; + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + InstallConnectionPool(GetGlobalConnectionPool()); + else + InstallConnectionPool(GetClassicConnectionPool()); + initialize_shared_mem_objects(clear_memcache_oidmaps); /* @@ -537,6 +542,12 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) */ POOL_SETMASK(&BlockSig); + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + { + ipc_endpoints = malloc(sizeof(IPC_Endpoint) * pool_config->num_init_children); + memset(ipc_endpoints, 0, sizeof(IPC_Endpoint) * pool_config->num_init_children); + } + if (pool_config->process_management == PM_DYNAMIC) { if (pool_config->process_management_strategy == PM_STRATEGY_AGGRESSIVE) @@ -558,6 +569,7 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) process_info[i].pooled_connections = 0; process_info[i].need_to_restart = false; process_info[i].exit_if_idle = false; + process_info[i].pool_id = -1; process_info[i].pid = fork_a_child(fds, i); } @@ -645,23 +657,23 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) /* This is the main loop */ for (;;) { + bool process_service_child = false; /* Check pending requests */ check_requests(); -#ifdef NOT_USED - CHECK_REQUEST; -#endif /* * check for child signals to ensure child startup before reporting * successful start. */ if (first) { - int i; - int n; + int i,n; + POOL_NODE_STATUS *node_status = pool_get_node_status(); ereport(LOG, (errmsg("%s successfully started. version %s (%s)", PACKAGE, VERSION, PGPOOLVERSION))); + ereport(LOG, + (errmsg("Configured connection pooling: %s", GetConnectionPoolInfo()))); /* * Very early stage node checking. It is assumed that @@ -686,24 +698,33 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) } } } + gettimeofday(&last_child_service_time, NULL); } first = false; processState = SLEEPING; - for (;;) + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + { + process_service_child = !handle_child_ipc_requests(); + } + else { - int r; + int r; struct timeval t = {2, 0}; - POOL_SETMASK(&UnBlockSig); r = pool_pause(&t); POOL_SETMASK(&BlockSig); - - if (pool_config->process_management == PM_DYNAMIC) + process_service_child = true; + } + if (process_service_child && pool_config->process_management == PM_DYNAMIC) + { + struct timeval current_time; + gettimeofday(¤t_time, NULL); + if (current_time.tv_sec - last_child_service_time.tv_sec >= SERVICE_CHILD_RASTER_SECONDS) + { service_child_processes(); - - if (r > 0) - break; + gettimeofday(&last_child_service_time, NULL); + } } } } @@ -832,12 +853,24 @@ static pid_t fork_a_child(int *fds, int id) { pid_t pid; + int ipc_sock[2] = {-1, -1}; + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + { + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, ipc_sock) == -1) { + ereport(FATAL, + (errmsg("failed to IPC socket"), + errdetail("system call socketpair() failed with reason: %m"))); + } + } pid = fork(); if (pid == 0) { on_exit_reset(); + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + close(ipc_sock[1]); /* * Before we unconditionally closed pipe_fds[0] and pipe_fds[1] here, @@ -860,7 +893,8 @@ fork_a_child(int *fds, int id) health_check_timer_expired = 0; reload_config_request = 0; my_proc_id = id; - do_child(fds); + ereport(LOG,(errmsg("CHILD PROCESS %d, pid %d",id, getpid()))); + do_child(fds, ipc_sock[0]); } else if (pid == -1) { @@ -868,7 +902,17 @@ fork_a_child(int *fds, int id) (errmsg("failed to fork a child"), errdetail("system call fork() failed with reason: %m"))); } - + else + { + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + { + close(ipc_sock[0]); + socket_set_nonblock(ipc_sock[1]); + ipc_endpoints[id].child_link = ipc_sock[1]; + ipc_endpoints[id].child_pid = pid; + ipc_endpoints[id].proc_info_id = id; + } + } return pid; } @@ -1997,7 +2041,8 @@ reaper(void) if (pid == process_info[i].pid) { found = true; - /* if found, fork a new child */ + child_process_exits(i, pid); + /* fork a new child */ if (!switching && !exiting && restart_child && pool_config->process_management != PM_DYNAMIC) { @@ -2132,6 +2177,16 @@ pool_get_process_info(pid_t pid) return NULL; } +ProcessInfo * +pool_get_process_info_from_IPC_Endpoint(IPC_Endpoint *endpoint) +{ + if (!endpoint || endpoint->proc_info_id <0 || endpoint->proc_info_id >= pool_config->num_init_children) + return NULL; + if (process_info[endpoint->proc_info_id].pid == endpoint->child_pid) + return &process_info[endpoint->proc_info_id]; + return NULL; +} + /* * handle SIGUSR2 * Wakeup all processes @@ -2437,7 +2492,7 @@ trigger_failover_command(int node, const char *command_line, static POOL_NODE_STATUS pool_node_status[MAX_NUM_BACKENDS]; POOL_NODE_STATUS * -verify_backend_node_status(POOL_CONNECTION_POOL_SLOT * *slots) +verify_backend_node_status(BackendNodeConnection **slots) { POOL_SELECT_RESULT *res; int num_primaries = 0; @@ -2727,7 +2782,7 @@ static int find_primary_node(void) { BackendInfo *bkinfo; - POOL_CONNECTION_POOL_SLOT *slots[MAX_NUM_BACKENDS]; + BackendNodeConnection *slots[MAX_NUM_BACKENDS]; int i; POOL_NODE_STATUS *status; int primary = -1; @@ -3003,7 +3058,6 @@ initialize_shared_mem_objects(bool clear_memcache_oidmaps) size = 256;/* let us have some extra space */ size += MAXALIGN(sizeof(BackendDesc)); elog(DEBUG1, "BackendDesc: %zu bytes requested for shared memory", MAXALIGN(sizeof(BackendDesc))); - size += MAXALIGN(pool_coninfo_size()); size += MAXALIGN(pool_config->num_init_children * (sizeof(ProcessInfo))); elog(DEBUG1, "ProcessInfo: num_init_children (%d) * sizeof(ProcessInfo) (%zu) = %zu bytes requested for shared memory", pool_config->num_init_children, sizeof(ProcessInfo), pool_config->num_init_children* sizeof(ProcessInfo)); @@ -3021,6 +3075,8 @@ initialize_shared_mem_objects(bool clear_memcache_oidmaps) size += MAXALIGN(pool_config->num_init_children * sizeof(pid_t)); size += MAXALIGN(pool_config->num_init_children * sizeof(pid_t)); + size += MAXALIGN(ConnectionPoolRequiredSharedMemSize()); + if (pool_is_shmem_cache()) { size += MAXALIGN(pool_shared_memory_cache_size()); @@ -3049,14 +3105,17 @@ initialize_shared_mem_objects(bool clear_memcache_oidmaps) pfree(pool_config->backend_desc); pool_config->backend_desc = backend_desc; - /* get the shared memory from main segment*/ - con_info = (ConnectionInfo *)pool_shared_memory_segment_get_chunk(pool_coninfo_size()); + if (ConnectionPoolRequiredSharedMemSize() > 0) + { + void* shmem_loc = pool_shared_memory_segment_get_chunk(ConnectionPoolRequiredSharedMemSize()); + InitializeConnectionPool(shmem_loc); + } process_info = (ProcessInfo *)pool_shared_memory_segment_get_chunk(pool_config->num_init_children * (sizeof(ProcessInfo))); for (i = 0; i < pool_config->num_init_children; i++) { - process_info[i].connection_info = pool_coninfo(i, 0, 0); process_info[i].pid = 0; + process_info[i].pool_id = -1; } user1SignalSlot = (User1SignalSlot *)pool_shared_memory_segment_get_chunk(sizeof(User1SignalSlot)); @@ -3741,7 +3800,6 @@ sync_backend_from_watchdog(void) ereport(LOG, (errmsg("primary node was changed after the sync from \"%s\"", backendStatus->nodeName), errdetail("all children needs to be restarted"))); - } else { @@ -3770,75 +3828,17 @@ sync_backend_from_watchdog(void) /* Kill children and restart them if needed */ if (need_to_restart_children) { - for (i = 0; i < pool_config->num_init_children; i++) + if (partial_restart) { - bool restart = false; - - if (partial_restart) - { - int j, - k; - - for (j = 0; j < pool_config->max_pool; j++) - { - for (k = 0; k < NUM_BACKENDS; k++) - { - int idx; - ConnectionInfo *con = pool_coninfo(i, j, k); - - for (idx = 0; idx < down_node_ids_index; idx++) - { - int node_id = down_node_ids[idx]; - - if (con->connected && con->load_balancing_node == node_id) - { - ereport(LOG, - (errmsg("child process with PID:%d needs restart, because pool %d uses backend %d", - process_info[i].pid, j, node_id))); - restart = true; - break; - } - if (restart) - break; - } - } - } - } - else + for (i = 0; i < down_node_ids_index; i++) { - restart = true; + int node_id = down_node_ids[i]; + conform_children_to_node_failure(node_id, false, false); } - - if (restart) - { - if (process_info[i].pid) - { - kill(process_info[i].pid, SIGQUIT); - - process_info[i].pid = fork_a_child(fds, i); - process_info[i].start_time = time(NULL); - process_info[i].client_connection_count = 0; - process_info[i].status = WAIT_FOR_CONNECT; - process_info[i].connected = 0; - process_info[i].wait_for_connect = 0; - process_info[i].pooled_connections = 0; - } - } - else - process_info[i].need_to_restart = 1; } - } - - else - { - /* - * Set restart request to each child. Children will exit(1) whenever - * they are convenient. - */ - for (i = 0; i < pool_config->num_init_children; i++) + else { - if (process_info[i].pid) - process_info[i].need_to_restart = 1; + conform_children_to_node_failure(0, true, false); } } @@ -3867,7 +3867,7 @@ sync_backend_from_watchdog(void) * version number is in the static memory area. */ static int -get_server_version(POOL_CONNECTION_POOL_SLOT * *slots, int node_id) +get_server_version(BackendNodeConnection **slots, int node_id) { static int server_versions[MAX_NUM_BACKENDS]; @@ -4303,13 +4303,104 @@ handle_failover_request(FAILOVER_CONTEXT *failover_context, int node_id) return 0; } +static void +conform_children_to_node_failure(int failed_node_id, bool need_total_reset, bool prepare_only) +{ + int i; + ProcessInfo* pi; + int max_respawn_children = pool_config->num_init_children; + if (pool_config->process_management == PM_DYNAMIC) + { + /* We only need to spawn max_spare_child number of processes*/ + max_respawn_children = pool_config->max_spare_children; + } + + if (need_total_reset) + { + /* We need to restart all child process. This could be the case + * when primary node fails + */ + ereport(LOG, + (errmsg("conform_children_to_node_failure: total reset requested for node id: %d", failed_node_id))); + /* + * if we are using global connection pool, we also need to invalidate + * all pooled connections + */ + InvalidateAllPooledConnections(NULL); + for (i =0; i < pool_config->num_init_children; i++) + { + ProcessInfo *pi = &process_info[i]; + if (pi->pid > 0) + { + child_process_exits(i, pi->pid); + kill(pi->pid, SIGQUIT); + if (prepare_only) + continue; + if (max_respawn_children > 0) + { + pi->pid = fork_a_child(fds, i); + pi->start_time = time(NULL); + pi->client_connection_count = 0; + pi->status = WAIT_FOR_CONNECT; + pi->connected = 0; + pi->wait_for_connect = 0; + pi->pooled_connections = 0; + max_respawn_children --; + } + else + pi->pid = 0; + } + } + return; + } + else + { + ereport(LOG, + (errmsg("conform_children_to_node_failure: partial reset requested for node id: %d", failed_node_id))); + InvalidateNodeInPooledConnections(failed_node_id); + for (i = 0; i < pool_config->num_init_children; i++) + { + ProcessInfo* pi = &process_info[i]; + ConnectionPoolEntry* pool_entry = GetConnectionPoolEntry(pi->pool_id, i); + if (pool_entry && pool_entry->endPoint.client_connected) + { + /* So the child process has an active connection + * See if it is also using the failed node + */ + if (pool_entry->endPoint.load_balancing_node == failed_node_id) + { + ereport(LOG, + (errmsg("conform_children_to_node_failure: child pid %d needs to restart because pool %d uses backend %d", + pi->pid, pi->pool_id, failed_node_id))); + child_process_exits(i, pi->pid); + kill(pi->pid, SIGQUIT); + if (prepare_only) + continue; + if (max_respawn_children > 0) + { + pi->pid = fork_a_child(fds, i); + pi->start_time = time(NULL); + pi->client_connection_count = 0; + pi->status = WAIT_FOR_CONNECT; + pi->connected = 0; + pi->wait_for_connect = 0; + pi->pooled_connections = 0; + max_respawn_children --; + } + else + pi->pid = 0; + } + } + } + } +} /* * Kill child process to prepare failover/failback. */ static void kill_failover_children(FAILOVER_CONTEXT *failover_context, int node_id) { - int i, j, k; + int i; /* * On 2011/5/2 Tatsuo Ishii says: if mode is streaming replication and * request is NODE_UP_REQUEST (failback case) we don't need to restart @@ -4346,6 +4437,14 @@ kill_failover_children(FAILOVER_CONTEXT *failover_context, int node_id) * requested node id is the former primary node. * * See bug 672 for more details. + * + * Muhammad Usama: Instead of main process restarting the children, we + * can offload this decision to individual child process. This will + * allow the child process to restart only if it has a leased connection + * and actively using the failed node. Moreover we can enhance this + * further by allowing the child process to decide if it can live + * without restarting, for example if their is no transaction in progress. + * */ if (STREAM && failover_context->reqkind == NODE_UP_REQUEST && failover_context->all_backend_down == false && Req_info->primary_node_id >= 0 && Req_info->primary_node_id != node_id) @@ -4381,64 +4480,21 @@ kill_failover_children(FAILOVER_CONTEXT *failover_context, int node_id) failover_context->need_to_restart_children = true; failover_context->partial_restart = true; + /* + * We do not need to restart the child process. All we need to do is to + * ask children that are using the failed node to return the pool back to + * gloabl pool + */ - for (i = 0; i < pool_config->num_init_children; i++) - { - bool restart = false; - - for (j = 0; j < pool_config->max_pool; j++) - { - for (k = 0; k < NUM_BACKENDS; k++) - { - ConnectionInfo *con = pool_coninfo(i, j, k); - - if (con->connected && con->load_balancing_node == node_id) - { - ereport(LOG, - (errmsg("child pid %d needs to restart because pool %d uses backend %d", - process_info[i].pid, j, node_id))); - restart = true; - break; - } - } - } - - if (restart) - { - pid_t pid = process_info[i].pid; - - if (pid) - { - kill(pid, SIGQUIT); - ereport(DEBUG1, - (errmsg("failover handler"), - errdetail("kill process with PID:%d", pid))); - } - } - } + conform_children_to_node_failure(node_id, false, true); } else { - ereport(LOG, - (errmsg("Restart all children"))); - - /* kill all children */ - for (i = 0; i < pool_config->num_init_children; i++) - { - pid_t pid = process_info[i].pid; - - if (pid) - { - kill(pid, SIGQUIT); - ereport(DEBUG1, - (errmsg("failover handler"), - errdetail("kill process with PID:%d", pid))); - } - } - failover_context->need_to_restart_children = true; failover_context->partial_restart = false; + conform_children_to_node_failure(node_id, false, true); } + } /* @@ -4664,69 +4720,22 @@ save_node_info(FAILOVER_CONTEXT *failover_context, int new_primary_node_id, int static void exec_child_restart(FAILOVER_CONTEXT *failover_context, int node_id) { - int i, j, k; + int i; if (failover_context->need_to_restart_children) { - for (i = 0; i < pool_config->num_init_children; i++) - { - /* - * Try to kill pgpool child because previous kill signal may - * not be received by pgpool child. This could happen if - * multiple PostgreSQL are going down (or even starting - * pgpool, without starting PostgreSQL can trigger this). - * Child calls degenerate_backend() and it tries to acquire - * semaphore to write a failover request. In this case the - * signal mask is set as well, thus signals are never - * received. - */ - - bool restart = false; - - if (failover_context->partial_restart) - { - for (j = 0; j < pool_config->max_pool; j++) - { - for (k = 0; k < NUM_BACKENDS; k++) - { - ConnectionInfo *con = pool_coninfo(i, j, k); - - if (con->connected && con->load_balancing_node == node_id) - { - - ereport(LOG, - (errmsg("child pid %d needs to restart because pool %d uses backend %d", - process_info[i].pid, j, node_id))); - restart = true; - break; - } - } - } - } - else - restart = true; - - if (restart) - { - if (process_info[i].pid) - { - kill(process_info[i].pid, SIGQUIT); - - process_info[i].pid = fork_a_child(fds, i); - process_info[i].start_time = time(NULL); - process_info[i].client_connection_count = 0; - process_info[i].status = WAIT_FOR_CONNECT; - process_info[i].connected = 0; - process_info[i].wait_for_connect = 0; - process_info[i].pooled_connections = 0; - - } - } - else - process_info[i].need_to_restart = 1; - } + /* + * Try to kill pgpool child because previous kill signal may + * not be received by pgpool child. This could happen if + * multiple PostgreSQL are going down (or even starting + * pgpool, without starting PostgreSQL can trigger this). + * Child calls degenerate_backend() and it tries to acquire + * semaphore to write a failover request. In this case the + * signal mask is set as well, thus signals are never + * received. + */ + conform_children_to_node_failure(node_id, !failover_context->partial_restart, false); } - else { /* @@ -5145,47 +5154,144 @@ service_child_processes(void) static int select_victim_processes(int *process_info_idxs, int count) { - int i, ki; - bool found_enough = false; - int selected_count = 0; + int i, ki; + bool found_enough = false; + int selected_count = 0; - if (count <= 0) - return 0; + if (count <= 0) + return 0; - for (i = 0; i < pool_config->num_init_children; i++) + for (i = 0; i < pool_config->num_init_children; i++) + { + /* Only the child process in waiting for connect can be terminated */ + if (process_info[i].pid && process_info[i].status == WAIT_FOR_CONNECT) { - /* Only the child process in waiting for connect can be terminated */ - if (process_info[i].pid && process_info[i].status == WAIT_FOR_CONNECT) + if (selected_count < count) { - if (selected_count < count) - { - process_info_idxs[selected_count++] = i; - } - else + process_info_idxs[selected_count++] = i; + } + else + { + found_enough = true; + /* we don't bother selecting the child having least pooled connection with + * aggressive strategy + */ + if (pool_config->process_management_strategy != PM_STRATEGY_AGGRESSIVE + && pool_config->connection_pool_type != GLOBAL_CONNECTION_POOL ) { - found_enough = true; - /* we don't bother selecting the child having least pooled connection with - * aggressive strategy - */ - if (pool_config->process_management_strategy != PM_STRATEGY_AGGRESSIVE) + for (ki = 0; ki < count; ki++) { - for (ki = 0; ki < count; ki++) + int old_index = process_info_idxs[ki]; + if (old_index < 0 || process_info[old_index].pooled_connections > process_info[i].pooled_connections) { - int old_index = process_info_idxs[ki]; - if (old_index < 0 || process_info[old_index].pooled_connections > process_info[i].pooled_connections) - { - process_info_idxs[ki] = i; - found_enough = false; - break; - } - if (process_info[old_index].pooled_connections) - found_enough = false; + process_info_idxs[ki] = i; + found_enough = false; + break; } + if (process_info[old_index].pooled_connections) + found_enough = false; } } } - if (found_enough) - break; } + if (found_enough) + break; + } return selected_count; } + +/* Returns true if signal was received while waiting */ +static bool +handle_child_ipc_requests(void) +{ + int fd_max; + int i; + int select_ret; + struct timeval tv; + fd_set rmask; + bool ret = false; + + if (pool_config->connection_pool_type != GLOBAL_CONNECTION_POOL) + return true; + + POOL_SETMASK(&UnBlockSig); + + FD_ZERO(&rmask); + fd_max = pipe_fds[0]; + + /* Just in case we receive some emergency signal */ + FD_SET(pipe_fds[0], &rmask); + + for (i = 0; i < pool_config->num_init_children; i++) + { + if (ipc_endpoints[i].child_link > 0 && ipc_endpoints[i].child_pid > 0) + { + FD_SET(ipc_endpoints[i].child_link, &rmask); + if (fd_max < ipc_endpoints[i].child_link) + fd_max = ipc_endpoints[i].child_link; + } + } + tv.tv_sec = 2; + tv.tv_usec = 0; + + select_ret = select(fd_max + 1, &rmask, NULL, NULL, &tv); + + POOL_SETMASK(&BlockSig); + + if (select_ret > 0) + { + int reads = 0; + if (FD_ISSET(pipe_fds[0], &rmask)) + { + char dummy; + ret = true; + if (read(pipe_fds[0], &dummy, 1) < 0) + ereport(WARNING, + (errmsg("handle_child_ipc_requests: read on pipe failed"), + errdetail("%m"))); + reads++; + if (reads >= select_ret) + return ret; + } + for (i = 0; i < pool_config->num_init_children; i++) + { + if (ipc_endpoints[i].child_link > 0 && ipc_endpoints[i].child_pid > 0) + { + if (FD_ISSET(ipc_endpoints[i].child_link, &rmask)) + { + ereport(DEBUG2, + (errmsg("IPC end point:%d from child:%d is ready",i, ipc_endpoints[i].child_pid))); + + if (ProcessChildRequestOnMain(&ipc_endpoints[i]) == false) + ereport(LOG, (errmsg("failed to process child:%d request on main",ipc_endpoints[i].child_pid))); + + if (reads++ >= select_ret) + break; + } + } + } + } + return ret; +} + +static void +child_process_exits(int child_id, pid_t child_pid) +{ + Assert(child_id >= 0 && child_id < pool_config->num_init_children); + Assert(child_pid > 0); + if (pool_config->connection_pool_type == GLOBAL_CONNECTION_POOL) + { + /* + * Inform global connection pool to release + * leased connection (if any) for this child + */ + GlobalPoolChildProcessDied(child_id, child_pid); + /* Close the ipc endpoint for the exited child */ + if (ipc_endpoints[child_id].child_link > 0) + close(ipc_endpoints[child_id].child_link); + process_info[child_id].pool_id = -1; + ipc_endpoints[child_id].child_link = -1; + ipc_endpoints[child_id].child_pid = 0; + ipc_endpoints[child_id].proc_info_id = -1; + } +} \ No newline at end of file diff --git a/src/parser/Makefile.am b/src/parser/Makefile.am index ce1dffaf..7484dd4f 100644 --- a/src/parser/Makefile.am +++ b/src/parser/Makefile.am @@ -28,7 +28,7 @@ endif EXTRA_DIST = scan.l ARFLAGS = cr -AM_YFLAGS = -d -Wno-yacc +AM_YFLAGS = -d gram.c: gram.y scan.c gram_minimal.c: gram_minimal.y scan.c diff --git a/src/parser/list.c b/src/parser/list.c index 62a01fa0..7a7e65dc 100644 --- a/src/parser/list.c +++ b/src/parser/list.c @@ -20,10 +20,10 @@ #include "utils/elog.h" #include #include "utils/palloc.h" +#include "utils/memutils.h" +#include "utils/memdebug.h" #include "pg_list.h" -static inline MemoryContext GetMemoryChunkContext(void *pointer); - /* * The previous List implementation, since it used a separate palloc chunk * for each cons cell, had the property that adding or deleting list cells @@ -1694,38 +1694,3 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2) return 0; } -/* - * GetMemoryChunkContext - * Given a currently-allocated chunk, determine the context - * it belongs to. - * - * All chunks allocated by any memory context manager are required to be - * preceded by the corresponding MemoryContext stored, without padding, in the - * preceding sizeof(void*) bytes. A currently-allocated chunk must contain a - * backpointer to its owning context. The backpointer is used by pfree() and - * repalloc() to find the context to call. - */ -#ifndef FRONTEND -static inline MemoryContext -GetMemoryChunkContext(void *pointer) -{ - MemoryContext context; - - /* - * Try to detect bogus pointers handed to us, poorly though we can. - * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an - * allocated chunk. - */ - Assert(pointer != NULL); - Assert(pointer == (void *) MAXALIGN(pointer)); - - /* - * OK, it's probably safe to look at the context. - */ - context = *(MemoryContext *) (((char *) pointer) - sizeof(void *)); - - AssertArg(MemoryContextIsValid(context)); - - return context; -} -#endif diff --git a/src/protocol/CommandComplete.c b/src/protocol/CommandComplete.c index a6605ddb..031fc88c 100644 --- a/src/protocol/CommandComplete.c +++ b/src/protocol/CommandComplete.c @@ -40,13 +40,13 @@ #include "utils/pool_stream.h" static int extract_ntuples(char *message); -static POOL_STATUS handle_mismatch_tuples(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *packet, int packetlen, bool command_complete); +static POOL_STATUS handle_mismatch_tuples(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *packet, int packetlen, bool command_complete); static int forward_command_complete(POOL_CONNECTION * frontend, char *packet, int packetlen); static int forward_empty_query(POOL_CONNECTION * frontend, char *packet, int packetlen); static int forward_packet_to_frontend(POOL_CONNECTION * frontend, char kind, char *packet, int packetlen); POOL_STATUS -CommandComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, bool command_complete) +CommandComplete(POOL_CONNECTION * frontend, BackendClusterConnection * backend, bool command_complete) { int len, len1; @@ -280,7 +280,7 @@ CommandComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, bool * Handle misc process which is necessary when query context exists. */ void -handle_query_context(POOL_CONNECTION_POOL * backend) +handle_query_context(BackendClusterConnection * backend) { POOL_SESSION_CONTEXT *session_context; Node *node; @@ -460,7 +460,7 @@ extract_ntuples(char *message) /* * Handle mismatch tuples */ -static POOL_STATUS handle_mismatch_tuples(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *packet, int packetlen, bool command_complete) +static POOL_STATUS handle_mismatch_tuples(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *packet, int packetlen, bool command_complete) { POOL_SESSION_CONTEXT *session_context; diff --git a/src/protocol/child.c b/src/protocol/child.c index c12a5a2c..126fd48f 100644 --- a/src/protocol/child.c +++ b/src/protocol/child.c @@ -57,6 +57,7 @@ #include "utils/elog.h" #include "utils/ps_status.h" #include "utils/timestamp.h" +#include "main/pgpool_ipc.h" #include "context/pool_process_context.h" #include "context/pool_session_context.h" @@ -68,18 +69,20 @@ #include "auth/pool_passwd.h" #include "auth/pool_hba.h" +#define POOL_LEASE_RETRY_INTERVAL_USEC 500 + static StartupPacket *read_startup_packet(POOL_CONNECTION * cp); -static POOL_CONNECTION_POOL * connect_backend(StartupPacket *sp, POOL_CONNECTION * frontend); +static BackendClusterConnection * connect_backend(StartupPacket *sp, POOL_CONNECTION * frontend, int pool_id); static RETSIGTYPE die(int sig); static RETSIGTYPE close_idle_connection(int sig); static RETSIGTYPE wakeup_handler(int sig); static RETSIGTYPE reload_config_handler(int sig); static RETSIGTYPE authentication_timeout(int sig); -static void send_params(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +static void send_params(POOL_CONNECTION * frontend, BackendClusterConnection * backend); static int connection_count_up(void); static void connection_count_down(void); static bool connect_using_existing_connection(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, StartupPacket *sp); static void check_restart_request(void); static void check_exit_request(void); @@ -90,12 +93,11 @@ static void check_config_reload(void); static void get_backends_status(unsigned int *valid_backends, unsigned int *down_backends); static void validate_backend_connectivity(int front_end_fd); static POOL_CONNECTION * get_connection(int front_end_fd, SockAddr *saddr); -static POOL_CONNECTION_POOL * get_backend_connection(POOL_CONNECTION * frontend); -static StartupPacket *StartupPacketCopy(StartupPacket *sp); +static BackendClusterConnection * get_backend_connection(POOL_CONNECTION * frontend); static void log_disconnections(char *database, char *username); static void print_process_status(char *remote_host, char *remote_port); -static bool backend_cleanup(POOL_CONNECTION * volatile *frontend, POOL_CONNECTION_POOL * volatile backend, bool frontend_invalid); - +static bool backend_cleanup(POOL_CONNECTION * volatile *frontend, BackendClusterConnection * volatile backend, bool frontend_invalid); +static void connect_missing_backend_nodes(StartupPacket *sp, POOL_CONNECTION * frontend, int pool_id); static void child_will_go_down(int code, Datum arg); static int opt_sort(const void *a, const void *b); @@ -136,6 +138,7 @@ POOL_CONNECTION *volatile child_frontend = NULL; struct timeval startTime; +int parent_link_fd = -1; #ifdef DEBUG bool stop_now = false; #endif @@ -144,10 +147,10 @@ bool stop_now = false; * child main loop */ void -do_child(int *fds) +do_child(int *fds, int ipc_fd) { sigjmp_buf local_sigjmp_buf; - POOL_CONNECTION_POOL *volatile backend = NULL; + BackendClusterConnection *volatile backend = NULL; struct timeval now; struct timezone tz; @@ -176,8 +179,11 @@ do_child(int *fds) signal(SIGUSR2, wakeup_handler); signal(SIGPIPE, SIG_IGN); + parent_link_fd = ipc_fd; + on_system_exit(child_will_go_down, (Datum) NULL); + int *walk; #ifdef NONE_BLOCK /* set listen fds to none-blocking */ @@ -219,15 +225,12 @@ do_child(int *fds) #endif /* initialize connection pool */ - if (pool_init_cp()) - { - child_exit(POOL_EXIT_AND_RESTART); - } - + // pool_init_cp(parent_link_fd); + LoadChildConnectionPool(parent_link_fd); /* - * Open pool_passwd in child process. This is necessary to avoid the file - * descriptor race condition reported in [pgpool-general: 1141]. - */ + * 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(); @@ -278,7 +281,7 @@ do_child(int *fds) 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()); + pool_coninfo_unset_frontend_connected(); /* increment queries counter if necessary */ if (pool_config->child_max_connections > 0) @@ -298,10 +301,10 @@ do_child(int *fds) if (child_frontend) { - pool_close(child_frontend); + pool_close(child_frontend, true); child_frontend = NULL; } - update_pooled_connection_count(); + UpdatePooledConnectionCount(); MemoryContextSwitchTo(TopMemoryContext); FlushErrorState(); } @@ -320,6 +323,8 @@ do_child(int *fds) MemoryContextSwitchTo(ProcessLoopContext); MemoryContextResetAndDeleteChildren(ProcessLoopContext); + ResetBackendClusterConnection(); + backend = NULL; idle = 1; @@ -376,7 +381,7 @@ do_child(int *fds) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("Sorry, too many clients already"))); - pool_close(cp); + pool_close(cp, true); continue; } } @@ -387,7 +392,6 @@ do_child(int *fds) check_config_reload(); validate_backend_connectivity(front_end_fd); child_frontend = get_connection(front_end_fd, &saddr); - /* set frontend fd to blocking */ socket_unset_nonblock(child_frontend->fd); @@ -414,11 +418,11 @@ do_child(int *fds) * frontend <--> pgpool and pgpool <--> backend. */ backend = get_backend_connection(child_frontend); - if (!backend) + if (!backend || !backend->sp) { if (pool_config->log_disconnections) log_disconnections(child_frontend->database, child_frontend->username); - pool_close(child_frontend); + pool_close(child_frontend, true); child_frontend = NULL; continue; } @@ -427,7 +431,7 @@ do_child(int *fds) /* * show ps status */ - sp = MAIN_CONNECTION(backend)->sp; + sp = backend->sp; snprintf(psbuf, sizeof(psbuf), "%s %s %s idle", sp->user, sp->database, remote_ps_data); set_ps_display(psbuf, false); @@ -447,7 +451,7 @@ do_child(int *fds) /* * Mark this connection pool is connected from frontend */ - pool_coninfo_set_frontend_connected(pool_get_process_context()->proc_id, pool_pool_index()); + pool_coninfo_set_frontend_connected(); /* create memory context for query processing */ QueryContext = AllocSetContextCreate(ProcessLoopContext, @@ -476,13 +480,8 @@ do_child(int *fds) 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()); - - /* - * Update number of established connections in the connection pool. - */ - update_pooled_connection_count(); - + pool_coninfo_unset_frontend_connected(); + UpdatePooledConnectionCount(); accepted = 0; connection_count_down(); if (pool_config->log_disconnections) @@ -518,7 +517,7 @@ do_child(int *fds) * return true if backend connection is cached */ static bool -backend_cleanup(POOL_CONNECTION * volatile *frontend, POOL_CONNECTION_POOL * volatile backend, bool frontend_invalid) +backend_cleanup(POOL_CONNECTION * volatile *frontend, BackendClusterConnection * volatile backend, bool frontend_invalid) { StartupPacket *sp; bool cache_connection = false; @@ -526,7 +525,7 @@ backend_cleanup(POOL_CONNECTION * volatile *frontend, POOL_CONNECTION_POOL * vol if (backend == NULL) return false; - sp = MAIN_CONNECTION(backend)->sp; + sp = backend->sp; /* * cache connection if connection cache configuration parameter is enabled @@ -567,7 +566,7 @@ backend_cleanup(POOL_CONNECTION * volatile *frontend, POOL_CONNECTION_POOL * vol if ((sp && (!strcmp(sp->database, "template0") || !strcmp(sp->database, "template1") || - !strcmp(sp->database, "postgres") || + !strcmp(sp->database, "postgres_no") || !strcmp(sp->database, "regression"))) || (*frontend != NULL && ((*frontend)->socket_state == POOL_SOCKET_EOF || @@ -583,17 +582,26 @@ backend_cleanup(POOL_CONNECTION * volatile *frontend, POOL_CONNECTION_POOL * vol if (*frontend != NULL) { - pool_close(*frontend); + pool_close(*frontend, true); *frontend = NULL; } if (cache_connection == false) { - pool_send_frontend_exits(backend); - if (sp) - pool_discard_cp(sp->user, sp->database, sp->major); + DiscardCurrentBackendConnection(); + } + else if (ClusterConnectionNeedPush()) + { + BackendClusterConnection *current_backend_connection = GetBackendClusterConnection(); + ereport(LOG, + (errmsg("Backend connection for:%s:%s pushed back to pool:%d", + current_backend_connection->backend_end_point->database, current_backend_connection->backend_end_point->user, + current_backend_connection->pool_id))); + PushClusterConnectionToPool(); } + ReleaseClusterConnection(!cache_connection); + /* reset the config parameters */ reset_all_variables(NULL, NULL); return cache_connection; @@ -793,38 +801,13 @@ read_startup_packet(POOL_CONNECTION * cp) */ static bool connect_using_existing_connection(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, StartupPacket *sp) { - int i, - freed = 0; - StartupPacket *topmem_sp = NULL; + int i; MemoryContext oldContext; MemoryContext frontend_auth_cxt; - /* - * Save startup packet info - */ - for (i = 0; i < NUM_BACKENDS; i++) - { - if (VALID_BACKEND(i)) - { - if (!freed) - { - oldContext = MemoryContextSwitchTo(TopMemoryContext); - - topmem_sp = StartupPacketCopy(sp); - MemoryContextSwitchTo(oldContext); - - pool_free_startup_packet(backend->slots[i]->sp); - backend->slots[i]->sp = NULL; - - freed = 1; - } - backend->slots[i]->sp = topmem_sp; - } - } - /* Reuse existing connection to backend */ frontend_auth_cxt = AllocSetContextCreate(CurrentMemoryContext, "frontend_auth", @@ -851,7 +834,7 @@ connect_using_existing_connection(POOL_CONNECTION * frontend, for (i = 0; i < NUM_BACKENDS; i++) { - if (VALID_BACKEND(i)) + if (VALID_BACKEND(i) && CONNECTION(backend, i)) { /* * We want to catch and ignore errors in do_command if a @@ -861,12 +844,16 @@ connect_using_existing_connection(POOL_CONNECTION * frontend, * "SET application_name" command if the backend goes * down. */ + // pool_param_debug_print(&backend->backend_end_point->params); + // pool_init_params(&backend->backend_end_point->params); + PG_TRY(); { + /* Question: Why sending same pid and key everytime ?*/ do_command(frontend, CONNECTION(backend, i), command_buf, MAJOR(backend), - MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key, 0); + MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key, 0); } PG_CATCH(); { @@ -877,7 +864,8 @@ connect_using_existing_connection(POOL_CONNECTION * frontend, PG_END_TRY(); } } - pool_add_param(&MAIN(backend)->params, "application_name", sp->application_name); + + pool_add_param(&backend->backend_end_point->params, "application_name", sp->application_name); set_application_name_with_string(sp->application_name); } @@ -911,13 +899,10 @@ cancel_request(CancelPacket * sp) { int len; int fd; + PooledBackendClusterConnection* backend_end_point; POOL_CONNECTION *con; - int i, - j, - k; - ConnectionInfo *c = NULL; + int i; CancelPacket cp; - bool found = false; if (pool_config->log_client_messages) ereport(LOG, @@ -926,41 +911,16 @@ cancel_request(CancelPacket * sp) ereport(DEBUG1, (errmsg("Cancel request received"))); - /* look for cancel key from shmem info */ - for (i = 0; i < pool_config->num_init_children; i++) - { - for (j = 0; j < pool_config->max_pool; j++) - { - for (k = 0; k < NUM_BACKENDS; k++) - { - c = pool_coninfo(i, j, k); - ereport(DEBUG2, - (errmsg("processing cancel request"), - errdetail("connection info: address:%p database:%s user:%s pid:%d key:%d i:%d", - c, c->database, c->user, ntohl(c->pid), ntohl(c->key), i))); - if (c->pid == sp->pid && c->key == sp->key) - { - ereport(DEBUG1, - (errmsg("processing cancel request"), - errdetail("found pid:%d key:%d i:%d", ntohl(c->pid), ntohl(c->key), i))); - - c = pool_coninfo(i, j, 0); - found = true; - goto found; - } - } - } - } - -found: - if (!found) + backend_end_point = GetBackendEndPointForCancelPacket(sp); + if (backend_end_point == NULL) { ereport(LOG, - (errmsg("invalid cancel key: pid:%d key:%d", ntohl(sp->pid), ntohl(sp->key)))); - return; /* invalid key */ + (errmsg("invalid cancel request received"), + errdetail("invalid pid: %d key: %d", ntohl(sp->pid), ntohl(sp->key)))); + return; } - for (i = 0; i < NUM_BACKENDS; i++, c++) + for (i = 0; i < NUM_BACKENDS; i++) { if (!VALID_BACKEND(i)) continue; @@ -987,8 +947,8 @@ found: pool_write(con, &len, sizeof(len)); cp.protoVersion = sp->protoVersion; - cp.pid = c->pid; - cp.key = c->key; + cp.pid = backend_end_point->conn_slots[i].pid; + cp.key = backend_end_point->conn_slots[i].key; ereport(LOG, (errmsg("forwarding cancel request to backend"), @@ -998,7 +958,7 @@ found: ereport(WARNING, (errmsg("failed to send cancel request to backend %d", i))); - pool_close(con); + pool_close(con, true); /* * this is needed to ensure that the next DB node executes the query @@ -1008,73 +968,92 @@ found: } } -/* - * Copy startup packet and return it. - * palloc is used. - */ -static StartupPacket * -StartupPacketCopy(StartupPacket *sp) -{ - StartupPacket *new_sp; - /* verify the length first */ - if (sp->len <= 0 || sp->len >= MAX_STARTUP_PACKET_LENGTH) - ereport(ERROR, - (errmsg("incorrect packet length (%d)", sp->len))); +static void +connect_missing_backend_nodes(StartupPacket *sp, POOL_CONNECTION * frontend, int pool_id) +{ + volatile int i; + int reconnect_count = 0; + MemoryContext frontend_auth_cxt, oldContext; + BackendClusterConnection *child_backend_connection = GetBackendClusterConnection(); + frontend_auth_cxt = NULL; + for (i = 0; i < NUM_BACKENDS; i++) + { + if (child_backend_connection->slots[i].state == CONNECTION_SLOT_SOCKET_CONNECTION_ONLY) + { + if (!frontend_auth_cxt) + frontend_auth_cxt = AllocSetContextCreate(CurrentMemoryContext, + "frontend_auth", + ALLOCSET_DEFAULT_SIZES); + PG_TRY(); + { + ereport(LOG, + (errmsg("Backend node:%d status was changed. connecting...",i))); - new_sp = (StartupPacket *) palloc0(sizeof(*sp)); - new_sp->startup_packet = palloc0(sp->len); - memcpy(new_sp->startup_packet, sp->startup_packet, sp->len); - new_sp->len = sp->len; + pool_ssl_negotiate_clientserver(child_backend_connection->slots[i].con); + send_startup_packet(&child_backend_connection->slots[i], sp); - new_sp->major = sp->major; - new_sp->minor = sp->minor; + oldContext = MemoryContextSwitchTo(frontend_auth_cxt); + connection_do_auth(child_backend_connection, i,NULL, frontend, NULL,sp); + MemoryContextSwitchTo(oldContext); - new_sp->database = pstrdup(sp->database); - new_sp->user = pstrdup(sp->user); + child_backend_connection->slots[i].state = CONNECTION_SLOT_VALID_LOCAL_CONNECTION; + child_backend_connection->backend_end_point->backend_status[i] = CON_UP; + reconnect_count++; + } + PG_CATCH(); + { + /* ignore the error message */ + // EmitErrorReport(); For debug + FlushErrorState(); + ereport(LOG, + (errmsg("Failed to connect backend node:%d",i))); + child_backend_connection->slots[i].state = CONNECTION_SLOT_SOCKET_CONNECTION_ERROR; + } + PG_END_TRY(); + } + } + if (frontend_auth_cxt) + { + MemoryContextSwitchTo(oldContext); + MemoryContextDelete(frontend_auth_cxt); + } - if (new_sp->major == PROTO_MAJOR_V3 && sp->application_name) + if (reconnect_count > 0) { - /* adjust the application name pointer in new packet */ - new_sp->application_name = new_sp->startup_packet + (sp->application_name - sp->startup_packet); + ereport(LOG,(errmsg("Reconnected %d backend nodes to pool:%d",reconnect_count,pool_id))); + /* TODO push the socket back right here or should we do it at end of session ?*/ } - return new_sp; } /* * Create a new connection to backend. * Authentication is performed if requested by backend. */ -static POOL_CONNECTION_POOL * connect_backend(StartupPacket *sp, POOL_CONNECTION * frontend) +static BackendClusterConnection * +connect_backend(StartupPacket *sp, POOL_CONNECTION * frontend, int pool_id) { - POOL_CONNECTION_POOL *backend; - StartupPacket *volatile topmem_sp = NULL; - volatile bool topmem_sp_set = false; + BackendClusterConnection *backend = GetBackendClusterConnection(); int i; /* connect to the backend */ - backend = pool_create_cp(); - if (backend == NULL) + if (ConnectBackendClusterSockets() == false) { pool_send_error_message(frontend, sp->major, "XX000", "all backend nodes are down, pgpool requires at least one valid node", "", "repair the backend nodes and restart pgpool", __FILE__, __LINE__); ereport(ERROR, - (errmsg("unable to connect to backend"), + (errmsg("unable to connect to backend"), errdetail("all backend nodes are down, pgpool requires at least one valid node"), errhint("repair the backend nodes and restart pgpool"))); } PG_TRY(); { - MemoryContext frontend_auth_cxt; - MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); - - topmem_sp = StartupPacketCopy(sp); - MemoryContextSwitchTo(oldContext); + MemoryContext frontend_auth_cxt, oldContext; for (i = 0; i < NUM_BACKENDS; i++) { - if (VALID_BACKEND(i) && CONNECTION_SLOT(backend, i)) + if (VALID_BACKEND(i) && CONNECTION(backend, i)) { /* set DB node id */ pool_set_db_node_id(CONNECTION(backend, i), i); @@ -1084,17 +1063,13 @@ static POOL_CONNECTION_POOL * connect_backend(StartupPacket *sp, POOL_CONNECTION pool_ssl_negotiate_clientserver(CONNECTION(backend, i)); - /* - * save startup packet info - */ - CONNECTION_SLOT(backend, i)->sp = topmem_sp; - topmem_sp_set = true; - /* send startup packet */ - send_startup_packet(CONNECTION_SLOT(backend, i)); + send_startup_packet(&CONNECTION_SLOT(backend, i), sp); } } + SetupNewConnectionIntoChild(sp); + /* * do authentication stuff */ @@ -1102,29 +1077,27 @@ static POOL_CONNECTION_POOL * connect_backend(StartupPacket *sp, POOL_CONNECTION "frontend_auth", ALLOCSET_DEFAULT_SIZES); oldContext = MemoryContextSwitchTo(frontend_auth_cxt); - /* do authentication against backend */ pool_do_auth(frontend, backend); MemoryContextSwitchTo(oldContext); MemoryContextDelete(frontend_auth_cxt); + SyncClusterConnectionDataInPool(); } PG_CATCH(); { - pool_discard_cp(sp->user, sp->database, sp->major); - topmem_sp = NULL; + DiscardCurrentBackendConnection(); + ReleaseClusterConnection(true); PG_RE_THROW(); } PG_END_TRY(); - /* At this point, we need to free previously allocated memory for the - * startup packet if no backend is up. - */ - if (!topmem_sp_set && topmem_sp != NULL) - pfree(topmem_sp); + /* save current backend status to pool */ + for (i = 0; i < MAX_NUM_BACKENDS; i++) + backend->backend_end_point->backend_status[i] = BACKEND_INFO(i).backend_status; - return backend; + return GetBackendClusterConnection(); } /* @@ -1193,9 +1166,10 @@ static RETSIGTYPE die(int sig) */ static RETSIGTYPE close_idle_connection(int sig) { +#ifdef NOT_USED int i, j; - POOL_CONNECTION_POOL *p = pool_connection_pool; + BackendClusterConnection *p = pool_connection_pool; ConnectionInfo *info; int save_errno = errno; int main_node_id; @@ -1236,16 +1210,17 @@ static RETSIGTYPE close_idle_connection(int sig) CONNECTION_SLOT(p, i)->sp = NULL; freed = true; } - pool_close(CONNECTION(p, i)); + pool_close(CONNECTION(p, i), true); /* TODO */ } info = p->info; - memset(p, 0, sizeof(POOL_CONNECTION_POOL)); + memset(p, 0, sizeof(BackendClusterConnection)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo)); } } errno = save_errno; +#endif } /* @@ -1290,7 +1265,7 @@ disable_authentication_timeout(void) * Send parameter status message to frontend. */ static void -send_params(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +send_params(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { int index; char *name, @@ -1299,7 +1274,7 @@ send_params(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) sendlen; index = 0; - while (pool_get_param(&MAIN(backend)->params, index++, &name, &value) == 0) + while (pool_get_param(&backend->backend_end_point->params, index++, &name, &value) == 0) { pool_write(frontend, "S", 1); len = sizeof(sendlen) + strlen(name) + 1 + strlen(value) + 1; @@ -1330,6 +1305,10 @@ child_will_go_down(int code, Datum arg) (errmsg("child_exit: called from invalid process. ignored."))); return; } + ReleaseChildConnectionPool(); + /* Close IPC Con */ + if (parent_link_fd != -1) + close(parent_link_fd); /* count down global connection counter */ if (accepted) @@ -1351,8 +1330,8 @@ child_will_go_down(int code, Datum arg) } /* let backend know now we are exiting */ - if (pool_connection_pool) - close_all_backend_connections(); + // if (pool_connection_pool) + // close_all_backend_connections(); } /* @@ -1961,12 +1940,17 @@ get_connection(int front_end_fd, SockAddr *saddr) * Connect to backend. Also do authentication between client <--> pgpool and * pgpool <--> backend. */ -static POOL_CONNECTION_POOL * +static BackendClusterConnection * get_backend_connection(POOL_CONNECTION * frontend) { - int found = 0; StartupPacket *sp; - POOL_CONNECTION_POOL *backend; + BorrowConnectionRes *borrowed_res; + LEASE_TYPES lease_type; + int retry_count = 0; + BackendClusterConnection *backend = GetBackendClusterConnection(); + ProcessInfo *pro_info = pool_get_my_process_info(); + int pool_availability_wait_usec = pool_config->pool_availability_timeout * 1000; + /* read the startup packet */ retry_startup: @@ -2051,69 +2035,85 @@ retry_startup: pool_get_my_process_info()->need_to_restart = 0; close_idle_connection(0); pool_initialize_private_backend_status(); + /* Re-Adjust the backend node connections */ + } + /* Ask for a connection from main process */ + for(;;) + { + borrowed_res = BorrowClusterConnection(frontend->database, frontend->username, frontend->protoVersion, 0); + if (borrowed_res->lease_type != LEASE_TYPE_NO_AVAILABLE_SLOT || pool_availability_wait_usec <= 0) + break; + pfree(borrowed_res); + usleep(POOL_LEASE_RETRY_INTERVAL_USEC); + ereport(LOG,(errmsg("No slot available in connection pool, retrying...[%d]", ++retry_count), + errdetail("database:%s user:%s", frontend->database, frontend->username))); + pool_availability_wait_usec -= POOL_LEASE_RETRY_INTERVAL_USEC; } - /* - * if there's no connection associated with user and database, we need to - * connect to the backend and send the startup packet. - */ - - /* look for an existing connection */ - found = 0; - - backend = pool_get_cp(sp->user, sp->database, sp->major, 1); + LoadBorrowedConnection(borrowed_res); + lease_type = borrowed_res->lease_type; + pfree(borrowed_res); - if (backend != NULL) + if (lease_type == LEASE_TYPE_DISCART_AND_CREATE || lease_type == LEASE_TYPE_READY_TO_USE) { - found = 1; + ereport(DEBUG2, + (errmsg("received sockets from parent pool_id:%d", pro_info->pool_id), + errdetail("database:%s user:%s protoMajor:%d", frontend->database, frontend->username, frontend->protoVersion))); + } - /* - * 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 != MAIN_CONNECTION(backend)->sp->len) + if (lease_type == LEASE_TYPE_READY_TO_USE) + { + BackendClusterConnection* current_cluster_con = GetBackendClusterConnection(); + if (current_cluster_con->sp == NULL) { - ereport(DEBUG1, - (errmsg("selecting backend connection"), - errdetail("connection exists but startup packet length is not identical"))); - - found = 0; + ereport(ERROR, + (errmsg("No startup packet found in the borrowed connection"), + errdetail("database:%s user:%s protoMajor:%d", frontend->database, frontend->username, frontend->protoVersion))); } - else if (memcmp(sp->startup_packet, MAIN_CONNECTION(backend)->sp->startup_packet, sp->len) != 0) + if (current_cluster_con->sp->len != sp->len || memcmp(current_cluster_con->sp->startup_packet, sp->startup_packet, sp->len) != 0) { - ereport(DEBUG1, - (errmsg("selecting backend connection"), - errdetail("connection exists but startup packet contents is not identical"))); - found = 0; + ereport(LOG, + (errmsg("startup packet mismatch in the borrowed connection"), + errdetail("database:%s user:%s protoMajor:%d", frontend->database, frontend->username, frontend->protoVersion))); + ereport(LOG, + (errmsg("discarding ready to use connection because of startup packet mismatch"), + errdetail("database:%s user:%s protoMajor:%d", sp->database, sp->user, sp->major))); + DiscardCurrentBackendConnection(); + ClearChildPooledConnectionData(); + lease_type = LEASE_TYPE_EMPTY_SLOT_RESERVED; } - - if (found == 0) + else { - /* - * we need to discard existing connection since startup packet is - * different - */ - pool_discard_cp(sp->user, sp->database, sp->major); - backend = NULL; + ereport(LOG, + (errmsg("Using the exisiting pooled connection at pool_id:%d", pro_info->pool_id), + errdetail("database:%s user:%s protoMajor:%d", frontend->database, frontend->username, frontend->protoVersion))); + connect_missing_backend_nodes(sp, frontend, pro_info->pool_id); + if (!connect_using_existing_connection(frontend, backend, sp)) + return NULL; + return backend; } } - - if (backend == NULL) + else if (lease_type == LEASE_TYPE_DISCART_AND_CREATE) { - /* - * Create a new connection to backend. - * Authentication is performed if requested by backend. - */ - backend = connect_backend(sp, frontend); + DiscardCurrentBackendConnection(); + ClearChildPooledConnectionData(); } - else + + if (lease_type == LEASE_TYPE_DISCART_AND_CREATE || lease_type == LEASE_TYPE_EMPTY_SLOT_RESERVED) { - /* reuse existing connection */ - if (!connect_using_existing_connection(frontend, backend, sp)) - return NULL; + /* Create a new connection */ + return connect_backend(sp, frontend, pro_info->pool_id); } + if (lease_type == LEASE_TYPE_NO_AVAILABLE_SLOT) + { + ereport(ERROR, + (errmsg("no pool slot available for database:%s user:%s protoMajor:%d",frontend->database, frontend->username, frontend->protoVersion), + errhint("Consider increasing the max_pool_size"))); + // WaitForSlotToBeAvailable(pro_info->pool_id); + /* Try again */ + return NULL; + } pool_free_startup_packet(sp); return backend; } diff --git a/src/protocol/pool_connection_pool.c b/src/protocol/pool_connection_pool.c index 79ee7e78..770fbf4d 100644 --- a/src/protocol/pool_connection_pool.c +++ b/src/protocol/pool_connection_pool.c @@ -43,7 +43,10 @@ #include #include "pool.h" +#include "connection_pool/connection_pool.h" +#include "main/pgpool_ipc.h" #include "context/pool_query_context.h" +#include "context/pool_session_context.h" #include "utils/pool_stream.h" #include "utils/palloc.h" #include "pool_config.h" @@ -57,308 +60,26 @@ #include "context/pool_process_context.h" +int parent_link = -1; static int pool_index; /* Active pool index */ -POOL_CONNECTION_POOL *pool_connection_pool; /* connection pool */ +// BackendClusterConnection child_backend_connection; + volatile sig_atomic_t backend_timer_expired = 0; /* flag for connection * closed timer is expired */ volatile sig_atomic_t health_check_timer_expired; /* non 0 if health check * timer expired */ -static POOL_CONNECTION_POOL_SLOT * create_cp(POOL_CONNECTION_POOL_SLOT * cp, int slot); -static POOL_CONNECTION_POOL * new_connection(POOL_CONNECTION_POOL * p); -static int check_socket_status(int fd); static bool connect_with_timeout(int fd, struct addrinfo *walk, char *host, int port, bool retry); #define TMINTMAX 0x7fffffff -/* -* initialize connection pools. this should be called once at the startup. -*/ -int -pool_init_cp(void) -{ - int i; - MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); - - pool_connection_pool = (POOL_CONNECTION_POOL *) palloc(sizeof(POOL_CONNECTION_POOL) * pool_config->max_pool); - memset(pool_connection_pool, 0, sizeof(POOL_CONNECTION_POOL) * pool_config->max_pool); - - for (i = 0; i < pool_config->max_pool; i++) - { - pool_connection_pool[i].info = pool_coninfo(pool_get_process_context()->proc_id, i, 0); - memset(pool_connection_pool[i].info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); - } - MemoryContextSwitchTo(oldContext); - return 0; -} - -/* -* find connection by user and database -*/ -POOL_CONNECTION_POOL * -pool_get_cp(char *user, char *database, int protoMajor, int check_socket) -{ - pool_sigset_t oldmask; - - int i, - freed = 0; - ConnectionInfo *info; - - POOL_CONNECTION_POOL *connection_pool = pool_connection_pool; - - if (connection_pool == NULL) - { - /* if no connection pool exists we have no reason to live */ - ereport(ERROR, - (return_code(2), - errmsg("unable to get connection"), - errdetail("connection pool is not initialized"))); - } - - POOL_SETMASK2(&BlockSig, &oldmask); - - for (i = 0; i < pool_config->max_pool; i++) - { - if (MAIN_CONNECTION(connection_pool) && - MAIN_CONNECTION(connection_pool)->sp && - MAIN_CONNECTION(connection_pool)->sp->major == protoMajor && - MAIN_CONNECTION(connection_pool)->sp->user != NULL && - strcmp(MAIN_CONNECTION(connection_pool)->sp->user, user) == 0 && - strcmp(MAIN_CONNECTION(connection_pool)->sp->database, database) == 0) - { - int sock_broken = 0; - int j; - - /* mark this connection is under use */ - MAIN_CONNECTION(connection_pool)->closetime = 0; - for (j = 0; j < NUM_BACKENDS; j++) - { - connection_pool->info[j].counter++; - } - POOL_SETMASK(&oldmask); - - if (check_socket) - { - for (j = 0; j < NUM_BACKENDS; j++) - { - if (!VALID_BACKEND(j)) - continue; - - if (CONNECTION_SLOT(connection_pool, j)) - { - sock_broken = check_socket_status(CONNECTION(connection_pool, j)->fd); - if (sock_broken < 0) - break; - } - else - { - sock_broken = -1; - break; - } - } - - if (sock_broken < 0) - { - ereport(LOG, - (errmsg("connection closed."), - errdetail("retry to create new connection pool"))); - /* - * It is possible that one of backend just broke. sleep 1 - * second to wait for failover occurres, then wait for the - * failover finishes. - */ - sleep(1); - wait_for_failover_to_finish(); - - for (j = 0; j < NUM_BACKENDS; j++) - { - if (!VALID_BACKEND(j) || (CONNECTION_SLOT(connection_pool, j) == NULL)) - continue; - - if (!freed) - { - pool_free_startup_packet(CONNECTION_SLOT(connection_pool, j)->sp); - CONNECTION_SLOT(connection_pool, j)->sp = NULL; - - freed = 1; - } - - pool_close(CONNECTION(connection_pool, j)); - pfree(CONNECTION_SLOT(connection_pool, j)); - } - info = connection_pool->info; - memset(connection_pool, 0, sizeof(POOL_CONNECTION_POOL)); - connection_pool->info = info; - info->swallow_termination = 0; - memset(connection_pool->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); - POOL_SETMASK(&oldmask); - return NULL; - } - } - POOL_SETMASK(&oldmask); - pool_index = i; - return connection_pool; - } - connection_pool++; - } - - POOL_SETMASK(&oldmask); - return NULL; -} - -/* - * 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); - ConnectionInfo *info; - int i, - freed = 0; - - if (p == NULL) - { - ereport(LOG, - (errmsg("cannot get connection pool for user: \"%s\" database: \"%s\", while discarding connection pool", user, database))); - return; - } - - 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; - } - CONNECTION_SLOT(p, i)->sp = NULL; - pool_close(CONNECTION(p, i)); - pfree(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); -} - - -/* -* 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; - int main_node_id; - - POOL_CONNECTION_POOL *p = pool_connection_pool; - - /* if no connection pool exists we have no reason to live */ - if (p == NULL) - ereport(ERROR, - (return_code(2), - errmsg("unable to create connection"), - errdetail("connection pool is not initialized"))); - - for (i = 0; i < pool_config->max_pool; i++) - { - if (in_use_backend_id(p) < 0) /* is this connection pool out of use? */ - { - ret = new_connection(p); - if (ret) - pool_index = i; - return ret; - } - p++; - } - ereport(DEBUG1, - (errmsg("creating connection pool"), - errdetail("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 = TMINTMAX; - pool_index = 0; - - for (i = 0; i < pool_config->max_pool; i++) - { - main_node_id = in_use_backend_id(p); - if (main_node_id < 0) - elog(ERROR, "no in use backend found"); /* this should not happen */ - - ereport(DEBUG1, - (errmsg("creating connection pool"), - errdetail("user: %s database: %s closetime: %ld", - CONNECTION_SLOT(p, main_node_id)->sp->user, - CONNECTION_SLOT(p, main_node_id)->sp->database, - CONNECTION_SLOT(p, main_node_id)->closetime))); - - if (CONNECTION_SLOT(p, main_node_id)->closetime < closetime) - { - closetime = CONNECTION_SLOT(p, main_node_id)->closetime; - oldestp = p; - pool_index = i; - } - p++; - } - - p = oldestp; - main_node_id = in_use_backend_id(p); - if (main_node_id < 0) - elog(ERROR, "no in use backend found"); /* this should not happen */ - pool_send_frontend_exits(p); - - ereport(DEBUG1, - (errmsg("creating connection pool"), - errdetail("discarding old %zd th connection. user: %s database: %s", - oldestp - pool_connection_pool, - CONNECTION_SLOT(p, main_node_id)->sp->user, - CONNECTION_SLOT(p, main_node_id)->sp->database))); - - for (i = 0; i < NUM_BACKENDS; i++) - { - if (CONNECTION_SLOT(p, i) == NULL) - continue; - - if (!freed) - { - pool_free_startup_packet(CONNECTION_SLOT(p, i)->sp); - CONNECTION_SLOT(p, i)->sp = NULL; - - freed = 1; - } - - pool_close(CONNECTION(p, i)); - pfree(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; -} - /* * set backend connection close timer */ void -pool_connection_pool_timer(POOL_CONNECTION_POOL * backend) +pool_connection_pool_timer(BackendClusterConnection * backend) { - POOL_CONNECTION_POOL *p = pool_connection_pool; + #ifdef NOT_USED + BackendClusterConnection *p = pool_connection_pool; int i; ereport(DEBUG1, @@ -395,6 +116,7 @@ pool_connection_pool_timer(POOL_CONNECTION_POOL * backend) errdetail("setting alarm after %d seconds", pool_config->connection_life_time))); pool_alarm(pool_backend_timer_handler, pool_config->connection_life_time); + #endif } /* @@ -409,7 +131,9 @@ pool_backend_timer_handler(int sig) void pool_backend_timer(void) { - POOL_CONNECTION_POOL *p = pool_connection_pool; +#define TMINTMAX 0x7fffffff +#ifdef UN_USED + BackendClusterConnection *p = pool_connection_pool; int i, j; time_t now; @@ -466,7 +190,7 @@ pool_backend_timer(void) pfree(CONNECTION_SLOT(p, j)); } info = p->info; - memset(p, 0, sizeof(POOL_CONNECTION_POOL)); + memset(p, 0, sizeof(BackendClusterConnection)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); } @@ -487,8 +211,9 @@ pool_backend_timer(void) nearest = 1; pool_alarm(pool_backend_timer_handler, nearest); } - update_pooled_connection_count(); + UpdatePooledConnectionCount(); POOL_SETMASK(&UnBlockSig); +#endif } /* @@ -848,212 +573,6 @@ connect_inet_domain_socket_by_port(char *host, int port, bool retry) return -1; } -/* - * create connection pool - */ -static POOL_CONNECTION_POOL_SLOT * create_cp(POOL_CONNECTION_POOL_SLOT * cp, int slot) -{ - BackendInfo *b = &pool_config->backend_desc->backend_info[slot]; - int fd; - - if (*b->backend_hostname == '/') - { - fd = connect_unix_domain_socket(slot, TRUE); - } - else - { - fd = connect_inet_domain_socket(slot, TRUE); - } - - if (fd < 0) - return NULL; - - cp->sp = NULL; - cp->con = pool_open(fd, true); - cp->closetime = 0; - return cp; -} - -/* - * Create actual connections to backends. - * New connection resides in TopMemoryContext. - */ -static POOL_CONNECTION_POOL * new_connection(POOL_CONNECTION_POOL * p) -{ - POOL_CONNECTION_POOL_SLOT *s; - int active_backend_count = 0; - int i; - bool status_changed = false; - volatile BACKEND_STATUS status; - - MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); - - for (i = 0; i < NUM_BACKENDS; i++) - { - ereport(DEBUG1, - (errmsg("creating new connection to backend"), - errdetail("connecting %d backend", i))); - - if (!VALID_BACKEND(i)) - { - ereport(DEBUG1, - (errmsg("creating new connection to backend"), - errdetail("skipping backend slot %d because backend_status = %d", - i, BACKEND_INFO(i).backend_status))); - continue; - } - - /* - * Make sure that the global backend status in the shared memory - * agrees the local status checked by VALID_BACKEND. It is possible - * that the local status is up, while the global status has been - * changed to down by failover. - */ - status = BACKEND_INFO(i).backend_status; - if (status != CON_UP && status != CON_CONNECT_WAIT) - { - ereport(DEBUG1, - (errmsg("creating new connection to backend"), - errdetail("skipping backend slot %d because global backend_status = %d", - i, BACKEND_INFO(i).backend_status))); - - /* sync local status with global status */ - *(my_backend_status[i]) = status; - my_main_node_id = Req_info->main_node_id; - continue; - } - - s = palloc(sizeof(POOL_CONNECTION_POOL_SLOT)); - - if (create_cp(s, i) == NULL) - { - pfree(s); - - /* - * If failover_on_backend_error is true, do failover. Otherwise, - * just exit this session or skip next health node. - */ - if (pool_config->failover_on_backend_error) - { - notice_backend_error(i, REQ_DETAIL_SWITCHOVER); - ereport(FATAL, - (errmsg("failed to create a backend connection"), - errdetail("executing failover on backend"))); - } - else - { - /* - * If we are in streaming replication mode and the node is a - * standby node, then we skip this node to avoid fail over. - */ - if (SL_MODE && !IS_PRIMARY_NODE_ID(i)) - { - ereport(LOG, - (errmsg("failed to create a backend %d connection", i), - errdetail("skip this backend because because failover_on_backend_error is off and we are in streaming replication mode and node is standby node"))); - - /* set down status to local status area */ - *(my_backend_status[i]) = CON_DOWN; - - /* if main_node_id is not updated, then update it */ - if (Req_info->main_node_id == i) - { - int old_main = Req_info->main_node_id; - - Req_info->main_node_id = get_next_main_node(); - - ereport(LOG, - (errmsg("main node %d is down. Update main node to %d", - old_main, Req_info->main_node_id))); - } - - /* - * make sure that we need to restart the process after - * finishing this session - */ - pool_get_my_process_info()->need_to_restart = 1; - continue; - } - else - { - ereport(FATAL, - (errmsg("failed to create a backend %d connection", i), - errdetail("not executing failover because failover_on_backend_error is off"))); - } - } - child_exit(POOL_EXIT_AND_RESTART); - } - else - { - p->info[i].create_time = time(NULL); - p->info[i].client_idle_duration = 0; - p->slots[i] = s; - - pool_init_params(&s->con->params); - - if (BACKEND_INFO(i).backend_status != CON_UP) - { - BACKEND_INFO(i).backend_status = CON_UP; - pool_set_backend_status_changed_time(i); - status_changed = true; - } - active_backend_count++; - } - } - - if (status_changed) - (void) write_status_file(); - - MemoryContextSwitchTo(oldContext); - - if (active_backend_count > 0) - { - return p; - } - - return NULL; -} - -/* check_socket_status() - * RETURN: 0 => OK - * -1 => broken socket. - */ -static int -check_socket_status(int fd) -{ - fd_set rfds; - int result; - struct timeval t; - - for (;;) - { - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - - t.tv_sec = t.tv_usec = 0; - - result = select(fd + 1, &rfds, NULL, NULL, &t); - if (result < 0 && errno == EINTR) - { - continue; - } - else - { - return (result == 0 ? 0 : -1); - } - } - - return -1; -} - -/* - * Return current used index (i.e. frontend connected) - */ -int -pool_pool_index(void) -{ - return pool_index; -} /* * send frontend exiting messages to all connections. this is called @@ -1064,8 +583,9 @@ pool_pool_index(void) void close_all_backend_connections(void) { +#ifdef NOT_USED int i; - POOL_CONNECTION_POOL *p = pool_connection_pool; + BackendClusterConnection *p = pool_connection_pool; pool_sigset_t oldmask; @@ -1083,40 +603,81 @@ close_all_backend_connections(void) } POOL_SETMASK(&oldmask); +#endif } -/* - * Return number of established connections in the connection pool. - * This is called when a client disconnects to pgpool. - */ -void -update_pooled_connection_count(void) -{ - int i; - int count = 0; - POOL_CONNECTION_POOL *p = pool_connection_pool; - for (i = 0; i < pool_config->max_pool; i++, p++) - { - if (MAIN_CONNECTION(p)) - count++; - } - pool_get_my_process_info()->pooled_connections = count; -} /* * Return the first node id in use. * If no node is in use, return -1. */ int -in_use_backend_id(POOL_CONNECTION_POOL *pool) +in_use_backend_id(BackendClusterConnection *pool) { int i; for (i = 0; i < NUM_BACKENDS; i++) { - if (pool->slots[i]) + if (pool->slots[i].con) return i; } return -1; } + + +/* Handles the failover/failback event in child process */ +// void +// HandleNodeChangeEventForLeasedConnection(void) +// { +// ConnectionPoolEntry* pool_entry = GetChildConnectionPoolEntry(); +// if (pool_entry == NULL) +// return; + +// if (pool_entry->endPoint.node_status_changed == NODE_STATUS_SYNC) +// return; + +// if (pool_entry->endPoint.node_status_changed | NODE_STATUS_NODE_PRIMARY_CHANGED) +// { +// /* See if we have a different primary node */ +// if (Req_info->primary_node_id != pool_entry->endPoint.primary_node_id) +// { +// ereport(LOG, +// (errmsg("Primary node changed from %d to %d", pool_entry->endPoint.primary_node_id, Req_info->primary_node_id))); +// /* What to do here ?? TODO */ +// } +// } +// else if (pool_entry->endPoint.node_status_changed | NODE_STATUS_NODE_REMOVED) +// { +// int i; +// for (i = 0; i < NUM_BACKENDS; i++) +// { +// if ((pool_entry->endPoint.backend_status[i] == CON_UP) && +// (BACKEND_INFO(i).backend_status == CON_DOWN || BACKEND_INFO(i).backend_status == CON_UNUSED)) +// { +// ereport(LOG, +// (errmsg("Backend node %d was failed", i))); +// pool_entry->endPoint.backend_status[i] = BACKEND_INFO(i).backend_status; +// /* Verify if the failed node was load balancing node */ +// if (i == pool_entry->endPoint.load_balancing_node) +// { +// /* if we were idle. Just select a new load balancing node and continue */ +// pool_select_new_load_balance_node(true); +// } +// if (i == pool_entry->endPoint.primary_node_id) +// { +// /* Primary node failed */ + +// } +// } +// } + +// if (pool_entry->endPoint.load_balancing_node >= 0) +// { +// if (pool_entry->endPoint.backend_status[pool_entry->endPoint.load_balancing_node]) +// ereport(LOG, +// (errmsg("Node %d removed from load balancing", pool_entry->endPoint.load_balancing_node))); +// /* What to do here ?? TDOD */ +// } +// } +// } \ No newline at end of file diff --git a/src/protocol/pool_pg_utils.c b/src/protocol/pool_pg_utils.c index 25942b6c..1fd569ab 100644 --- a/src/protocol/pool_pg_utils.c +++ b/src/protocol/pool_pg_utils.c @@ -40,18 +40,18 @@ #include "pool_config_variables.h" static int choose_db_node_id(char *str); -static void free_persistent_db_connection_memory(POOL_CONNECTION_POOL_SLOT * cp); +static void free_startup_packet(StartupPacket* sp); static void si_enter_critical_region(void); static void si_leave_critical_region(void); /* * create a persistent connection */ -POOL_CONNECTION_POOL_SLOT * +BackendNodeConnection * make_persistent_db_connection( - int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry) + int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry) { - POOL_CONNECTION_POOL_SLOT *cp; + BackendNodeConnection *cp; int fd; #define MAX_USER_AND_DATABASE 1024 @@ -66,8 +66,9 @@ make_persistent_db_connection( static StartupPacket_v3 * startup_packet; int len, len1; + StartupPacket *sp; - cp = palloc0(sizeof(POOL_CONNECTION_POOL_SLOT)); + cp = palloc0(sizeof(BackendNodeConnection)); startup_packet = palloc0(sizeof(*startup_packet)); startup_packet->protoVersion = htonl(0x00030000); /* set V3 proto * major/minor */ @@ -86,8 +87,8 @@ make_persistent_db_connection( if (fd < 0) { - free_persistent_db_connection_memory(cp); pfree(startup_packet); + pfree(cp); ereport(ERROR, (errmsg("failed to make persistent db connection"), errdetail("connection to host:\"%s:%d\" failed", hostname, port))); @@ -107,8 +108,8 @@ make_persistent_db_connection( len1 = snprintf(&startup_packet->data[len], sizeof(startup_packet->data) - len, "%s", user) + 1; if (len1 >= (sizeof(startup_packet->data) - len)) { - pool_close(cp->con); - free_persistent_db_connection_memory(cp); + pool_close(cp->con, true); + pfree(cp); pfree(startup_packet); ereport(ERROR, (errmsg("failed to make persistent db connection"), @@ -119,8 +120,8 @@ make_persistent_db_connection( len1 = snprintf(&startup_packet->data[len], sizeof(startup_packet->data) - len, "database") + 1; if (len1 >= (sizeof(startup_packet->data) - len)) { - pool_close(cp->con); - free_persistent_db_connection_memory(cp); + pool_close(cp->con,true); + pfree(cp); pfree(startup_packet); ereport(ERROR, (errmsg("failed to make persistent db connection"), @@ -131,8 +132,8 @@ make_persistent_db_connection( len1 = snprintf(&startup_packet->data[len], sizeof(startup_packet->data) - len, "%s", dbname) + 1; if (len1 >= (sizeof(startup_packet->data) - len)) { - pool_close(cp->con); - free_persistent_db_connection_memory(cp); + pool_close(cp->con, true); + pfree(cp); pfree(startup_packet); ereport(ERROR, (errmsg("failed to make persistent db connection"), @@ -141,31 +142,33 @@ make_persistent_db_connection( len += len1; startup_packet->data[len++] = '\0'; - cp->sp = palloc(sizeof(StartupPacket)); + sp = palloc(sizeof(StartupPacket)); - cp->sp->startup_packet = (char *) startup_packet; - cp->sp->len = len + 4; - cp->sp->major = 3; - cp->sp->minor = 0; - cp->sp->database = pstrdup(dbname); - cp->sp->user = pstrdup(user); + sp->startup_packet = (char *) startup_packet; + sp->len = len + 4; + sp->major = 3; + sp->minor = 0; + sp->database = pstrdup(dbname); + sp->user = pstrdup(user); /* * send startup packet */ PG_TRY(); { - send_startup_packet(cp); - connection_do_auth(cp, password); + send_startup_packet(cp, sp); + connection_do_auth(NULL,-1, cp,NULL, password, sp); } PG_CATCH(); { - pool_close(cp->con); - free_persistent_db_connection_memory(cp); + pool_close(cp->con,true); + pfree(cp); + free_startup_packet(sp); PG_RE_THROW(); } PG_END_TRY(); + free_startup_packet(sp); return cp; } @@ -173,11 +176,11 @@ make_persistent_db_connection( * make_persistent_db_connection_noerror() is a wrapper over * make_persistent_db_connection() which does not ereports in case of an error */ -POOL_CONNECTION_POOL_SLOT * +BackendNodeConnection * make_persistent_db_connection_noerror( - int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry) + int db_node_id, char *hostname, int port, char *dbname, char *user, char *password, bool retry) { - POOL_CONNECTION_POOL_SLOT *slot = NULL; + BackendNodeConnection *slot = NULL; MemoryContext oldContext = CurrentMemoryContext; PG_TRY(); @@ -214,35 +217,25 @@ make_persistent_db_connection_noerror( } /* - * Free memory of POOL_CONNECTION_POOL_SLOT. Should only be used in - * make_persistent_db_connection and discard_persistent_db_connection. + * Free memory of StartupPacket. */ static void -free_persistent_db_connection_memory(POOL_CONNECTION_POOL_SLOT * cp) +free_startup_packet(StartupPacket* sp) { - if (!cp) - return; - if (!cp->sp) - { - pfree(cp); - return; - } - if (cp->sp->startup_packet) - pfree(cp->sp->startup_packet); - if (cp->sp->database) - pfree(cp->sp->database); - if (cp->sp->user) - pfree(cp->sp->user); - pfree(cp->sp); - pfree(cp); + if (sp->startup_packet) + pfree(sp->startup_packet); + if (sp->database) + pfree(sp->database); + if (sp->user) + pfree(sp->user); + pfree(sp); } /* * Discard connection and memory allocated by * make_persistent_db_connection(). */ -void -discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT * cp) +void discard_persistent_db_connection(BackendNodeConnection *cp) { int len; @@ -263,21 +256,20 @@ discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT * cp) pool_flush_it(cp->con); socket_unset_nonblock(cp->con->fd); - pool_close(cp->con); - free_persistent_db_connection_memory(cp); + pool_close(cp->con, true); + pfree(cp); } /* * send startup packet */ -void -send_startup_packet(POOL_CONNECTION_POOL_SLOT * cp) +void send_startup_packet(BackendNodeConnection *cp, StartupPacket *sp) { int len; - len = htonl(cp->sp->len + sizeof(len)); + len = htonl(sp->len + sizeof(len)); pool_write(cp->con, &len, sizeof(len)); - pool_write_and_flush(cp->con, cp->sp->startup_packet, cp->sp->len); + pool_write_and_flush(cp->con, sp->startup_packet, sp->len); } void @@ -335,7 +327,7 @@ select_load_balancing_node(void) */ if (SL_MODE && pool_config->redirect_usernames) { - char *user = MAIN_CONNECTION(ses->backend)->sp->user; + char *user = ses->backend->sp->user; /* * Check to see if the user matches any of @@ -362,7 +354,7 @@ select_load_balancing_node(void) */ if (SL_MODE && pool_config->redirect_dbnames) { - char *database = MAIN_CONNECTION(ses->backend)->sp->database; + char *database = ses->backend->sp->database; /* * Check to see if the database matches any of @@ -396,7 +388,7 @@ select_load_balancing_node(void) */ if (SL_MODE && pool_config->redirect_app_names) { - char *app_name = MAIN_CONNECTION(ses->backend)->sp->application_name; + char *app_name = ses->backend->sp->application_name; /* * Check only if application name is set. Old applications may not @@ -687,7 +679,7 @@ select_load_balancing_node(void) * */ PGVersion * -Pgversion(POOL_CONNECTION_POOL * backend) +Pgversion(BackendClusterConnection * backend) { #define VERSION_BUF_SIZE 10 static PGVersion pgversion; diff --git a/src/protocol/pool_process_query.c b/src/protocol/pool_process_query.c index 2eb877f9..b2d1bb6a 100644 --- a/src/protocol/pool_process_query.c +++ b/src/protocol/pool_process_query.c @@ -80,20 +80,20 @@ #define IDLE_IN_TRANSACTION_SESSION_TIMEOUT_ERROR_CODE "25P03" #define IDLE_SESSION_TIMEOUT_ERROR_CODE "57P05" -static int reset_backend(POOL_CONNECTION_POOL * backend, int qcnt); +static int reset_backend(BackendClusterConnection * backend, int qcnt); static char *get_insert_command_table_name(InsertStmt *node); -static bool is_cache_empty(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +static bool is_cache_empty(POOL_CONNECTION * frontend, BackendClusterConnection * backend); static bool is_panic_or_fatal_error(char *message, int major); static int extract_message(POOL_CONNECTION * backend, char *error_code, int major, char class, bool unread); static int detect_postmaster_down_error(POOL_CONNECTION * backend, int major); static bool is_internal_transaction_needed(Node *node); static bool pool_has_insert_lock(void); -static POOL_STATUS add_lock_target(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *table); -static bool has_lock_target(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *table, bool for_update); -static POOL_STATUS insert_oid_into_insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *table); -static POOL_STATUS read_packets_and_process(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int reset_request, int *state, short *num_fields, bool *cont); +static POOL_STATUS add_lock_target(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *table); +static bool has_lock_target(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *table, bool for_update); +static POOL_STATUS insert_oid_into_insert_lock(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *table); +static POOL_STATUS read_packets_and_process(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int reset_request, int *state, short *num_fields, bool *cont); static bool is_all_standbys_command_complete(unsigned char *kind_list, int num_backends, int main_node); -static bool pool_process_notice_message_from_one_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int backend_idx, char kind); +static bool pool_process_notice_message_from_one_backend(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int backend_idx, char kind); /* * Main module for query processing @@ -101,7 +101,7 @@ static bool pool_process_notice_message_from_one_backend(POOL_CONNECTION * front */ POOL_STATUS pool_process_query(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int reset_request) { short num_fields = 0; /* the number of fields in a row (V2 protocol) */ @@ -245,7 +245,7 @@ pool_process_query(POOL_CONNECTION * frontend, bool cont = true; status = read_packets_and_process(frontend, backend, reset_request, &state, &num_fields, &cont); - backend->info->client_idle_duration = 0; +//TODO backend->info->client_idle_duration = 0; if (status != POOL_CONTINUE) return status; else if (!cont) /* Detected admin shutdown */ @@ -297,7 +297,7 @@ pool_process_query(POOL_CONNECTION * frontend, bool cont = true; status = read_packets_and_process(frontend, backend, reset_request, &state, &num_fields, &cont); - backend->info->client_idle_duration = 0; +//TODO backend->info->client_idle_duration = 0; if (status != POOL_CONTINUE) return status; else if (!cont) /* Detected admin shutdown */ @@ -615,7 +615,7 @@ wait_for_query_response(POOL_CONNECTION * frontend, POOL_CONNECTION * backend, i * Extended query protocol has to send Flush message. */ POOL_STATUS -send_extended_protocol_message(POOL_CONNECTION_POOL * backend, +send_extended_protocol_message(BackendClusterConnection * backend, int node_id, char *kind, int len, char *string) { @@ -661,7 +661,7 @@ synchronize(POOL_CONNECTION * cp) * valid backends might be changed by failover/failback. */ void -pool_send_frontend_exits(POOL_CONNECTION_POOL * backend) +pool_send_frontend_exits(BackendClusterConnection * backend) { int len; int i; @@ -672,7 +672,7 @@ pool_send_frontend_exits(POOL_CONNECTION_POOL * backend) * send a terminate message to backend if there's an existing * connection */ - if (VALID_BACKEND(i) && CONNECTION_SLOT(backend, i)) + if (VALID_BACKEND(i) && &CONNECTION_SLOT(backend, i)) { pool_write_noerror(CONNECTION(backend, i), "X", 1); @@ -704,7 +704,7 @@ pool_send_frontend_exits(POOL_CONNECTION_POOL * backend) POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { int len, len1 = 0; @@ -835,7 +835,7 @@ SimpleForwardToFrontend(char kind, POOL_CONNECTION * frontend, POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int len, char *contents) { int sendlen; @@ -895,7 +895,7 @@ SimpleForwardToBackend(char kind, POOL_CONNECTION * frontend, * Handle parameter status message */ POOL_STATUS -ParameterStatus(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +ParameterStatus(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { int len, len1 = 0; @@ -957,11 +957,11 @@ ParameterStatus(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) parambuf = palloc(len); memcpy(parambuf, p, len); - pool_add_param(&CONNECTION(backend, i)->params, name, value); + pool_add_param(&backend->backend_end_point->params, name, value); if (!strcmp("application_name", name)) { - set_application_name_with_string(pool_find_name(&CONNECTION(backend, i)->params, name, &pos)); + set_application_name_with_string(pool_find_name(&backend->backend_end_point->params, name, &pos)); } } else @@ -976,7 +976,7 @@ ParameterStatus(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) } #ifdef DEBUG - pool_param_debug_print(&MAIN(backend)->params); + pool_param_debug_print(&backend->backend_end_point->params); #endif } } @@ -1020,7 +1020,7 @@ reset_connection(void) * 0: no query was issued 1: a query was issued 2: no more queries remain -1: error */ static int -reset_backend(POOL_CONNECTION_POOL * backend, int qcnt) +reset_backend(BackendClusterConnection * backend, int qcnt) { char *query; int qn; @@ -1735,7 +1735,7 @@ do_error_command(POOL_CONNECTION * backend, int major) * than main node to ket them go into abort status. */ void -do_error_execute_command(POOL_CONNECTION_POOL * backend, int node_id, int major) +do_error_execute_command(BackendClusterConnection * backend, int node_id, int major) { char kind; char *string; @@ -2481,7 +2481,7 @@ do_query(POOL_CONNECTION * backend, char *query, POOL_SELECT_RESULT * *result, i * 3: row lock against insert_lock table is required */ int -need_insert_lock(POOL_CONNECTION_POOL * backend, char *query, Node *node) +need_insert_lock(BackendClusterConnection * backend, char *query, Node *node) { /* * Query to know if the target table has SERIAL column or not. @@ -2601,7 +2601,7 @@ need_insert_lock(POOL_CONNECTION_POOL * backend, char *query, Node *node) * [ADMIN] 'SGT DETAIL: Could not open file "pg_clog/05DC": ... */ POOL_STATUS -insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *query, InsertStmt *node, int lock_kind) +insert_lock(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *query, InsertStmt *node, int lock_kind) { char *table; int len = 0; @@ -2750,8 +2750,8 @@ insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *qu } else { - status = do_command(frontend, MAIN(backend), qbuf, MAJOR(backend), MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key, 0); + status = do_command(frontend, MAIN(backend), qbuf, MAJOR(backend), MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key, 0); } } else if (lock_kind == 2) @@ -2808,8 +2808,8 @@ insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *qu } else { - status = do_command(frontend, MAIN(backend), qbuf, MAJOR(backend), MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key, 0); + status = do_command(frontend, MAIN(backend), qbuf, MAJOR(backend), MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key, 0); } } } @@ -2827,7 +2827,7 @@ insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *qu { if (deadlock_detected) status = do_command(frontend, CONNECTION(backend, i), POOL_ERROR_QUERY, PROTO_MAJOR_V3, - MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 0); + MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 0); else { if (lock_kind == 1) @@ -2842,7 +2842,7 @@ insert_lock(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *qu else { status = do_command(frontend, CONNECTION(backend, i), qbuf, PROTO_MAJOR_V3, - MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 0); + MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 0); } } else if (lock_kind == 2) @@ -2876,7 +2876,7 @@ pool_has_insert_lock(void) bool result; static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; backend = pool_get_session_context(false)->backend; @@ -2903,7 +2903,7 @@ pool_has_insert_lock(void) * Return POOL_CONTINUE if the row is inserted successfully * or the row already exists, the others return POOL_ERROR. */ -static POOL_STATUS add_lock_target(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *table) +static POOL_STATUS add_lock_target(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *table) { /* * lock the row where reloid is 0 to avoid "duplicate key violates..." @@ -2917,7 +2917,7 @@ static POOL_STATUS add_lock_target(POOL_CONNECTION * frontend, POOL_CONNECTION_P per_node_statement_log(backend, MAIN_NODE_ID, "LOCK TABLE pgpool_catalog.insert_lock IN SHARE ROW EXCLUSIVE MODE"); if (do_command(frontend, MAIN(backend), "LOCK TABLE pgpool_catalog.insert_lock IN SHARE ROW EXCLUSIVE MODE", - PROTO_MAJOR_V3, MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 0) != POOL_CONTINUE) + PROTO_MAJOR_V3, MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 0) != POOL_CONTINUE) ereport(ERROR, (errmsg("unable to add lock target"), errdetail("do_command returned DEADLOCK status"))); @@ -2964,7 +2964,7 @@ static POOL_STATUS add_lock_target(POOL_CONNECTION * frontend, POOL_CONNECTION_P */ static bool has_lock_target(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, char *table, bool lock) { char *suffix; @@ -3005,7 +3005,7 @@ has_lock_target(POOL_CONNECTION * frontend, * Insert the oid of the specified table into insert_lock table. */ static POOL_STATUS insert_oid_into_insert_lock(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, char *table) { char qbuf[QUERY_STRING_BUFFER_LEN]; @@ -3027,7 +3027,7 @@ static POOL_STATUS insert_oid_into_insert_lock(POOL_CONNECTION * frontend, per_node_statement_log(backend, MAIN_NODE_ID, qbuf); status = do_command(frontend, MAIN(backend), qbuf, PROTO_MAJOR_V3, - MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 0); + MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 0); return status; } @@ -3071,7 +3071,7 @@ is_drop_database(Node *node) * check if any pending data remains in backend. */ bool -is_backend_cache_empty(POOL_CONNECTION_POOL * backend) +is_backend_cache_empty(BackendClusterConnection * backend) { int i; @@ -3098,7 +3098,7 @@ is_backend_cache_empty(POOL_CONNECTION_POOL * backend) * check if any pending data remains. */ static bool -is_cache_empty(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +is_cache_empty(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { /* Are we suspending reading from frontend? */ if (!pool_is_suspend_reading_from_frontend()) @@ -3206,7 +3206,7 @@ check_copy_from_stdin(Node *node) * read kind from one backend */ void -read_kind_from_one_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *kind, int node) +read_kind_from_one_backend(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *kind, int node) { if (VALID_BACKEND(node)) { @@ -3253,7 +3253,7 @@ is_all_standbys_command_complete(unsigned char *kind_list, int num_backends, int * this function uses "decide by majority" method if kinds from all backends do not agree. */ void -read_kind_from_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, char *decided_kind) +read_kind_from_backend(POOL_CONNECTION * frontend, BackendClusterConnection * backend, char *decided_kind) { int i; unsigned char kind_list[MAX_NUM_BACKENDS]; /* records each backend's kind */ @@ -3446,11 +3446,11 @@ read_kind_from_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backen if (IS_MAIN_NODE_ID(i)) { int pos; - pool_add_param(&CONNECTION(backend, i)->params, p, value); + pool_add_param(&backend->backend_end_point->params, p, value); if (!strcmp("application_name", p)) { - set_application_name_with_string(pool_find_name(&CONNECTION(backend, i)->params, p, &pos)); + set_application_name_with_string(pool_find_name(&backend->backend_end_point->params, p, &pos)); } } /* forward to frontend */ @@ -3924,7 +3924,7 @@ parse_copy_data(char *buf, int len, char delimiter, int col_id) } void -query_ps_status(char *query, POOL_CONNECTION_POOL * backend) +query_ps_status(char *query, BackendClusterConnection * backend) { StartupPacket *sp; char psbuf[1024]; @@ -3933,7 +3933,7 @@ query_ps_status(char *query, POOL_CONNECTION_POOL * backend) if (*query == '\0') return; - sp = MAIN_CONNECTION(backend)->sp; + sp = backend->sp; if (sp) i = snprintf(psbuf, sizeof(psbuf) - 1, "%s %s %s ", sp->user, sp->database, remote_ps_data); @@ -4104,7 +4104,7 @@ is_internal_transaction_needed(Node *node) * Start an internal transaction if necessary. */ POOL_STATUS -start_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, Node *node) +start_internal_transaction(POOL_CONNECTION * frontend, BackendClusterConnection * backend, Node *node) { int i; @@ -4115,14 +4115,14 @@ start_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * ba { for (i = 0; i < NUM_BACKENDS; i++) { - if (VALID_BACKEND_RAW(i) && CONNECTION_SLOT(backend, i) && + if (VALID_BACKEND_RAW(i) && &CONNECTION_SLOT(backend, i) && !INTERNAL_TRANSACTION_STARTED(backend, i) && TSTATE(backend, i) == 'I') { per_node_statement_log(backend, i, "BEGIN"); if (do_command(frontend, CONNECTION(backend, i), "BEGIN", MAJOR(backend), - MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 0) != POOL_CONTINUE) + MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 0) != POOL_CONTINUE) ereport(ERROR, (errmsg("unable to start the internal transaction"), errdetail("do_command returned DEADLOCK status"))); @@ -4147,7 +4147,7 @@ start_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * ba * that satisfy VALID_BACKEND macro. */ POOL_STATUS -end_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +end_internal_transaction(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { int i; int len; @@ -4187,7 +4187,7 @@ end_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * back PG_TRY(); { if (do_command(frontend, CONNECTION(backend, i), "COMMIT", MAJOR(backend), - MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 1) != POOL_CONTINUE) + MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 1) != POOL_CONTINUE) { ereport(ERROR, (errmsg("unable to COMMIT the transaction"), @@ -4240,7 +4240,7 @@ end_internal_transaction(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * back PG_TRY(); { if (do_command(frontend, MAIN(backend), "COMMIT", MAJOR(backend), - MAIN_CONNECTION(backend)->pid, MAIN_CONNECTION(backend)->key, 1) != POOL_CONTINUE) + MAIN_CONNECTION(backend).pid, MAIN_CONNECTION(backend).key, 1) != POOL_CONTINUE) { ereport(ERROR, (errmsg("unable to COMMIT the transaction"), @@ -4540,7 +4540,7 @@ extract_message(POOL_CONNECTION * backend, char *error_code, int major, char cla */ static bool -pool_process_notice_message_from_one_backend(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int backend_idx, char kind) +pool_process_notice_message_from_one_backend(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int backend_idx, char kind) { int major = MAJOR(backend); POOL_CONNECTION *backend_conn = CONNECTION(backend, backend_idx); @@ -4737,7 +4737,7 @@ pool_extract_error_message(bool read_kind, POOL_CONNECTION * backend, int major, * read message kind and rest of the packet then discard it */ POOL_STATUS -pool_discard_packet(POOL_CONNECTION_POOL * cp) +pool_discard_packet(BackendClusterConnection * cp) { int i; char kind; @@ -4765,7 +4765,7 @@ pool_discard_packet(POOL_CONNECTION_POOL * cp) * read message length and rest of the packet then discard it */ POOL_STATUS -pool_discard_packet_contents(POOL_CONNECTION_POOL * cp) +pool_discard_packet_contents(BackendClusterConnection * cp) { int len, i; @@ -4807,7 +4807,7 @@ pool_discard_packet_contents(POOL_CONNECTION_POOL * cp) /* * Read packet from either frontend or backend and process it. */ -static POOL_STATUS read_packets_and_process(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int reset_request, int *state, short *num_fields, bool *cont) +static POOL_STATUS read_packets_and_process(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int reset_request, int *state, short *num_fields, bool *cont) { fd_set readmask; fd_set writemask; @@ -4848,20 +4848,18 @@ SELECT_RETRY: * may return 0. */ if (pool_config->load_balance_mode && - BACKEND_INFO(backend->info->load_balancing_node).backend_status == CON_DOWN) + BACKEND_INFO(backend->backend_end_point->load_balancing_node).backend_status == CON_DOWN) { /* select load balancing node */ POOL_SESSION_CONTEXT *session_context; + BackendClusterConnection *child_con = GetBackendClusterConnection(); int node_id; session_context = pool_get_session_context(false); node_id = select_load_balancing_node(); - for (i = 0; i < NUM_BACKENDS; i++) - { - pool_coninfo(session_context->process_context->proc_id, - pool_pool_index(), i)->load_balancing_node = node_id; - } + if(child_con->backend_end_point) + child_con->backend_end_point->load_balancing_node = node_id; } for (i = 0; i < NUM_BACKENDS; i++) @@ -4903,7 +4901,7 @@ SELECT_RETRY: /* select timeout */ if (fds == 0) { - backend->info->client_idle_duration++; +//TODO backend->info->client_idle_duration++; if (*InRecovery == RECOVERY_INIT && pool_config->client_idle_limit > 0) { idle_count++; @@ -4950,7 +4948,7 @@ SELECT_RETRY: /* * make sure that connection slot exists */ - if (CONNECTION_SLOT(backend, i) == 0) + if (&CONNECTION_SLOT(backend, i) == 0) { ereport(LOG, (errmsg("error occurred while reading and processing packets"), @@ -5015,7 +5013,7 @@ SELECT_RETRY: * we do not need to trigger failover. */ if (SL_MODE && - (i == PRIMARY_NODE_ID || i == backend->info->load_balancing_node)) + (i == PRIMARY_NODE_ID || i == backend->backend_end_point->load_balancing_node)) { /* detach backend node. */ was_error = 1; @@ -5026,7 +5024,7 @@ SELECT_RETRY: * check if the pg_terminate_backend was issued on * this connection */ - if (CONNECTION(backend, i)->con_info->swallow_termination == 1) + if(backend->backend_end_point->conn_slots[i].swallow_termination) { ereport(FATAL, (errmsg("connection to postmaster on DB node %d was lost due to pg_terminate_backend", i), @@ -5049,7 +5047,7 @@ SELECT_RETRY: was_error = 1; if (!VALID_BACKEND(i)) break; - if (CONNECTION(backend, i)->con_info->swallow_termination == 1) + if (backend->backend_end_point->conn_slots[i].swallow_termination) { ereport(FATAL, (errmsg("connection to postmaster on DB node %d was lost due to pg_terminate_backend", i), @@ -5195,7 +5193,7 @@ pool_push_pending_data(POOL_CONNECTION * backend) { POOL_CONNECTION *con; - con = session_context->backend->slots[session_context->load_balance_node_id]->con; + con = session_context->backend->slots[session_context->load_balance_node_id].con; pool_write(con, "H", 1); len = htonl(sizeof(len)); pool_write_and_flush(con, &len, sizeof(len)); diff --git a/src/protocol/pool_proto2.c b/src/protocol/pool_proto2.c index ffca996c..5507a6bd 100644 --- a/src/protocol/pool_proto2.c +++ b/src/protocol/pool_proto2.c @@ -35,7 +35,7 @@ POOL_STATUS AsciiRow(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, short num_fields) { static char nullmap[8192], @@ -162,7 +162,7 @@ AsciiRow(POOL_CONNECTION * frontend, POOL_STATUS BinaryRow(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, short num_fields) { static char nullmap[8192], @@ -274,7 +274,7 @@ BinaryRow(POOL_CONNECTION * frontend, POOL_STATUS CompletedResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { int i; char *string = NULL; @@ -340,7 +340,7 @@ CompletedResponse(POOL_CONNECTION * frontend, POOL_STATUS CursorResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char *string = NULL; char *string1 = NULL; @@ -388,7 +388,7 @@ CursorResponse(POOL_CONNECTION * frontend, void EmptyQueryResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char c; int i; @@ -407,7 +407,7 @@ EmptyQueryResponse(POOL_CONNECTION * frontend, POOL_STATUS ErrorResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char *string = ""; int len = 0; @@ -449,7 +449,7 @@ ErrorResponse(POOL_CONNECTION * frontend, POOL_STATUS FunctionResultResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char dummy; int len; @@ -518,7 +518,7 @@ FunctionResultResponse(POOL_CONNECTION * frontend, void NoticeResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char *string = NULL; int len = 0; @@ -551,7 +551,7 @@ NoticeResponse(POOL_CONNECTION * frontend, POOL_STATUS NotificationResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { int pid, pid1; @@ -594,7 +594,7 @@ NotificationResponse(POOL_CONNECTION * frontend, int RowDescription(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, short *result) { short num_fields, diff --git a/src/protocol/pool_proto_modules.c b/src/protocol/pool_proto_modules.c index 7725df65..420d6c49 100644 --- a/src/protocol/pool_proto_modules.c +++ b/src/protocol/pool_proto_modules.c @@ -63,6 +63,7 @@ #include "main/pool_internal_comms.h" #include "pool_config_variables.h" #include "utils/psqlscan.h" +#include "connection_pool/connection_pool.h" char *copy_table = NULL; /* copy table name */ char *copy_schema = NULL; /* copy table name */ @@ -86,27 +87,27 @@ int is_select_for_update = 0; /* 1 if SELECT INTO or SELECT FOR */ char query_string_buffer[QUERY_STRING_BUFFER_LEN]; -static int check_errors(POOL_CONNECTION_POOL * backend, int backend_id); +static int check_errors(BackendClusterConnection * backend, int backend_id); static void generate_error_message(char *prefix, int specific_error, char *query); static POOL_STATUS parse_before_bind(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, POOL_SENT_MESSAGE * message, POOL_SENT_MESSAGE * bind_message); static POOL_STATUS send_prepare(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, POOL_SENT_MESSAGE * message); static int *find_victim_nodes(int *ntuples, int nmembers, int main_node, int *number_of_nodes); static POOL_STATUS close_standby_transactions(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); + BackendClusterConnection * backend); static char *flatten_set_variable_args(const char *name, List *args); static bool process_pg_terminate_backend_func(POOL_QUERY_CONTEXT * query_context); static void pool_discard_except_sync_and_ready_for_query(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend); -static void si_get_snapshot(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, Node *node, bool tstate_check); + BackendClusterConnection * backend); +static void si_get_snapshot(POOL_CONNECTION * frontend, BackendClusterConnection * backend, Node *node, bool tstate_check); -static bool check_transaction_state_and_abort(char *query, Node *node, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend); +static bool check_transaction_state_and_abort(char *query, Node *node, POOL_CONNECTION * frontend, BackendClusterConnection * backend); static bool multi_statement_query(char *buf); @@ -143,14 +144,13 @@ process_pg_terminate_backend_func(POOL_QUERY_CONTEXT * query_context) * locate pg_terminate_backend and get the pid argument, if * pg_terminate_backend is present in the query */ - int backend_pid = pool_get_terminate_backend_pid(query_context->parse_tree); + int backend_pid = pool_get_terminate_backend_pid(query_context->parse_tree); if (backend_pid > 0) { - int backend_node = 0; - ConnectionInfo *conn = pool_coninfo_backend_pid(backend_pid, &backend_node); - - if (conn == NULL) + int backend_node = -1; + PooledBackendNodeConnection *backend_connection = GetBackendNodeConnectionForBackendPID(backend_pid, &backend_node); + if (backend_connection == NULL) { ereport(LOG, (errmsg("found the pg_terminate_backend request for backend pid:%d, but the backend connection does not belong to pgpool-II", backend_pid))); @@ -162,17 +162,18 @@ process_pg_terminate_backend_func(POOL_QUERY_CONTEXT * query_context) return false; } ereport(LOG, - (errmsg("found the pg_terminate_backend request for backend pid:%d on backend node:%d", backend_pid, backend_node), + (errmsg("found the pg_terminate_backend request for backend pid:%d", backend_pid), errdetail("setting the connection flag"))); - pool_set_connection_will_be_terminated(conn); + pool_set_connection_will_be_terminated(backend_connection); /* * It was the pg_terminate_backend call so send the query to * appropriate backend */ - query_context->pg_terminate_backend_conn = conn; - pool_force_query_node_to_backend(query_context, backend_node); + query_context->pg_terminate_backend_conn = backend_connection; + if (backend_node >= 0) + pool_force_query_node_to_backend(query_context, backend_node); return true; } return false; @@ -185,7 +186,7 @@ process_pg_terminate_backend_func(POOL_QUERY_CONTEXT * query_context) */ POOL_STATUS SimpleQuery(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, int len, char *contents) + BackendClusterConnection * backend, int len, char *contents) { static char *sq_config = "pool_status"; static char *sq_pools = "pool_pools"; @@ -908,7 +909,7 @@ SimpleQuery(POOL_CONNECTION * frontend, * process EXECUTE (V3 only) */ POOL_STATUS -Execute(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +Execute(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents) { int commit = 0; @@ -1221,7 +1222,7 @@ Execute(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, * process Parse (V3 only) */ POOL_STATUS -Parse(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +Parse(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents) { int deadlock_detected = 0; @@ -1600,7 +1601,7 @@ Parse(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, } POOL_STATUS -Bind(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +Bind(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents) { char *pstmt_name; @@ -1762,7 +1763,7 @@ Bind(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, } POOL_STATUS -Describe(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +Describe(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents) { POOL_SENT_MESSAGE *msg; @@ -1855,7 +1856,7 @@ Describe(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, POOL_STATUS -Close(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +Close(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents) { POOL_SENT_MESSAGE *msg; @@ -1986,7 +1987,7 @@ Close(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, POOL_STATUS -FunctionCall3(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +FunctionCall3(POOL_CONNECTION * frontend, BackendClusterConnection * backend, int len, char *contents) { /* @@ -2018,7 +2019,7 @@ FunctionCall3(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, */ POOL_STATUS ReadyForQuery(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, bool send_ready, bool cache_commit) + BackendClusterConnection * backend, bool send_ready, bool cache_commit) { int i; int len; @@ -2355,21 +2356,21 @@ ReadyForQuery(POOL_CONNECTION * frontend, * Close running transactions on standbys. */ static POOL_STATUS close_standby_transactions(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { int i; for (i = 0; i < NUM_BACKENDS; i++) { - if (CONNECTION_SLOT(backend, i) && + if (&CONNECTION_SLOT(backend, i) && TSTATE(backend, i) == 'T' && BACKEND_INFO(i).backend_status == CON_UP && (MAIN_REPLICA ? PRIMARY_NODE_ID : REAL_MAIN_NODE_ID) != i) { per_node_statement_log(backend, i, "COMMIT"); if (do_command(frontend, CONNECTION(backend, i), "COMMIT", MAJOR(backend), - MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key, 0) != POOL_CONTINUE) + MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key, 0) != POOL_CONTINUE) ereport(ERROR, (errmsg("unable to close standby transactions"), errdetail("do_command returned DEADLOCK status"))); @@ -2379,7 +2380,7 @@ static POOL_STATUS close_standby_transactions(POOL_CONNECTION * frontend, } POOL_STATUS -ParseComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +ParseComplete(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { POOL_SESSION_CONTEXT *session_context; @@ -2403,7 +2404,7 @@ ParseComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) } POOL_STATUS -BindComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +BindComplete(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { POOL_SESSION_CONTEXT *session_context; @@ -2427,7 +2428,7 @@ BindComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) } POOL_STATUS -CloseComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +CloseComplete(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { POOL_SESSION_CONTEXT *session_context; POOL_STATUS status; @@ -2502,7 +2503,7 @@ CloseComplete(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) POOL_STATUS ParameterDescription(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { int len, len1 = 0; @@ -2586,7 +2587,7 @@ ParameterDescription(POOL_CONNECTION * frontend, POOL_STATUS ErrorResponse3(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { POOL_STATUS ret; @@ -2602,7 +2603,7 @@ ErrorResponse3(POOL_CONNECTION * frontend, POOL_STATUS FunctionCall(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char dummy[2]; int oid; @@ -2698,7 +2699,7 @@ FunctionCall(POOL_CONNECTION * frontend, POOL_STATUS ProcessFrontendResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { char fkind; char *bufp = NULL; @@ -2957,7 +2958,7 @@ ProcessFrontendResponse(POOL_CONNECTION * frontend, POOL_STATUS ProcessBackendResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, int *state, short *num_fields) { int status = POOL_CONTINUE; @@ -3276,7 +3277,7 @@ ProcessBackendResponse(POOL_CONNECTION * frontend, POOL_STATUS CopyInResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { POOL_STATUS status; @@ -3295,7 +3296,7 @@ CopyInResponse(POOL_CONNECTION * frontend, POOL_STATUS CopyOutResponse(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { POOL_STATUS status; @@ -3314,7 +3315,7 @@ CopyOutResponse(POOL_CONNECTION * frontend, POOL_STATUS CopyDataRows(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, int copyin) + BackendClusterConnection * backend, int copyin) { char *string = NULL; int len; @@ -3480,7 +3481,7 @@ CopyDataRows(POOL_CONNECTION * frontend, * transaction state. */ void -raise_intentional_error_if_need(POOL_CONNECTION_POOL * backend) +raise_intentional_error_if_need(BackendClusterConnection * backend) { int i; POOL_SESSION_CONTEXT *session_context; @@ -3561,7 +3562,7 @@ raise_intentional_error_if_need(POOL_CONNECTION_POOL * backend) *--------------------------------------------------- */ static int -check_errors(POOL_CONNECTION_POOL * backend, int backend_id) +check_errors(BackendClusterConnection * backend, int backend_id) { /* @@ -3661,20 +3662,18 @@ generate_error_message(char *prefix, int specific_error, char *query) * Make per DB node statement log */ void -per_node_statement_log(POOL_CONNECTION_POOL * backend, int node_id, char *query) +per_node_statement_log(BackendClusterConnection * backend, int node_id, char *query) { - POOL_CONNECTION_POOL_SLOT *slot = backend->slots[node_id]; - if (pool_config->log_per_node_statement) ereport(LOG, - (errmsg("DB node id: %d backend pid: %d statement: %s", node_id, ntohl(slot->pid), query))); + (errmsg("DB node id: %d backend pid: %d statement: %s", node_id, ntohl(backend->slots[node_id].pid), query))); } /* * Make per DB node statement notice message */ void -per_node_statement_notice(POOL_CONNECTION_POOL * backend, int node_id, char *query) +per_node_statement_notice(BackendClusterConnection * backend, int node_id, char *query) { if (pool_config->notice_per_node_statement) ereport(NOTICE, @@ -3687,9 +3686,8 @@ per_node_statement_notice(POOL_CONNECTION_POOL * backend, int node_id, char *que * All data read in this function is returned to stream. */ char -per_node_error_log(POOL_CONNECTION_POOL * backend, int node_id, char *query, char *prefix, bool unread) +per_node_error_log(BackendClusterConnection * backend, int node_id, char *query, char *prefix, bool unread) { - POOL_CONNECTION_POOL_SLOT *slot = backend->slots[node_id]; char *message; char kind; @@ -3705,7 +3703,7 @@ per_node_error_log(POOL_CONNECTION_POOL * backend, int node_id, char *query, cha { ereport(LOG, (errmsg("%s: DB node id: %d backend pid: %d statement: \"%s\" message: \"%s\"", - prefix, node_id, ntohl(slot->pid), query, message))); + prefix, node_id, ntohl(backend->slots[node_id].pid), query, message))); pfree(message); } return kind; @@ -3717,7 +3715,7 @@ per_node_error_log(POOL_CONNECTION_POOL * backend, int node_id, char *query, cha * node. Caller must provide the parse message data as "message". */ static POOL_STATUS parse_before_bind(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, POOL_SENT_MESSAGE * message, POOL_SENT_MESSAGE * bind_message) { @@ -3884,7 +3882,7 @@ static POOL_STATUS parse_before_bind(POOL_CONNECTION * frontend, * argument. */ static POOL_STATUS send_prepare(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, POOL_SENT_MESSAGE * message) { int node_id; @@ -4179,7 +4177,7 @@ flatten_set_variable_args(const char *name, List *args) * Wait till ready for query received. */ static void -pool_wait_till_ready_for_query(POOL_CONNECTION_POOL * backend) +pool_wait_till_ready_for_query(BackendClusterConnection * backend) { char kind; int len; @@ -4228,7 +4226,7 @@ pool_wait_till_ready_for_query(POOL_CONNECTION_POOL * backend) */ static void pool_discard_except_sync_and_ready_for_query(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { POOL_PENDING_MESSAGE *pmsg; int i; @@ -4337,7 +4335,7 @@ pool_discard_except_sync_and_ready_for_query(POOL_CONNECTION * frontend, * Preconditions: query is in progress. The command is succeeded. */ void -pool_at_command_success(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +pool_at_command_success(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { Node *node; char *query; @@ -4465,7 +4463,7 @@ pool_at_command_success(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backe * read message length (V3 only) */ int -pool_read_message_length(POOL_CONNECTION_POOL * cp) +pool_read_message_length(BackendClusterConnection * cp) { int length, length0; @@ -4517,7 +4515,7 @@ pool_read_message_length(POOL_CONNECTION_POOL * cp) * The array is in the static storage, thus it will be destroyed by subsequent calls. */ int * -pool_read_message_length2(POOL_CONNECTION_POOL * cp) +pool_read_message_length2(BackendClusterConnection * cp) { int length, length0; @@ -4601,7 +4599,7 @@ pool_emit_log_for_message_length_diff(int *length_array, char *name) } signed char -pool_read_kind(POOL_CONNECTION_POOL * cp) +pool_read_kind(BackendClusterConnection * cp) { char kind0, kind; @@ -4658,7 +4656,7 @@ pool_read_kind(POOL_CONNECTION_POOL * cp) } int -pool_read_int(POOL_CONNECTION_POOL * cp) +pool_read_int(BackendClusterConnection * cp) { int data0, data; @@ -4698,7 +4696,7 @@ pool_read_int(POOL_CONNECTION_POOL * cp) * In case of starting an internal transaction, this should be false. */ static void -si_get_snapshot(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, Node * node, bool tstate_check) +si_get_snapshot(POOL_CONNECTION * frontend, BackendClusterConnection * backend, Node * node, bool tstate_check) { POOL_SESSION_CONTEXT *session_context; @@ -4759,7 +4757,7 @@ si_get_snapshot(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, Node */ static bool check_transaction_state_and_abort(char *query, Node *node, POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend) + BackendClusterConnection * backend) { int len; diff --git a/src/query_cache/pool_memqcache.c b/src/query_cache/pool_memqcache.c index 24dc27ec..8c5f691a 100644 --- a/src/query_cache/pool_memqcache.c +++ b/src/query_cache/pool_memqcache.c @@ -63,11 +63,11 @@ memcached_st *memc; #endif -static char *encode_key(const char *s, char *buf, POOL_CONNECTION_POOL * backend); +static char *encode_key(const char *s, char *buf, BackendClusterConnection * backend); #ifdef DEBUG static void dump_cache_data(const char *data, size_t len); #endif -static int pool_commit_cache(POOL_CONNECTION_POOL * backend, char *query, char *data, size_t datalen, int num_oids, int *oids); +static int pool_commit_cache(BackendClusterConnection * backend, char *query, char *data, size_t datalen, int num_oids, int *oids); static int send_cached_messages(POOL_CONNECTION * frontend, const char *qcache, int qcachelen); static void send_message(POOL_CONNECTION * conn, char kind, int len, const char *data); #ifdef USE_MEMCACHED @@ -235,7 +235,7 @@ memqcache_register(char kind, * Commit SELECT results to cache storage. */ static int -pool_commit_cache(POOL_CONNECTION_POOL * backend, char *query, char *data, size_t datalen, int num_oids, int *oids) +pool_commit_cache(BackendClusterConnection * backend, char *query, char *data, size_t datalen, int num_oids, int *oids) { #ifdef USE_MEMCACHED memcached_return rc; @@ -345,7 +345,7 @@ pool_commit_cache(POOL_CONNECTION_POOL * backend, char *query, char *data, size_ * Commit SELECT system catalog results to cache storage. */ int -pool_catalog_commit_cache(POOL_CONNECTION_POOL * backend, char *query, char *data, size_t datalen) +pool_catalog_commit_cache(BackendClusterConnection * backend, char *query, char *data, size_t datalen) { #ifdef USE_MEMCACHED memcached_return rc; @@ -453,7 +453,7 @@ pool_catalog_commit_cache(POOL_CONNECTION_POOL * backend, char *query, char *dat * 1: not found */ int -pool_fetch_cache(POOL_CONNECTION_POOL * backend, const char *query, char **buf, size_t *len) +pool_fetch_cache(BackendClusterConnection * backend, const char *query, char **buf, size_t *len) { char *ptr; char tmpkey[MAX_KEY]; @@ -555,7 +555,7 @@ pool_fetch_cache(POOL_CONNECTION_POOL * backend, const char *query, char **buf, * create cache key as md5(username + query string + database name) */ static char * -encode_key(const char *s, char *buf, POOL_CONNECTION_POOL * backend) +encode_key(const char *s, char *buf, BackendClusterConnection * backend) { char *strkey; int u_length; @@ -563,12 +563,12 @@ encode_key(const char *s, char *buf, POOL_CONNECTION_POOL * backend) int q_length; int length; - u_length = strlen(backend->info->user); + u_length = strlen(backend->backend_end_point->user); ereport(DEBUG1, (errmsg("memcache encode key"), - errdetail("username: \"%s\" database_name: \"%s\"", backend->info->user, backend->info->database))); + errdetail("username: \"%s\" database_name: \"%s\"", backend->backend_end_point->user, backend->backend_end_point->database))); - d_length = strlen(backend->info->database); + d_length = strlen(backend->backend_end_point->database); q_length = strlen(s); ereport(DEBUG1, @@ -579,7 +579,7 @@ encode_key(const char *s, char *buf, POOL_CONNECTION_POOL * backend) strkey = (char *) palloc(sizeof(char) * length); - snprintf(strkey, length, "%s%s%s", backend->info->user, s, backend->info->database); + snprintf(strkey, length, "%s%s%s", backend->backend_end_point->user, s, backend->backend_end_point->database); pool_md5_hash(strkey, strlen(strkey), buf); ereport(DEBUG1, @@ -729,7 +729,7 @@ delete_cache_on_memcached(const char *key) */ POOL_STATUS pool_fetch_from_memory_cache(POOL_CONNECTION * frontend, - POOL_CONNECTION_POOL * backend, + BackendClusterConnection * backend, char *contents, bool *foundp) { char *qcache; @@ -1505,7 +1505,7 @@ pool_get_database_oid(void) */ int oid = 0; static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; backend = pool_get_session_context(false)->backend; @@ -1529,7 +1529,7 @@ pool_get_database_oid(void) * Search relcache. */ oid = (int) (intptr_t) pool_search_relcache(relcache, backend, - MAIN_CONNECTION(backend)->sp->database); + backend->sp->database); return oid; } @@ -1544,7 +1544,7 @@ pool_get_database_oid_from_dbname(char *dbname) POOL_SELECT_RESULT *res; char *query; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; backend = pool_get_session_context(false)->backend; @@ -3586,7 +3586,7 @@ pool_check_and_discard_cache_buffer(int num_oids, int *oids) * For other case At Ready for Query handle query cache. */ void -pool_handle_query_cache(POOL_CONNECTION_POOL * backend, char *query, Node *node, char state) +pool_handle_query_cache(BackendClusterConnection * backend, char *query, Node *node, char state) { POOL_SESSION_CONTEXT *session_context; pool_sigset_t oldmask; diff --git a/src/rewrite/pool_lobj.c b/src/rewrite/pool_lobj.c index 38187fe1..33031df7 100644 --- a/src/rewrite/pool_lobj.c +++ b/src/rewrite/pool_lobj.c @@ -53,7 +53,7 @@ */ char * pool_rewrite_lo_creat(char kind, char *packet, int packet_len, - POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, int *len) + POOL_CONNECTION * frontend, BackendClusterConnection * backend, int *len) { #define LO_CREAT_OID_QUERY "SELECT oid FROM pg_catalog.pg_proc WHERE proname = 'lo_creat' and pronamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')" @@ -173,8 +173,8 @@ pool_rewrite_lo_creat(char kind, char *packet, int packet_len, /* issue lock table command to lob_lock_table */ snprintf(qbuf, sizeof(qbuf), "LOCK TABLE %s IN SHARE ROW EXCLUSIVE MODE", pool_config->lobj_lock_table); per_node_statement_log(backend, MAIN_NODE_ID, qbuf); - status = do_command(frontend, MAIN(backend), qbuf, MAJOR(backend), MAIN_CONNECTION(backend)->pid, - MAIN_CONNECTION(backend)->key, 0); + status = do_command(frontend, MAIN(backend), qbuf, MAJOR(backend), MAIN_CONNECTION(backend).pid, + MAIN_CONNECTION(backend).key, 0); if (status == POOL_END) { ereport(WARNING, diff --git a/src/rewrite/pool_timestamp.c b/src/rewrite/pool_timestamp.c index 4dca05e9..acc20c0b 100644 --- a/src/rewrite/pool_timestamp.c +++ b/src/rewrite/pool_timestamp.c @@ -54,7 +54,7 @@ typedef struct typedef struct { A_Const *ts_const; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; char *relname; int num_params; /* num of original params (for Parse) */ bool rewrite_to_params; /* true if timestamp is rewritten to param @@ -70,10 +70,10 @@ static bool isStringConst(Node *node, const char *str); static bool rewrite_timestamp_walker(Node *node, void *context); static bool rewrite_timestamp_insert(InsertStmt *i_stmt, TSRewriteContext * ctx); static bool rewrite_timestamp_update(UpdateStmt *u_stmt, TSRewriteContext * ctx); -static char *get_current_timestamp(POOL_CONNECTION_POOL * backend); +static char *get_current_timestamp(BackendClusterConnection * backend); static Node *makeTsExpr(TSRewriteContext * ctx); static TypeCast *makeTypeCastFromSvfOp(SQLValueFunctionOp op); -static A_Const *makeStringConstFromQuery(POOL_CONNECTION_POOL * backend, char *expression); +static A_Const *makeStringConstFromQuery(BackendClusterConnection * backend, char *expression); bool raw_expression_tree_walker(Node *node, bool (*walker) (), void *context); POOL_RELCACHE *ts_relcache; @@ -483,7 +483,7 @@ rewrite_timestamp_walker(Node *node, void *context) * Get `now()' from MAIN node */ static char * -get_current_timestamp(POOL_CONNECTION_POOL * backend) +get_current_timestamp(BackendClusterConnection * backend) { POOL_SELECT_RESULT *res; static char timestamp[64]; @@ -786,7 +786,7 @@ rewrite_timestamp_update(UpdateStmt *u_stmt, TSRewriteContext * ctx) * returns query string as palloced string, or NULL if not to need rewrite. */ char * -rewrite_timestamp(POOL_CONNECTION_POOL * backend, Node *node, +rewrite_timestamp(BackendClusterConnection * backend, Node *node, bool rewrite_to_params, POOL_SENT_MESSAGE * message) { TSRewriteContext ctx; @@ -1068,7 +1068,7 @@ rewrite_timestamp(POOL_CONNECTION_POOL * backend, Node *node, * rewrite Bind message to add parameter values */ char * -bind_rewrite_timestamp(POOL_CONNECTION_POOL * backend, +bind_rewrite_timestamp(BackendClusterConnection * backend, POOL_SENT_MESSAGE * message, const char *orig_msg, int *len) { @@ -1250,7 +1250,7 @@ bind_rewrite_timestamp(POOL_CONNECTION_POOL * backend, /* make A_Const of T_String from "SELECT "*/ static A_Const * -makeStringConstFromQuery(POOL_CONNECTION_POOL * backend, char *expression) +makeStringConstFromQuery(BackendClusterConnection * backend, char *expression) { A_Const *con; POOL_SELECT_RESULT *res; diff --git a/src/sample/pgpool.conf.sample-stream b/src/sample/pgpool.conf.sample-stream index b2f67275..fae19777 100644 --- a/src/sample/pgpool.conf.sample-stream +++ b/src/sample/pgpool.conf.sample-stream @@ -206,8 +206,18 @@ backend_clustering_mode = 'streaming_replication' #max_pool = 4 # Number of connection pool caches per connection + # only valid for connection_pool_type = classic # (change requires restart) +#max_pool_size = 32 + # Maximum number of global connection pool caches + # only valid for connection_pool_type = global + # (change requires restart) + +#pool_availability_timeout = 5 + # Maximum time in seconds to wait for pool connection + # slot to become available when the pool is full. + # - Life time - #child_life_time = 5min @@ -362,6 +372,11 @@ backend_clustering_mode = 'streaming_replication' # Semicolon separated list of queries # to be issued at the end of a session # The default is for 8.3 and later +#connection_pool_type = classic + # Type of connection pooling to use. + # Possible types are 'global' or 'classic'. + # (change requires restart) + #reset_query_list = 'ABORT; DISCARD ALL' # The following one is for 8.2 and before #reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT' diff --git a/src/streaming_replication/pool_worker_child.c b/src/streaming_replication/pool_worker_child.c index 2a410564..f766046b 100644 --- a/src/streaming_replication/pool_worker_child.c +++ b/src/streaming_replication/pool_worker_child.c @@ -69,7 +69,7 @@ #include "watchdog/wd_internal_commands.h" #include "watchdog/watchdog.h" -static POOL_CONNECTION_POOL_SLOT * slots[MAX_NUM_BACKENDS]; +static BackendNodeConnection *slots[MAX_NUM_BACKENDS]; static volatile sig_atomic_t reload_config_request = 0; static volatile sig_atomic_t restart_request = 0; @@ -740,8 +740,7 @@ reload_config(void) * Caller must prepare memory for POOL_SELECT_RESULT and pass it as "res". It * is guaranteed that no exception occurs within this function. */ -int -get_query_result(POOL_CONNECTION_POOL_SLOT * *slots, int backend_id, char *query, POOL_SELECT_RESULT * *res) +int get_query_result(BackendNodeConnection **slots, int backend_id, char *query, POOL_SELECT_RESULT **res) { int sts = -1; MemoryContext oldContext = CurrentMemoryContext; diff --git a/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c b/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c index 89a36f2c..0e493aa8 100644 --- a/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c +++ b/src/test/regression/tests/010.rewrite_timestamp/timestamp/main.c @@ -78,7 +78,7 @@ pool_get_major_version(void) PGVersion * -Pgversion(POOL_CONNECTION_POOL * backend) +Pgversion(BackendClusterConnection * backend) { #define VERSION_BUF_SIZE 10 static PGVersion pgversion; @@ -97,7 +97,7 @@ pool_create_relcache(int cachesize, char *sql, func_ptr register_func, func_ptr /* dummy result of relcache (attrname, adsrc, usetimestamp)*/ void * -pool_search_relcache(POOL_RELCACHE * relcache, POOL_CONNECTION_POOL * backend, char *table) +pool_search_relcache(POOL_RELCACHE * relcache, BackendClusterConnection * backend, char *table) { if (strcmp(table, "\"rel1\"") == 0) return (void *) &(rc[0]); @@ -134,8 +134,8 @@ main(int argc, char **argv) List *tree; ListCell *l; StartupPacket sp; - POOL_CONNECTION_POOL backend; - POOL_CONNECTION_POOL_SLOT slot; + BackendClusterConnection backend; + BackendNodeConnection slot; POOL_SENT_MESSAGE msg; POOL_QUERY_CONTEXT ctx; diff --git a/src/utils/ancillary/fd_recv.c b/src/utils/ancillary/fd_recv.c new file mode 100644 index 00000000..57cb9a15 --- /dev/null +++ b/src/utils/ancillary/fd_recv.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * fd_send.c - receiving file descriptors + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XPG4_2 /* Solaris sucks */ +# define _XPG4_2 +#endif + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include /* FreeBSD sucks */ +#endif + +#include "utils/ancillary/ancillary.h" + +int +ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer) +{ + struct msghdr msghdr; + char nothing; + struct iovec nothing_ptr; + struct cmsghdr *cmsg; + int i; + + nothing_ptr.iov_base = ¬hing; + nothing_ptr.iov_len = 1; + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = ¬hing_ptr; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + msghdr.msg_control = buffer; + msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_len = msghdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for(i = 0; i < n_fds; i++) + ((int *)CMSG_DATA(cmsg))[i] = -1; + + if(recvmsg(sock, &msghdr, 0) < 0) + return(-1); + for(i = 0; i < n_fds; i++) + fds[i] = ((int *)CMSG_DATA(cmsg))[i]; + n_fds = (msghdr.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); + return(n_fds); +} + +#ifndef SPARE_RECV_FDS +int +ancil_recv_fds(int sock, int *fd, unsigned n_fds) +{ + ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; + + assert(n_fds <= ANCIL_MAX_N_FDS); + return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer)); +} +#endif /* SPARE_RECV_FDS */ + +#ifndef SPARE_RECV_FD +int +ancil_recv_fd(int sock, int *fd) +{ + ANCIL_FD_BUFFER(1) buffer; + + return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1); +} +#endif /* SPARE_RECV_FD */ diff --git a/src/utils/ancillary/fd_send.c b/src/utils/ancillary/fd_send.c new file mode 100644 index 00000000..6f60cf9f --- /dev/null +++ b/src/utils/ancillary/fd_send.c @@ -0,0 +1,92 @@ +/*************************************************************************** + * libancillary - black magic on Unix domain sockets + * (C) Nicolas George + * fd_send.c - sending file descriptors + ***************************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _XPG4_2 /* Solaris sucks */ +# define _XPG4_2 +#endif + +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include /* FreeBSD sucks */ +#endif + +#include "utils/ancillary/ancillary.h" + +int +ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer) +{ + struct msghdr msghdr; + char nothing = '!'; + struct iovec nothing_ptr; + struct cmsghdr *cmsg; + int i; + + nothing_ptr.iov_base = ¬hing; + nothing_ptr.iov_len = 1; + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = ¬hing_ptr; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + msghdr.msg_control = buffer; + msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_len = msghdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + for(i = 0; i < n_fds; i++) + ((int *)CMSG_DATA(cmsg))[i] = fds[i]; + return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1); +} + +#ifndef SPARE_SEND_FDS +int +ancil_send_fds(int sock, const int *fds, unsigned n_fds) +{ + ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; + + assert(n_fds <= ANCIL_MAX_N_FDS); + return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer)); +} +#endif /* SPARE_SEND_FDS */ + +#ifndef SPARE_SEND_FD +int +ancil_send_fd(int sock, int fd) +{ + ANCIL_FD_BUFFER(1) buffer; + + return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer)); +} +#endif /* SPARE_SEND_FD */ diff --git a/src/utils/mmgr/mcxt.c b/src/utils/mmgr/mcxt.c index 2033e9d1..627e03db 100644 --- a/src/utils/mmgr/mcxt.c +++ b/src/utils/mmgr/mcxt.c @@ -52,6 +52,9 @@ MemoryContext CacheMemoryContext = NULL; MemoryContext MessageContext = NULL; MemoryContext QueryContext = NULL; +#ifdef USE_ASSERT_CHECKING +int CritSectionCount = 0; +#endif static void MemoryContextCallResetCallbacks(MemoryContext context); static void MemoryContextStatsInternal(MemoryContext context, int level, diff --git a/src/utils/pool_params.c b/src/utils/pool_params.c index 43bdb7c5..d65d3830 100644 --- a/src/utils/pool_params.c +++ b/src/utils/pool_params.c @@ -33,22 +33,13 @@ #include "utils/palloc.h" #include "utils/memutils.h" -#define MAX_PARAM_ITEMS 128 - /* * initialize parameter structure */ int pool_init_params(ParamStatus * params) { - MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); - params->num = 0; - params->names = palloc(MAX_PARAM_ITEMS * sizeof(char *)); - params->values = palloc(MAX_PARAM_ITEMS * sizeof(char *)); - - MemoryContextSwitchTo(oldContext); - return 0; } @@ -58,21 +49,7 @@ pool_init_params(ParamStatus * params) void pool_discard_params(ParamStatus * params) { - int i; - - for (i = 0; i < params->num; i++) - { - pfree(params->names[i]); - pfree(params->values[i]); - } - if (params->names) - pfree(params->names); - if (params->values) - pfree(params->values); params->num = 0; - params->names = NULL; - params->values = NULL; - } /* @@ -87,10 +64,10 @@ pool_find_name(ParamStatus * params, char *name, int *pos) for (i = 0; i < params->num; i++) { - if (!strcmp(name, params->names[i])) + if (!strcmp(name, params->params[i].name)) { *pos = i; - return params->values[i]; + return params->params[i].value; } } return NULL; @@ -105,8 +82,8 @@ pool_get_param(ParamStatus * params, int index, char **name, char **value) if (index < 0 || index >= params->num) return -1; - *name = params->names[index]; - *value = params->values[index]; + *name = params->params[index].name; + *value = params->params[index].value; return 0; } @@ -120,31 +97,19 @@ pool_add_param(ParamStatus * params, char *name, char *value) int pos; MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); - if (pool_find_name(params, name, &pos)) - { - /* name already exists */ - if (strlen(params->values[pos]) < strlen(value)) - { - params->values[pos] = repalloc(params->values[pos], strlen(value) + 1); - } - strcpy(params->values[pos], value); - } - else + if (pool_find_name(params, name, &pos) == NULL) { - int num; - - /* add name/value pair */ if (params->num >= MAX_PARAM_ITEMS) { ereport(ERROR, - (errmsg("add parameter failed"), - errdetail("no more room for num"))); + (errmsg("add parameter failed"), + errdetail("no more room for parameter number %d", params->num))); } - num = params->num; - params->names[num] = pstrdup(name); - params->values[num] = pstrdup(value); + pos = params->num; + strncpy(params->params[pos].name, name, sizeof(params->params[pos].name)); params->num++; } + strncpy(params->params[pos].value, value, sizeof(params->params[pos].value)); parser_set_param(name, value); MemoryContextSwitchTo(oldContext); @@ -158,7 +123,7 @@ pool_param_debug_print(ParamStatus * params) for (i = 0; i < params->num; i++) { - ereport(DEBUG2, - (errmsg("No.%d: name: %s value: %s", i, params->names[i], params->values[i]))); + ereport(LOG, + (errmsg("No.%d: name: %s value: %s", i, params->params[i].name, params->params[i].value))); } } diff --git a/src/utils/pool_process_reporting.c b/src/utils/pool_process_reporting.c index a431d032..78be6ef2 100644 --- a/src/utils/pool_process_reporting.c +++ b/src/utils/pool_process_reporting.c @@ -21,6 +21,7 @@ * Process pgPool-II "SHOW" queries. */ #include "pool.h" +#include "utils/pool_process_reporting.h" #include "main/health_check.h" #include "protocol/pool_proto_modules.h" #include "protocol/pool_process_query.h" @@ -37,16 +38,18 @@ #include #include -static void send_row_description_and_data_rows(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +static void send_row_description_and_data_rows(POOL_CONNECTION * frontend, BackendClusterConnection * backend, short num_fields, char **field_names, int *offsettbl, char *data, int row_size, int nrows); static void write_one_field(POOL_CONNECTION * frontend, char *field); static void write_one_field_v2(POOL_CONNECTION * frontend, char *field); static char *db_node_status(int node); static char *db_node_role(int node); +static int add_pool_entry_info_to_report(POOL_REPORT_POOLS *pools, int start_line, ConnectionPoolEntry *pool_entry); + void -send_row_description(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +send_row_description(POOL_CONNECTION * frontend, BackendClusterConnection * backend, short num_fields, char **field_names) { static char *cursorname = "blank"; @@ -124,7 +127,7 @@ send_row_description(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, * to the command complete message. */ void -send_complete_and_ready(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *message, const int num_rows) +send_complete_and_ready(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *message, const int num_rows) { int len; int msg_len; @@ -364,6 +367,16 @@ get_config(int *nrows) StrNCpy(status[i].desc, "max # of connection pool per child", POOLCONFIG_MAXDESCLEN); i++; + StrNCpy(status[i].name, "max_pool_size", POOLCONFIG_MAXNAMELEN); + snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->max_pool_size); + StrNCpy(status[i].desc, "max # of global connection pools", POOLCONFIG_MAXDESCLEN); + i++; + + StrNCpy(status[i].name, "connection_pool_type", POOLCONFIG_MAXNAMELEN); + snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->connection_pool_type); + StrNCpy(status[i].desc, "connection pool type", POOLCONFIG_MAXDESCLEN); + i++; + StrNCpy(status[i].name, "process_management_mode", POOLCONFIG_MAXNAMELEN); snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->process_management); StrNCpy(status[i].desc, "process management mode", POOLCONFIG_MAXDESCLEN); @@ -1199,7 +1212,7 @@ get_config(int *nrows) } void -send_config_var_detail_row(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *name, const char *value, const char *description) +send_config_var_detail_row(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *name, const char *value, const char *description) { int size; int hsize; @@ -1262,7 +1275,7 @@ send_config_var_detail_row(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * ba } void -send_config_var_value_only_row(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *value) +send_config_var_value_only_row(POOL_CONNECTION * frontend, BackendClusterConnection * backend, const char *value) { int size; int hsize; @@ -1304,7 +1317,7 @@ send_config_var_value_only_row(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL } void -config_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +config_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"item", "value", "description"}; static unsigned char nullmap[2] = {0xff, 0xff}; @@ -1474,7 +1487,7 @@ get_nodes(int *nrows, int node_id) * SHOW pool_nodes; */ void -nodes_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +nodes_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"node_id", "hostname", "port", "status", "pg_status", "lb_weight", "role", "pg_role", "select_cnt", "load_balance_node", "replication_delay", @@ -1510,190 +1523,198 @@ nodes_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) pfree(nodes); } - -/* - * Used by pcp_proc_info and SHOW pool_pools - */ -POOL_REPORT_POOLS * -get_pools(int *nrows) +static int +add_pool_entry_info_to_report(POOL_REPORT_POOLS *pools, int start_line, ConnectionPoolEntry *pool_entry) { - int child, - pool, - poolBE, - backend_id; + int backend_id; + int lines = start_line; + int idle_duration = 0; // TODO pi->connection_info[pool * MAX_NUM_BACKENDS].client_idle_duration; + int cliet_idle_time = pool_config->client_idle_limit; + bool is_pool_empty = false; + char proc_start_time[POOLCONFIG_MAXDATELEN + 1]; ProcessInfo *pi = NULL; - int proc_id; + Assert(pool_entry); - int lines = 0; - - POOL_REPORT_POOLS *pools = palloc0( - pool_config->num_init_children * pool_config->max_pool * NUM_BACKENDS * sizeof(POOL_REPORT_POOLS) - ); - - for (child = 0; child < pool_config->num_init_children; child++) + if (pool_config->connection_pool_type == CLASSIC_CONNECTION_POOL) { - int exist_live_connection = 0; + /* If the pool belongs to child that does not exists. No point in + * adding it to info + */ + if (pool_entry->child_id < 0) + return 0; + pi = &process_info[pool_entry->child_id]; + if (pi->pid <= 0) + return 0; + } + else if (pool_entry->child_id > 0) + pi = &process_info[pool_entry->child_id]; - pi = &process_info[child]; - proc_id = pi->pid; + if (pi && pi->start_time) + { + if (pool_config->child_life_time > 0) + { + char temp_proc_start_time[POOLCONFIG_MAXDATELEN + 1]; + int wait_for_connect_time = pool_config->child_life_time - pi->wait_for_connect; - for (pool = 0; pool < pool_config->max_pool; pool++) + strftime(temp_proc_start_time, sizeof(temp_proc_start_time), + "%Y-%m-%d %H:%M:%S", localtime(&pi->start_time)); + snprintf(proc_start_time, sizeof(proc_start_time), + "%s (%d:%02d before process restarting)", temp_proc_start_time, + wait_for_connect_time / 60, + wait_for_connect_time % 60); + } + else { - poolBE = pool * MAX_NUM_BACKENDS; - if (pi->connection_info[poolBE].connected) - { - exist_live_connection = 1; - break; - } + strftime(proc_start_time, sizeof(proc_start_time), + "%Y-%m-%d %H:%M:%S", localtime(&pi->start_time)); } + } + else + snprintf(proc_start_time, sizeof(proc_start_time), + "not attached to a process"); + if (strlen(pool_entry->endPoint.database) == 0) + is_pool_empty = true; - for (pool = 0; pool < pool_config->max_pool; pool++) - { - int idle_duration = pi->connection_info[pool * MAX_NUM_BACKENDS].client_idle_duration; - int load_balancing_node_id = pi->connection_info[pool * MAX_NUM_BACKENDS].load_balancing_node; - int client_idle_time = pool_config->client_idle_limit; - - if (pool_config->client_idle_limit > 0) - { - client_idle_time = pool_config->client_idle_limit - idle_duration; - } + if (pool_config->client_idle_limit > 0) + { + cliet_idle_time = pool_config->client_idle_limit - idle_duration; + } - for (backend_id = 0; backend_id < NUM_BACKENDS; backend_id++) - { - poolBE = pool * MAX_NUM_BACKENDS + backend_id; - snprintf(pools[lines].pool_pid, sizeof(pools[lines].pool_pid), "%d", proc_id); + for (backend_id = 0; backend_id < NUM_BACKENDS; backend_id++) + { + if (pool_entry->child_pid > 0) + snprintf(pools[lines].pool_pid, sizeof(pools[lines].pool_pid), "%d", pool_entry->child_pid); + else + snprintf(pools[lines].pool_pid, sizeof(pools[lines].pool_pid), "%s", "N/A"); - if (pi->start_time) - { - if ((pool_config->child_life_time > 0) - && (pi->connected) - && (!exist_live_connection)) - { - char proc_start_time[POOLCONFIG_MAXDATELEN + 1]; - int wait_for_connect_time = pool_config->child_life_time - pi->wait_for_connect; - - strftime(proc_start_time, sizeof(proc_start_time), - "%Y-%m-%d %H:%M:%S", localtime(&pi->start_time)); - snprintf(pools[lines].process_start_time, sizeof(pools[lines].process_start_time), - "%s (%d:%02d before process restarting)", proc_start_time, - wait_for_connect_time / 60, - wait_for_connect_time % 60); - } - else - { - strftime(pools[lines].process_start_time, sizeof(pools[lines].process_start_time), - "%Y-%m-%d %H:%M:%S", localtime(&pi->start_time)); - } - } - else - *(pools[lines].process_start_time) = '\0'; + memcpy(pools[lines].process_start_time, proc_start_time, sizeof(pools[lines].process_start_time)); - snprintf(pools[lines].pool_id, sizeof(pools[lines].pool_id), "%d", pool); + snprintf(pools[lines].pool_id, sizeof(pools[lines].pool_id), "%d", pool_entry->pool_id); - snprintf(pools[lines].backend_id, sizeof(pools[lines].backend_id), "%d", backend_id); + snprintf(pools[lines].backend_id, sizeof(pools[lines].backend_id), "%d", backend_id); - snprintf(pools[lines].client_connection_count, sizeof(pools[lines].client_connection_count), - "%d", pi->client_connection_count); + snprintf(pools[lines].client_connection_count, sizeof(pools[lines].client_connection_count), + "%d", pool_entry->endPoint.client_connection_count); - if (pi->connection_info[poolBE].client_connection_time == 0) - { - *(pools[lines].client_connection_time) = '\0'; - } - else - { - strftime(pools[lines].client_connection_time, sizeof(pools[lines].client_connection_time), - "%Y-%m-%d %H:%M:%S", localtime(&pi->connection_info[poolBE].client_connection_time)); - } + if (pool_entry->endPoint.client_connection_time == 0) + { + *(pools[lines].client_connection_time) = '\0'; + } + else + { + strftime(pools[lines].client_connection_time, sizeof(pools[lines].client_connection_time), + "%Y-%m-%d %H:%M:%S", localtime(&pool_entry->endPoint.client_connection_time)); + } - if (pi->connection_info[poolBE].client_disconnection_time == 0) - { - *(pools[lines].client_disconnection_time) = '\0'; - } - else - { - strftime(pools[lines].client_disconnection_time, sizeof(pools[lines].client_disconnection_time), - "%Y-%m-%d %H:%M:%S", localtime(&pi->connection_info[poolBE].client_disconnection_time)); - } + if (pool_entry->endPoint.client_disconnection_time == 0) + { + *(pools[lines].client_disconnection_time) = '\0'; + } + else + { + strftime(pools[lines].client_disconnection_time, sizeof(pools[lines].client_disconnection_time), + "%Y-%m-%d %H:%M:%S", localtime(&pool_entry->endPoint.client_disconnection_time)); + } - if ((pool_config->client_idle_limit > 0) - && (pi->connection_info[poolBE].connected)) - { - snprintf(pools[lines].client_idle_duration, sizeof(pools[lines].client_idle_duration), - "%d (%d:%02d before client disconnected)", idle_duration, - client_idle_time / 60, - client_idle_time % 60); - } - else - snprintf(pools[lines].client_idle_duration, sizeof(pools[lines].client_idle_duration), - "%d", idle_duration); + if ((pool_config->client_idle_limit > 0) && pi) + { + snprintf(pools[lines].client_idle_duration, sizeof(pools[lines].client_idle_duration), + "%d (%d:%02d before client disconnected)", idle_duration, + cliet_idle_time / 60, + cliet_idle_time % 60); + } + else + snprintf(pools[lines].client_idle_duration, sizeof(pools[lines].client_idle_duration), + "%d", idle_duration); - if (strlen(pi->connection_info[poolBE].database) == 0) - { - StrNCpy(pools[lines].database, "", POOLCONFIG_MAXIDENTLEN); - StrNCpy(pools[lines].username, "", POOLCONFIG_MAXIDENTLEN); - *(pools[lines].backend_connection_time) = '\0'; - snprintf(pools[lines].pool_majorversion, sizeof(pools[lines].pool_majorversion), "%d", 0); - snprintf(pools[lines].pool_minorversion, sizeof(pools[lines].pool_minorversion), "%d", 0); - } - else - { - StrNCpy(pools[lines].database, pi->connection_info[poolBE].database, POOLCONFIG_MAXIDENTLEN); - StrNCpy(pools[lines].username, pi->connection_info[poolBE].user, POOLCONFIG_MAXIDENTLEN); - strftime(pools[lines].backend_connection_time, sizeof(pools[lines].backend_connection_time), - "%Y-%m-%d %H:%M:%S", localtime(&pi->connection_info[poolBE].create_time)); - snprintf(pools[lines].pool_majorversion, sizeof(pools[lines].pool_majorversion), "%d", - pi->connection_info[poolBE].major); - snprintf(pools[lines].pool_minorversion, sizeof(pools[lines].pool_minorversion), "%d", - pi->connection_info[poolBE].minor); - } - snprintf(pools[lines].pool_counter, sizeof(pools[lines].pool_counter), "%d", - pi->connection_info[poolBE].counter); - snprintf(pools[lines].pool_backendpid, sizeof(pools[lines].pool_backendpid), "%d", - ntohl(pi->connection_info[poolBE].pid)); - snprintf(pools[lines].pool_connected, sizeof(pools[lines].pool_connected), "%d", - pi->connection_info[poolBE].connected); - - switch(pi->status) - { - case WAIT_FOR_CONNECT: - StrNCpy(pools[lines].status, "Wait for connection", POOLCONFIG_MAXPROCESSSTATUSLEN); - break; - case COMMAND_EXECUTE: - StrNCpy(pools[lines].status, "Execute command", POOLCONFIG_MAXPROCESSSTATUSLEN); - break; - case IDLE: - StrNCpy(pools[lines].status, "Idle", POOLCONFIG_MAXPROCESSSTATUSLEN); - break; - case IDLE_IN_TRANS: - StrNCpy(pools[lines].status, "Idle in transaction", POOLCONFIG_MAXPROCESSSTATUSLEN); - break; - case CONNECTING: - StrNCpy(pools[lines].status, "Connecting", POOLCONFIG_MAXPROCESSSTATUSLEN); - break; - default: - *(pools[lines].status) = '\0'; - } + if (is_pool_empty) + { + StrNCpy(pools[lines].database, "", POOLCONFIG_MAXIDENTLEN); + StrNCpy(pools[lines].username, "", POOLCONFIG_MAXIDENTLEN); + *(pools[lines].backend_connection_time) = '\0'; + snprintf(pools[lines].pool_majorversion, sizeof(pools[lines].pool_majorversion), "%d", 0); + snprintf(pools[lines].pool_minorversion, sizeof(pools[lines].pool_minorversion), "%d", 0); + + snprintf(pools[lines].pool_counter, sizeof(pools[lines].pool_counter), "%d", 0); + snprintf(pools[lines].pool_backendpid, sizeof(pools[lines].pool_backendpid), "%d",0); + snprintf(pools[lines].pool_connected, sizeof(pools[lines].pool_connected), "%d", 0); + } + else + { + StrNCpy(pools[lines].database, pool_entry->endPoint.database, POOLCONFIG_MAXIDENTLEN); + StrNCpy(pools[lines].username, pool_entry->endPoint.user, POOLCONFIG_MAXIDENTLEN); + strftime(pools[lines].backend_connection_time, sizeof(pools[lines].backend_connection_time), + "%Y-%m-%d %H:%M:%S", localtime(&pool_entry->endPoint.conn_slots[backend_id].create_time)); + snprintf(pools[lines].pool_majorversion, sizeof(pools[lines].pool_majorversion), "%d", + 3); + snprintf(pools[lines].pool_minorversion, sizeof(pools[lines].pool_minorversion), "%d", + 0); + snprintf(pools[lines].pool_counter, sizeof(pools[lines].pool_counter), "%d", + pool_entry->endPoint.conn_slots[backend_id].counter); + snprintf(pools[lines].pool_backendpid, sizeof(pools[lines].pool_backendpid), "%d", + ntohl(pool_entry->endPoint.conn_slots[backend_id].pid)); + snprintf(pools[lines].pool_connected, sizeof(pools[lines].pool_connected), "%d", pi ? pi->connected : 0); + } - if (pi->connection_info[poolBE].connected && backend_id == load_balancing_node_id) - StrNCpy(pools[lines].load_balance_node, "1", POOLCONFIG_MAXPROCESSSTATUSLEN); - else - StrNCpy(pools[lines].load_balance_node, "0", POOLCONFIG_MAXPROCESSSTATUSLEN); - lines++; + if (!pi) + StrNCpy(pools[lines].status, "Pool not leased", POOLCONFIG_MAXPROCESSSTATUSLEN); + else + { + switch (pi->status) + { + case WAIT_FOR_CONNECT: + StrNCpy(pools[lines].status, "Wait for connection", POOLCONFIG_MAXPROCESSSTATUSLEN); + break; + case COMMAND_EXECUTE: + StrNCpy(pools[lines].status, "Execute command", POOLCONFIG_MAXPROCESSSTATUSLEN); + break; + case IDLE: + StrNCpy(pools[lines].status, "Idle", POOLCONFIG_MAXPROCESSSTATUSLEN); + break; + case IDLE_IN_TRANS: + StrNCpy(pools[lines].status, "Idle in transaction", POOLCONFIG_MAXPROCESSSTATUSLEN); + break; + case CONNECTING: + StrNCpy(pools[lines].status, "Connecting", POOLCONFIG_MAXPROCESSSTATUSLEN); + break; + default: + *(pools[lines].status) = '\0'; } } + if (is_pool_empty) + StrNCpy(pools[lines].load_balance_node, "N/A", POOLCONFIG_MAXPROCESSSTATUSLEN); + else if (pi && backend_id == pool_entry->endPoint.load_balancing_node) + StrNCpy(pools[lines].load_balance_node, "1", POOLCONFIG_MAXPROCESSSTATUSLEN); + else + StrNCpy(pools[lines].load_balance_node, "0", POOLCONFIG_MAXPROCESSSTATUSLEN); + lines++; } + return lines - start_line; +} +/* + * Used by pcp_proc_info and SHOW pool_pools + */ +POOL_REPORT_POOLS * +get_pools(int *nrows) +{ + int pool_id; + int lines = 0; + POOL_REPORT_POOLS *pools = palloc0(GetPoolEntriesCount() *NUM_BACKENDS * sizeof(POOL_REPORT_POOLS)); + for (pool_id = 0; pool_id < GetPoolEntriesCount(); pool_id++) + { + lines += add_pool_entry_info_to_report(pools, lines, GetConnectionPoolEntryAtIndex(pool_id)); + } *nrows = lines; return pools; } - /* * SHOW pool_pools; */ void -pools_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +pools_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { short num_fields; static char *field_names[] = {"pool_pid", "start_time", "client_connection_count", "pool_id", @@ -1722,30 +1743,29 @@ pools_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) POOL_REPORT_PROCESSES * get_processes(int *nrows) { - int child; - int pool; - int poolBE; + int child = 0; + int child_id; ProcessInfo *pi = NULL; int proc_id; POOL_REPORT_PROCESSES *processes = palloc0(pool_config->num_init_children * sizeof(POOL_REPORT_PROCESSES)); - for (child = 0; child < pool_config->num_init_children; child++) + for (child_id = 0; child_id < pool_config->num_init_children; child_id++) { int exist_live_connection = 0; + ConnectionPoolEntry *pool_entry = NULL; - pi = &process_info[child]; - proc_id = pi->pid; + pi = &process_info[child_id]; + if (pi->pid <= 0) + continue; - for (pool = 0; pool < pool_config->max_pool; pool++) + if (pi->pool_id >= 0) { - poolBE = pool * MAX_NUM_BACKENDS; - if (pi->connection_info[poolBE].connected) - { + pool_entry = GetConnectionPoolEntry(pi->pool_id, child_id); + if (pool_entry && pool_entry->endPoint.client_connected) exist_live_connection = 1; - break; - } } + proc_id = pi->pid; snprintf(processes[child].pool_pid, POOLCONFIG_MAXCOUNTLEN, "%d", proc_id); if ((pool_config->child_life_time > 0) @@ -1774,18 +1794,15 @@ get_processes(int *nrows) StrNCpy(processes[child].backend_connection_time, "", POOLCONFIG_MAXDATELEN); StrNCpy(processes[child].pool_counter, "", POOLCONFIG_MAXCOUNTLEN); - for (pool = 0; pool < pool_config->max_pool; pool++) + if (pool_entry && + pool_entry->endPoint.client_connected && + strlen(pool_entry->endPoint.database) > 0 && + strlen(pool_entry->endPoint.user) > 0) { - poolBE = pool * MAX_NUM_BACKENDS; - if (pi->connection_info[poolBE].connected && - strlen(pi->connection_info[poolBE].database) > 0 && - strlen(pi->connection_info[poolBE].user) > 0) - { - StrNCpy(processes[child].database, pi->connection_info[poolBE].database, POOLCONFIG_MAXIDENTLEN); - StrNCpy(processes[child].username, pi->connection_info[poolBE].user, POOLCONFIG_MAXIDENTLEN); - strftime(processes[child].backend_connection_time, POOLCONFIG_MAXDATELEN, "%Y-%m-%d %H:%M:%S", localtime(&pi->connection_info[poolBE].create_time)); - snprintf(processes[child].pool_counter, POOLCONFIG_MAXCOUNTLEN, "%d", pi->connection_info[poolBE].counter); - } + StrNCpy(processes[child].database, pool_entry->endPoint.database, POOLCONFIG_MAXIDENTLEN); + StrNCpy(processes[child].username, pool_entry->endPoint.user, POOLCONFIG_MAXIDENTLEN); + strftime(processes[child].backend_connection_time, POOLCONFIG_MAXDATELEN, "%Y-%m-%d %H:%M:%S", localtime(&pool_entry->endPoint.create_time)); + snprintf(processes[child].pool_counter, POOLCONFIG_MAXCOUNTLEN, "%d", pool_entry->endPoint.client_connection_count); } switch(pi->status) { @@ -1807,6 +1824,7 @@ get_processes(int *nrows) default: *(processes[child].status) = '\0'; } + child++; } *nrows = child; @@ -1818,7 +1836,7 @@ get_processes(int *nrows) * SHOW pool_processes */ void -processes_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +processes_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"pool_pid", "start_time", "client_connection_count", "database", "username", "backend_connection_time", "pool_counter", "status"}; @@ -1861,7 +1879,7 @@ get_version(void) * SHOW pool_version; */ void -version_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +version_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"pool_version"}; static int offsettbl[] = { @@ -1885,7 +1903,7 @@ version_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) * Show in memory cache reporting */ void -cache_reporting(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +cache_reporting(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"num_cache_hits", "num_selects", "cache_hit_ratio", "num_hash_entries", "used_hash_entries", "num_cache_entries", "used_cache_entries_size", "free_cache_entries_size", "fragment_cache_entries_size"}; short num_fields = sizeof(field_names) / sizeof(char *); @@ -2100,7 +2118,7 @@ get_health_check_stats(int *nrows) * SHOW health_check_stats; */ void -show_health_check_stats(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +show_health_check_stats(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"node_id", "hostname", "port", "status", "role", "last_status_change", "total_count", "success_count", "fail_count", "skip_count", "retry_count", @@ -2202,7 +2220,7 @@ get_backend_stats(int *nrows) * SHOW backend_stats; */ void -show_backend_stats(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) +show_backend_stats(POOL_CONNECTION * frontend, BackendClusterConnection * backend) { static char *field_names[] = {"node_id", "hostname", "port", "status", "role", "select_cnt", "insert_cnt", "update_cnt", "delete_cnt", "ddl_cnt", "other_cnt", @@ -2258,7 +2276,7 @@ show_backend_stats(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend) * * nrows: number of rows in data. */ -static void send_row_description_and_data_rows(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, +static void send_row_description_and_data_rows(POOL_CONNECTION * frontend, BackendClusterConnection * backend, short num_fields, char **field_names, int *offsettbl, char *data, int row_size, int nrows) { @@ -2422,7 +2440,7 @@ static char *db_node_role(int node) { BackendInfo *bkinfo; - POOL_CONNECTION_POOL_SLOT *slots[MAX_NUM_BACKENDS]; + BackendNodeConnection *slots[MAX_NUM_BACKENDS]; POOL_SELECT_RESULT *res; char *user; char *password; diff --git a/src/utils/pool_relcache.c b/src/utils/pool_relcache.c index 32362fc6..659cc4b5 100644 --- a/src/utils/pool_relcache.c +++ b/src/utils/pool_relcache.c @@ -105,7 +105,7 @@ pool_discard_relcache(POOL_RELCACHE * relcache) * 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) +pool_search_relcache(POOL_RELCACHE * relcache, BackendClusterConnection * backend, char *table) { char *dbname; int i; @@ -145,14 +145,14 @@ pool_search_relcache(POOL_RELCACHE * relcache, POOL_CONNECTION_POOL * backend, c */ if (pool_config->relcache_query_target == RELQTARGET_LOAD_BALANCE_NODE && session_context && VALID_BACKEND_RAW(session_context->load_balance_node_id) && - backend->slots[session_context->load_balance_node_id]) + &backend->slots[session_context->load_balance_node_id]) { - dbname = backend->slots[session_context->load_balance_node_id]->sp->database; + dbname = backend->sp->database; node_id = session_context->load_balance_node_id; } else { - dbname = MAIN_CONNECTION(backend)->sp->database; + dbname = backend->sp->database; /* * If in streaming replication mode, prefer to send query to the diff --git a/src/utils/pool_select_walker.c b/src/utils/pool_select_walker.c index 27e745ba..29b5c6ac 100644 --- a/src/utils/pool_select_walker.c +++ b/src/utils/pool_select_walker.c @@ -574,7 +574,7 @@ is_system_catalog(char *table_name) bool result; static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; if (table_name == NULL) { @@ -668,7 +668,7 @@ is_temp_table(char *table_name) #define ISTEMPQUERY84 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.relname = '%s' AND c.relistemp" bool result; char *query; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; int major; if (table_name == NULL || pool_config->check_temp_table == CHECK_TEMP_NONE || @@ -777,7 +777,7 @@ is_unlogged_table(char *table_name) #define ISUNLOGGEDQUERY3 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.oid = pg_catalog.to_regclass('%s') AND c.relpersistence = 'u'" static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; int major; if (table_name == NULL) @@ -859,7 +859,7 @@ is_view(char *table_name) #define ISVIEWQUERY3 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.oid = pg_catalog.to_regclass('%s') AND (c.relkind = 'v' OR c.relkind = 'm')" static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; bool result; char *query; @@ -922,11 +922,11 @@ pool_has_pgpool_regclass(void) bool result; static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; char *user; backend = pool_get_session_context(false)->backend; - user = MAIN_CONNECTION(backend)->sp->user; + user = backend->sp->user; if (!relcache) { @@ -951,7 +951,7 @@ pool_has_pgpool_regclass(void) bool pool_has_to_regclass(void) { - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; PGVersion *pgversion; backend = pool_get_session_context(false)->backend; @@ -1106,7 +1106,7 @@ bool function_volatile_property(char *fname, FUNC_VOLATILE_PROPERTY property) char query[1024]; char *rawstring = NULL; List *names = NIL; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; static POOL_RELCACHE *relcache; char prop_volatile; @@ -1211,7 +1211,7 @@ pool_table_name_to_oid(char *table_name) int oid = 0; static POOL_RELCACHE * relcache; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; char *query; if (table_name == NULL) @@ -1517,7 +1517,7 @@ bool function_has_return_type(char *fname, char *typename) char query[1024]; char *rawstring = NULL; List *names = NIL; - POOL_CONNECTION_POOL *backend; + BackendClusterConnection *backend; static POOL_RELCACHE *relcache; /* We need a modifiable copy of the input string. */ diff --git a/src/utils/pool_stream.c b/src/utils/pool_stream.c index b210f23f..e1c67970 100644 --- a/src/utils/pool_stream.c +++ b/src/utils/pool_stream.c @@ -112,14 +112,15 @@ pool_open(int fd, bool backend_connection) * close read/write file descriptors. */ void -pool_close(POOL_CONNECTION * cp) +pool_close(POOL_CONNECTION * cp, bool close_socket) { /* * shutdown connection to the client so that pgpool is not blocked */ if (!cp->isbackend) shutdown(cp->fd, 1); - close(cp->fd); + if (close_socket) + close(cp->fd); cp->socket_state = POOL_SOCKET_CLOSED; pfree(cp->wbuf); pfree(cp->hp); @@ -129,7 +130,7 @@ pool_close(POOL_CONNECTION * cp) pfree(cp->buf2); if (cp->buf3) pfree(cp->buf3); - pool_discard_params(&cp->params); + // pool_discard_params(&cp->params); pool_ssl_close(cp); @@ -225,9 +226,9 @@ pool_read(POOL_CONNECTION * cp, void *buf, int len) cp->socket_state = POOL_SOCKET_ERROR; if (cp->isbackend) { - if (cp->con_info && cp->con_info->swallow_termination == 1) + if (cp->pooled_backend_ref && cp->pooled_backend_ref->swallow_termination) { - cp->con_info->swallow_termination = 0; + cp->pooled_backend_ref->swallow_termination = false; ereport(FATAL, (errmsg("unable to read data from DB node %d", cp->db_node_id), errdetail("pg_terminate_backend was called on the backend"))); @@ -381,9 +382,9 @@ pool_read2(POOL_CONNECTION * cp, int len) cp->socket_state = POOL_SOCKET_ERROR; if (cp->isbackend) { - if (cp->con_info && cp->con_info->swallow_termination == 1) + if (cp->pooled_backend_ref && cp->pooled_backend_ref->swallow_termination) { - cp->con_info->swallow_termination = 0; + cp->pooled_backend_ref->swallow_termination = false; ereport(FATAL, (errmsg("unable to read data from DB node %d", cp->db_node_id), errdetail("pg_terminate_backend was called on the backend"))); @@ -738,9 +739,9 @@ pool_flush(POOL_CONNECTION * cp) { if (cp->isbackend) { - if (cp->con_info && cp->con_info->swallow_termination == 1) + if (cp->pooled_backend_ref && cp->pooled_backend_ref->swallow_termination) { - cp->con_info->swallow_termination = 0; + cp->pooled_backend_ref->swallow_termination = false; ereport(FATAL, (errmsg("unable to read data from DB node %d", cp->db_node_id), errdetail("pg_terminate_backend was called on the backend"))); @@ -794,9 +795,9 @@ pool_flush_noerror(POOL_CONNECTION * cp) { if (cp->isbackend) { - if (cp->con_info && cp->con_info->swallow_termination == 1) + if (cp->pooled_backend_ref && cp->pooled_backend_ref->swallow_termination) { - cp->con_info->swallow_termination = 0; + cp->pooled_backend_ref->swallow_termination = false; ereport(FATAL, (errmsg("unable to read data from DB node %d", cp->db_node_id), errdetail("pg_terminate_backend was called on the backend"))); @@ -973,9 +974,9 @@ pool_read_string(POOL_CONNECTION * cp, int *len, int line) cp->socket_state = POOL_SOCKET_ERROR; if (cp->isbackend) { - if (cp->con_info && cp->con_info->swallow_termination == 1) + if (cp->pooled_backend_ref && cp->pooled_backend_ref->swallow_termination) { - cp->con_info->swallow_termination = 0; + cp->pooled_backend_ref->swallow_termination = false; ereport(FATAL, (errmsg("unable to read data from DB node %d", cp->db_node_id), errdetail("pg_terminate_backend was called on the backend"))); diff --git a/src/utils/ps_status.c b/src/utils/ps_status.c index bedcfc6b..1f5a105a 100644 --- a/src/utils/ps_status.c +++ b/src/utils/ps_status.c @@ -392,12 +392,12 @@ get_ps_display(int *displen) * Show ps idle status */ void -pool_ps_idle_display(POOL_CONNECTION_POOL * backend) +pool_ps_idle_display(BackendClusterConnection * backend) { StartupPacket *sp; char psbuf[1024]; - sp = MAIN_CONNECTION(backend)->sp; + sp = backend->sp; if (MAIN(backend)->tstate == 'T') { snprintf(psbuf, sizeof(psbuf), "%s %s %s idle in transaction", diff --git a/src/watchdog/watchdog.c b/src/watchdog/watchdog.c index 41b53d26..2726464b 100644 --- a/src/watchdog/watchdog.c +++ b/src/watchdog/watchdog.c @@ -7547,6 +7547,8 @@ verify_pool_configurations(WatchdogNode * wdNode, POOL_CONFIG * config) WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, child_max_connections); WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, client_idle_limit); WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, max_pool); + WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, max_pool_size); + WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, connection_pool_type); WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, health_check_timeout); WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, health_check_period); WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_INT(config, wdNode, health_check_max_retries); diff --git a/src/watchdog/wd_json_data.c b/src/watchdog/wd_json_data.c index 474fc37a..b19d2f75 100644 --- a/src/watchdog/wd_json_data.c +++ b/src/watchdog/wd_json_data.c @@ -60,15 +60,19 @@ get_pool_config_from_json(char *json_data, int data_len) goto ERROR_EXIT; if (json_get_int_value_for_key(root, "max_pool", &config->max_pool)) goto ERROR_EXIT; + if (json_get_int_value_for_key(root, "max_pool_size", &config->max_pool_size)) + goto ERROR_EXIT; if (json_get_int_value_for_key(root, "num_init_children", &config->num_init_children)) goto ERROR_EXIT; if (json_get_int_value_for_key(root, "min_spare_children", &config->min_spare_children)) goto ERROR_EXIT; if (json_get_int_value_for_key(root, "max_spare_children", &config->max_spare_children)) goto ERROR_EXIT; - if (json_get_int_value_for_key(root, "process_management_mode", (int*)&config->process_management)) + if (json_get_int_value_for_key(root, "connection_pool_type", (int *)&config->connection_pool_type)) + goto ERROR_EXIT; + if (json_get_int_value_for_key(root, "process_management_mode", (int *)&config->process_management)) goto ERROR_EXIT; - if (json_get_int_value_for_key(root, "process_management_strategy", (int*)&config->process_management_strategy)) + if (json_get_int_value_for_key(root, "process_management_strategy", (int *)&config->process_management_strategy)) goto ERROR_EXIT; if (json_get_bool_value_for_key(root, "replication_mode", &config->replication_mode)) goto ERROR_EXIT; -- 2.39.3 (Apple Git-146)