3 * $Header: /cvsroot/pgpool/pgpool-II/pool_connection_pool.c,v 1.13.2.3 2009/08/22 04:19:49 t-ishii Exp $
5 * pgpool: a language independent connection pool server for PostgreSQL
6 * written by Tatsuo Ishii
8 * Copyright (c) 2003-2009 PgPool Global Development Group
10 * Permission to use, copy, modify, and distribute this software and
11 * its documentation for any purpose and without fee is hereby
12 * granted, provided that the above copyright notice appear in all
13 * copies and that both that copyright notice and this permission
14 * notice appear in supporting documentation, and that the name of the
15 * author not be used in advertising or publicity pertaining to
16 * distribution of the software without specific, written prior
17 * permission. The author makes no representations about the
18 * suitability of this software for any purpose. It is provided "as
19 * is" without express or implied warranty.
21 * poo_connection_pool.c: connection pool stuff
25 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
30 #ifdef HAVE_SYS_SELECT_H
31 #include <sys/select.h>
33 #ifdef HAVE_NETINET_TCP_H
34 #include <netinet/tcp.h>
47 POOL_CONNECTION_POOL *pool_connection_pool; /* connection pool */
48 volatile sig_atomic_t backend_timer_expired = 0; /* flag for connection closed timer is expired */
50 static POOL_CONNECTION_POOL_SLOT *create_cp(POOL_CONNECTION_POOL_SLOT *cp, int slot);
51 static POOL_CONNECTION_POOL *new_connection(POOL_CONNECTION_POOL *p);
52 static int check_socket_status(int fd);
55 * initialize connection pools. this should be called once at the startup.
57 int pool_init_cp(void)
61 pool_connection_pool = (POOL_CONNECTION_POOL *)malloc(sizeof(POOL_CONNECTION_POOL)*pool_config->max_pool);
62 if (pool_connection_pool == NULL)
64 pool_error("pool_init_cp: malloc() failed");
67 memset(pool_connection_pool, 0, sizeof(POOL_CONNECTION_POOL)*pool_config->max_pool);
69 for (i = 0; i < pool_config->max_pool; i++)
71 pool_connection_pool[i].info = &(MY_PROCESS_INFO.connection_info[i]);
72 memset(pool_connection_pool[i].info, 0, sizeof(ConnectionInfo));
78 * find connection by user and database
80 POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int protoMajor, int check_socket)
82 #ifdef HAVE_SIGPROCMASK
91 POOL_CONNECTION_POOL *p = pool_connection_pool;
95 pool_error("pool_get_cp: pool_connection_pool is not initialized");
99 POOL_SETMASK2(&BlockSig, &oldmask);
101 for (i=0;i<pool_config->max_pool;i++)
103 if (MASTER_CONNECTION(p) &&
104 MASTER_CONNECTION(p)->sp &&
105 MASTER_CONNECTION(p)->sp->major == protoMajor &&
106 MASTER_CONNECTION(p)->sp->user != NULL &&
107 strcmp(MASTER_CONNECTION(p)->sp->user, user) == 0 &&
108 strcmp(MASTER_CONNECTION(p)->sp->database, database) == 0)
112 /* mark this connection is under use */
113 MASTER_CONNECTION(p)->closetime = 0;
115 POOL_SETMASK(&oldmask);
119 for (j=0;j<NUM_BACKENDS;j++)
121 if (!VALID_BACKEND(j))
124 if (CONNECTION_SLOT(p, j))
126 sock_broken = check_socket_status(CONNECTION(p, j)->fd);
139 pool_log("connection closed. retry to create new connection pool.");
140 for (j=0;j<NUM_BACKENDS;j++)
142 if (!VALID_BACKEND(j) || (CONNECTION_SLOT(p, j) == NULL))
147 pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp);
151 pool_close(CONNECTION(p, j));
152 free(CONNECTION_SLOT(p, j));
155 memset(p, 0, sizeof(POOL_CONNECTION_POOL_SLOT));
157 memset(p->info, 0, sizeof(ConnectionInfo));
158 POOL_SETMASK(&oldmask);
162 POOL_SETMASK(&oldmask);
168 POOL_SETMASK(&oldmask);
173 * disconnect and release a connection to the database
175 void pool_discard_cp(char *user, char *database, int protoMajor)
177 POOL_CONNECTION_POOL *p = pool_get_cp(user, database, protoMajor, 0);
178 ConnectionInfo *info;
183 pool_error("pool_discard_cp: cannot get connection pool for user %s datbase %s", user, database);
187 for (i=0;i<NUM_BACKENDS;i++)
189 if (!VALID_BACKEND(i))
194 pool_free_startup_packet(CONNECTION_SLOT(p, i)->sp);
197 pool_close(CONNECTION(p, i));
198 free(CONNECTION_SLOT(p, i));
202 memset(p, 0, sizeof(POOL_CONNECTION_POOL));
204 memset(p->info, 0, sizeof(ConnectionInfo));
209 * create a connection pool by user and database
211 POOL_CONNECTION_POOL *pool_create_cp(void)
215 POOL_CONNECTION_POOL *oldestp;
216 ConnectionInfo *info;
218 POOL_CONNECTION_POOL *p = pool_connection_pool;
222 pool_error("pool_create_cp: pool_connection_pool is not initialized");
226 for (i=0;i<pool_config->max_pool;i++)
228 if (MASTER_CONNECTION(p) == NULL)
229 return new_connection(p);
233 pool_debug("no empty connection slot was found");
236 * no empty connection slot was found. look for the oldest connection and discard it.
238 oldestp = p = pool_connection_pool;
239 closetime = MASTER_CONNECTION(p)->closetime;
240 for (i=0;i<pool_config->max_pool;i++)
242 pool_debug("user: %s database: %s closetime: %ld",
243 MASTER_CONNECTION(p)->sp->user,
244 MASTER_CONNECTION(p)->sp->database,
245 MASTER_CONNECTION(p)->closetime);
246 if (MASTER_CONNECTION(p)->closetime < closetime)
248 closetime = MASTER_CONNECTION(p)->closetime;
255 pool_send_frontend_exits(p);
257 pool_debug("discarding old %d th connection. user: %s database: %s",
258 oldestp - pool_connection_pool,
259 MASTER_CONNECTION(p)->sp->user,
260 MASTER_CONNECTION(p)->sp->database);
262 for (i=0;i<NUM_BACKENDS;i++)
264 if (!VALID_BACKEND(i))
269 pool_free_startup_packet(CONNECTION_SLOT(p, i)->sp);
273 pool_close(CONNECTION(p, i));
274 free(CONNECTION_SLOT(p, i));
278 memset(p, 0, sizeof(POOL_CONNECTION_POOL));
280 memset(p->info, 0, sizeof(ConnectionInfo));
282 return new_connection(p);
286 * set backend connection close timer
288 void pool_connection_pool_timer(POOL_CONNECTION_POOL *backend)
290 POOL_CONNECTION_POOL *p = pool_connection_pool;
293 pool_debug("pool_connection_pool_timer: set close time %ld", time(NULL));
295 MASTER_CONNECTION(backend)->closetime = time(NULL); /* set connection close time */
297 if (pool_config->connection_life_time == 0)
300 /* look for any other timeout */
301 for (i=0;i<pool_config->max_pool;i++, p++)
303 if (!MASTER_CONNECTION(p))
305 if (!MASTER_CONNECTION(p)->sp)
307 if (MASTER_CONNECTION(p)->sp->user == NULL)
310 if (p != backend && MASTER_CONNECTION(p)->closetime)
314 /* no other timer found. set my timer */
315 pool_debug("pool_connection_pool_timer: set alarm after %d seconds", pool_config->connection_life_time);
316 pool_signal(SIGALRM, pool_backend_timer_handler);
317 alarm(pool_config->connection_life_time);
321 * backend connection close timer handler
323 RETSIGTYPE pool_backend_timer_handler(int sig)
325 backend_timer_expired = 1;
328 void pool_backend_timer(void)
330 #define TMINTMAX 0x7fffffff
332 POOL_CONNECTION_POOL *p = pool_connection_pool;
335 time_t nearest = TMINTMAX;
336 ConnectionInfo *info;
338 POOL_SETMASK(&BlockSig);
342 pool_debug("pool_backend_timer_handler called at %ld", now);
344 for (i=0;i<pool_config->max_pool;i++, p++)
346 if (!MASTER_CONNECTION(p))
348 if (!MASTER_CONNECTION(p)->sp)
350 if (MASTER_CONNECTION(p)->sp->user == NULL)
354 if (MASTER_CONNECTION(p)->closetime)
358 pool_debug("pool_backend_timer_handler: expire time: %ld",
359 MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time);
361 if (now >= (MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time))
363 /* discard expired connection */
364 pool_debug("pool_backend_timer_handler: expires user %s database %s",
365 MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database);
367 pool_send_frontend_exits(p);
369 for (j=0;j<NUM_BACKENDS;j++)
371 if (!VALID_BACKEND(j))
376 pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp);
380 pool_close(CONNECTION(p, j));
381 free(CONNECTION_SLOT(p, j));
384 memset(p, 0, sizeof(POOL_CONNECTION_POOL));
386 memset(p->info, 0, sizeof(ConnectionInfo));
390 /* look for nearest timer */
391 if (MASTER_CONNECTION(p)->closetime < nearest)
392 nearest = MASTER_CONNECTION(p)->closetime;
397 /* any remaining timer */
398 if (nearest != TMINTMAX)
400 nearest = pool_config->connection_life_time - (now - nearest);
403 pool_signal(SIGALRM, pool_backend_timer_handler);
407 POOL_SETMASK(&UnBlockSig);
411 * connect to postmaster through INET domain socket
413 int connect_inet_domain_socket(int slot)
418 host = pool_config->backend_desc->backend_info[slot].backend_hostname;
419 port = pool_config->backend_desc->backend_info[slot].backend_port;
421 return connect_inet_domain_socket_by_port(host, port);
425 * connect to postmaster through UNIX domain socket
427 int connect_unix_domain_socket(int slot)
432 port = pool_config->backend_desc->backend_info[slot].backend_port;
433 socket_dir = pool_config->backend_socket_dir;
435 return connect_unix_domain_socket_by_port(port, socket_dir);
438 int connect_unix_domain_socket_by_port(int port, char *socket_dir)
440 struct sockaddr_un addr;
444 fd = socket(AF_UNIX, SOCK_STREAM, 0);
447 pool_error("connect_unix_domain_socket_by_port: setsockopt() failed: %s", strerror(errno));
451 memset((char *) &addr, 0, sizeof(addr));
452 ((struct sockaddr *)&addr)->sa_family = AF_UNIX;
453 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.s.PGSQL.%d", socket_dir, port);
454 len = sizeof(struct sockaddr_un);
458 if (connect(fd, (struct sockaddr *)&addr, len) < 0)
460 if (errno == EINTR || errno == EAGAIN)
463 pool_error("connect_unix_domain_socket_by_port: connect() failed: %s", strerror(errno));
473 int connect_inet_domain_socket_by_port(char *host, int port)
478 struct sockaddr_in addr;
481 fd = socket(AF_INET, SOCK_STREAM, 0);
484 pool_error("connect_inet_domain_socket_by_port: socket() failed: %s", strerror(errno));
489 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
493 pool_error("connect_inet_domain_socket_by_port: setsockopt() failed: %s", strerror(errno));
498 memset((char *) &addr, 0, sizeof(addr));
499 ((struct sockaddr *)&addr)->sa_family = AF_INET;
501 addr.sin_port = htons(port);
502 len = sizeof(struct sockaddr_in);
504 hp = gethostbyname(host);
505 if ((hp == NULL) || (hp->h_addrtype != AF_INET))
507 pool_error("connect_inet_domain_socket: gethostbyname() failed: %s host: %s", strerror(errno), host);
511 memmove((char *) &(addr.sin_addr),
517 if (connect(fd, (struct sockaddr *)&addr, len) < 0)
519 if (errno == EINTR || errno == EAGAIN)
522 pool_error("connect_inet_domain_socket: connect() failed: %s",strerror(errno));
533 * create connection pool
535 static POOL_CONNECTION_POOL_SLOT *create_cp(POOL_CONNECTION_POOL_SLOT *cp, int slot)
537 BackendInfo *b = &pool_config->backend_desc->backend_info[slot];
540 if (*b->backend_hostname == '\0')
542 fd = connect_unix_domain_socket(slot);
546 fd = connect_inet_domain_socket(slot);
551 pool_error("connection to %s(%d) failed", b->backend_hostname, b->backend_port);
555 cp->con = pool_open(fd);
561 * create actual connections to backends
563 static POOL_CONNECTION_POOL *new_connection(POOL_CONNECTION_POOL *p)
565 POOL_CONNECTION_POOL_SLOT *s;
566 int active_backend_count = 0;
569 for (i=0;i<NUM_BACKENDS;i++)
571 pool_debug("new_connection: connecting %d backend", i);
573 if (!VALID_BACKEND(i))
575 pool_debug("new_connection: skipping slot %d because backend_status = %d",
576 i, BACKEND_INFO(i).backend_status);
580 s = malloc(sizeof(POOL_CONNECTION_POOL_SLOT));
583 pool_error("new_connection: malloc() failed");
587 if (create_cp(s, i) == NULL)
589 /* connection failed. mark this backend down */
590 pool_error("new_connection: create_cp() failed");
592 /* send failover request to parent if operated in pgpool-I mode */
593 /* notice_backend_error() returns immediately if in pgpool-II */
594 notice_backend_error(i);
600 if (pool_init_params(&s->con->params))
605 BACKEND_INFO(i).backend_status = CON_UP;
606 active_backend_count++;
609 if (active_backend_count > 0)
611 p->info->create_time = time(NULL);
618 /* check_socket_status()
620 * -1 => broken socket.
622 static int check_socket_status(int fd)
633 t.tv_sec = t.tv_usec = 0;
635 result = select(fd+1, &rfds, NULL, NULL, &t);
636 if (result < 0 && errno == EINTR)
642 return (result == 0 ? 0 : -1);