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.
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);
struct addrinfo *result;
struct addrinfo *current;
+ struct addrinfo *oldres;
usec_t res_ttl;
};
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;
}
free(ucb);
}
req_reset(req);
+ if (req->oldres) {
+ freeaddrinfo(req->oldres);
+ req->oldres = NULL;
+ }
free(req->name);
free(req);
}
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)
{
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);
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;
}
}
+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)
{
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)
{