Prevent DNS flood when TSDB is not available
authorYves Mettier <ymettier@free.fr>
Wed, 14 Oct 2015 14:32:56 +0000 (15:32 +0100)
committerFlorian Forster <octo@collectd.org>
Tue, 29 Nov 2016 07:53:20 +0000 (08:53 +0100)
src/collectd.conf.pod
src/write_tsdb.c

index 27c4e16..4d05352 100644 (file)
@@ -8143,6 +8143,7 @@ packets.
 Synopsis:
 
  <Plugin write_tsdb>
+   DNS_Cache_TTL 60
    <Node "example">
      Host "tsd-1.my.domain"
      Port "4242"
@@ -8151,7 +8152,22 @@ Synopsis:
  </Plugin>
 
 The configuration consists of one or more E<lt>B<Node>E<nbsp>I<Name>E<gt>
-blocks. Inside the B<Node> blocks, the following options are recognized:
+blocks and global directives.
+
+Global directives are:
+
+=over 4
+
+=item B<DNS_Cache_TTL> I<ttl>
+
+When Collectd connects to a TSDB node, it will request the DNS. This can become
+a problem is the TSDN node is unavailable or badly configured because Collected
+will request DNS in order to reconnect for every metric, which can flood your DNS.
+So you can cache the last value for C<ttl> seconds (default: 60s).
+
+=back
+
+Inside the B<Node> blocks, the following options are recognized:
 
 =over 4
 
index 0c87c47..d1d65ca 100644 (file)
@@ -71,6 +71,8 @@
  * Private variables
  */
 struct wt_callback {
+  struct addrinfo *sock_info;
+  cdtime_t sock_info_last_update;
   int sock_fd;
 
   char *node;
@@ -88,6 +90,8 @@ struct wt_callback {
   pthread_mutex_t send_lock;
 };
 
+static cdtime_t dnsttl = TIME_T_TO_CDTIME_T_STATIC(60);
+
 /*
  * Functions
  */
@@ -145,8 +149,8 @@ static int wt_flush_nolock(cdtime_t timeout, struct wt_callback *cb) {
 }
 
 static int wt_callback_init(struct wt_callback *cb) {
-  struct addrinfo *ai_list;
   int status;
+  cdtime_t now;
 
   const char *node = cb->node ? cb->node : WT_DEFAULT_NODE;
   const char *service = cb->service ? cb->service : WT_DEFAULT_SERVICE;
@@ -154,19 +158,45 @@ static int wt_callback_init(struct wt_callback *cb) {
   if (cb->sock_fd > 0)
     return 0;
 
-  struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
-                              .ai_flags = AI_ADDRCONFIG,
-                              .ai_socktype = SOCK_STREAM};
+  now = cdtime();
+  if ((cb->sock_info_last_update + dnsttl) < now) {
+    if (cb->sock_info) {
+      freeaddrinfo(cb->sock_info);
+      cb->sock_info = NULL;
+    }
+  }
 
-  status = getaddrinfo(node, service, &ai_hints, &ai_list);
-  if (status != 0) {
-    ERROR("write_tsdb plugin: getaddrinfo (%s, %s) failed: %s", node, service,
-          gai_strerror(status));
-    return -1;
+  if (NULL == cb->sock_info) {
+    struct addrinfo ai_hints = {
+        .ai_family = AF_UNSPEC,
+        .ai_flags = AI_ADDRCONFIG,
+        .ai_socktype = SOCK_STREAM,
+    };
+
+    if ((cb->sock_info_last_update + dnsttl) >= now) {
+      DEBUG("write_tsdb plugin: too many getaddrinfo (%s, %s) failures", node,
+            service);
+      return (-1);
+    }
+
+    cb->sock_info_last_update = now;
+    status = getaddrinfo(node, service, &ai_hints, &(cb->sock_info));
+    if (status != 0) {
+      if (cb->sock_info) {
+        freeaddrinfo(cb->sock_info);
+        cb->sock_info = NULL;
+      }
+      if (cb->connect_failed_log_enabled) {
+        ERROR("write_tsdb plugin: getaddrinfo (%s, %s) failed: %s", node,
+              service, gai_strerror(status));
+        cb->connect_failed_log_enabled = 0;
+      }
+      return -1;
+    }
   }
 
-  assert(ai_list != NULL);
-  for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
+  assert(cb->sock_info != NULL);
+  for (struct addrinfo *ai_ptr = cb->sock_info; ai_ptr != NULL;
        ai_ptr = ai_ptr->ai_next) {
     cb->sock_fd =
         socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
@@ -185,8 +215,6 @@ static int wt_callback_init(struct wt_callback *cb) {
     break;
   }
 
-  freeaddrinfo(ai_list);
-
   if (cb->sock_fd < 0) {
     char errbuf[1024];
     ERROR("write_tsdb plugin: Connecting to %s:%s failed. "
@@ -522,10 +550,6 @@ static int wt_config_tsd(oconfig_item_t *ci) {
     return -1;
   }
   cb->sock_fd = -1;
-  cb->node = NULL;
-  cb->service = NULL;
-  cb->host_tags = NULL;
-  cb->store_rates = 0;
 
   pthread_mutex_init(&cb->send_lock, NULL);
 
@@ -569,7 +593,11 @@ static int wt_config(oconfig_item_t *ci) {
 
     if (strcasecmp("Node", child->key) == 0)
       wt_config_tsd(child);
-    else {
+    if (strcasecmp("DNS_Cache_TTL", child->key) == 0) {
+      int ttl;
+      cf_util_get_int(child, &ttl);
+      dnsttl = TIME_T_TO_CDTIME_T(ttl);
+    } else {
       ERROR("write_tsdb plugin: Invalid configuration "
             "option: %s.",
             child->key);