]> granicus.if.org Git - pgbouncer/commitdiff
Detect DNS reply changes and invalidate connections to IPs no longer present in lates...
authorPetr Jelinek <petr.jelinek@skype.net>
Tue, 23 Aug 2011 19:22:53 +0000 (21:22 +0200)
committerPetr Jelinek <petr.jelinek@skype.net>
Tue, 23 Aug 2011 19:22:53 +0000 (21:22 +0200)
include/bouncer.h
include/objects.h
src/dnslookup.c
src/objects.c
src/util.c

index df84fa883023146056f47e532a34f2b352e7013c..2773fc8fc78e096c2fd334ec659daf44a5056849 100644 (file)
@@ -157,6 +157,7 @@ void pga_copy(PgAddr *a, const struct sockaddr *sa);
 bool pga_pton(PgAddr *a, const char *s, int port);
 const char *pga_ntop(const PgAddr *a, char *dst, int dstlen);
 const char *pga_str(const PgAddr *a, char *dst, int dstlen);
+int pga_cmp_addr(const PgAddr *a, const PgAddr *b);
 
 /*
  * Stats, kept per-pool.
index a5b9619e9c5b6150aebf78daefe3f95af38afe83..92e46f248cd255a77279e74b89fcf5934d701f81 100644 (file)
@@ -67,6 +67,7 @@ int get_active_client_count(void);
 int get_active_server_count(void);
 
 void tag_database_dirty(PgDatabase *db);
+void tag_host_addr_dirty(const char *host, const struct sockaddr *sa);
 void for_each_server(PgPool *pool, void (*func)(PgSocket *sk));
 
 void reuse_just_freed_objects(void);
index 2643fe539799f4708aa3dea7e1b8c85343a3bc72..b46248468907d2dac0d00b033c67960b0dd81ec2 100644 (file)
@@ -70,6 +70,7 @@ struct DNSRequest {
 
        struct addrinfo *result;
        struct addrinfo *current;
+       struct addrinfo *oldres;
 
        usec_t res_ttl;
 };
@@ -384,8 +385,11 @@ static int req_cmp(uintptr_t arg, struct AANode *node)
 static void req_reset(struct DNSRequest *req)
 {
        req->done = false;
-       if (req->result)
-               freeaddrinfo(req->result);
+       if (req->result) {
+               if (req->oldres)
+                       freeaddrinfo(req->oldres);
+               req->oldres = req->result;
+       }
        req->result = req->current = NULL;
 }
 
@@ -400,6 +404,10 @@ static void req_free(struct AANode *node, void *arg)
                free(ucb);
        }
        req_reset(req);
+       if (req->oldres) {
+               freeaddrinfo(req->oldres);
+               req->oldres = NULL;
+       }
        free(req->name);
        free(req);
 }
@@ -482,6 +490,36 @@ nomem:
        return NULL;
 }
 
+static int cmp_addrinfo(const struct addrinfo *a1, const struct addrinfo *a2)
+{
+    if (a1->ai_family != a2->ai_family)
+               return a1->ai_family-a2->ai_family;
+    if (a1->ai_addrlen != a2->ai_addrlen)
+               return a1->ai_addrlen-a2->ai_addrlen;
+
+    return memcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen);
+}
+
+/* check if new dns reply is missing some IP compared to old one */
+static void check_req_result_changes(struct DNSRequest *req)
+{
+       struct addrinfo *ai, *aj;
+
+       for (ai = req->oldres; ai; ai = ai->ai_next) {
+               bool found = false;
+               for (aj = req->result; aj; aj = aj->ai_next) {
+                       if (cmp_addrinfo(ai, aj) == 0) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               /* missing IP (possible DNS failover) make connections to it dirty */
+               if (!found)
+                       tag_host_addr_dirty(req->name, ai->ai_addr);
+       }
+}
+
 /* struct addrinfo -> deliver_info() */
 static void got_result_gai(int result, struct addrinfo *res, void *arg)
 {
@@ -492,6 +530,9 @@ static void got_result_gai(int result, struct addrinfo *res, void *arg)
        if (result == 0) {
                req->result = res;
                req->current = res;
+
+               if (req->oldres)
+                       check_req_result_changes(req);
        } else {
                /* lookup failed */
                log_warning("lookup failed: %s: result=%d", req->name, result);
index 15e6410c42d3ff5b25a1643dacc5d021f69b194e..86437b31f9aaa81c69f08d3388af3fd91fcbc045 100644 (file)
@@ -1246,11 +1246,49 @@ void for_each_server(PgPool *pool, void (*func)(PgSocket *sk))
                func(container_of(item, PgSocket, head));
 }
 
+static void for_each_server_filtered(PgPool *pool, void (*func)(PgSocket *sk), bool (*filter)(PgSocket *sk, void *arg), void *filter_arg)
+{
+       struct List *item;
+       PgSocket *sk;
+
+       statlist_for_each(item, &pool->idle_server_list) {
+               sk = container_of(item, PgSocket, head);
+               if (filter(sk, filter_arg))
+                       func(sk);
+       }
+
+       statlist_for_each(item, &pool->used_server_list) {
+               sk = container_of(item, PgSocket, head);
+               if (filter(sk, filter_arg))
+                       func(sk);
+       }
+
+       statlist_for_each(item, &pool->tested_server_list) {
+               sk = container_of(item, PgSocket, head);
+               if (filter(sk, filter_arg))
+                       func(sk);
+       }
+
+       statlist_for_each(item, &pool->active_server_list) {
+               sk = container_of(item, PgSocket, head);
+               if (filter(sk, filter_arg))
+                       func(sk);
+       }
+
+       statlist_for_each(item, &pool->new_server_list) {
+               sk = container_of(item, PgSocket, head);
+               if (filter(sk, filter_arg))
+                       func(sk);
+       }
+}
+
+
 static void tag_dirty(PgSocket *sk)
 {
        sk->close_needed = 1;
 }
 
+
 void tag_database_dirty(PgDatabase *db)
 {
        struct List *item;
@@ -1263,6 +1301,30 @@ void tag_database_dirty(PgDatabase *db)
        }
 }
 
+static bool server_remote_addr_filter(PgSocket *sk, void *arg) {
+       PgAddr *addr = arg;
+
+       return (pga_cmp_addr(&sk->remote_addr, addr) == 0);
+}
+
+void tag_host_addr_dirty(const char *host, const struct sockaddr *sa)
+{
+       struct List *item;
+       PgPool *pool;
+       PgAddr addr;
+
+       memset(&addr, 0, sizeof(addr));
+       pga_copy(&addr, sa);
+
+       statlist_for_each(item, &pool_list) {
+               pool = container_of(item, PgPool, head);
+               if (pool->db->host && strcmp(host, pool->db->host) == 0) {
+                       for_each_server_filtered(pool, tag_dirty, server_remote_addr_filter, &addr);
+               }
+       }
+}
+
+
 /* move objects from justfree_* to free_* lists */
 void reuse_just_freed_objects(void)
 {
index afa968397884bfbb7e6af152591a508e398ec5c6..f5e93cc31d8a64a0c01e361d7d5e34b6a215aae0 100644 (file)
@@ -346,6 +346,24 @@ static inline unsigned pga_family(const PgAddr *a)
        return a->sa.sa_family;
 }
 
+int pga_cmp_addr(const PgAddr *a, const PgAddr *b)
+{
+    if (pga_family(a) != pga_family(b))
+               return pga_family(a)-pga_family(b);
+
+       switch (pga_family(a)) {
+       case AF_INET:
+               return memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(a->sin.sin_addr));
+               break;
+       case AF_INET6:
+               return memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(a->sin6.sin6_addr));
+               break;
+       default:
+               log_error("pga_cmp_addr: unsupported family");
+               return 0;
+       }
+}
+
 /* convert pgaddr to string */
 const char *pga_ntop(const PgAddr *a, char *dst, int dstlen)
 {