=item B<ReportStats> B<true>|B<false>
The network plugin cannot only receive and send statistics, it can also create
-statistics about itself. Collected data included the number of received and
+statistics about itself. Collectd data included the number of received and
sent octets and packets, the length of the receive queue and the number of
values handled. When set to B<true>, the I<Network plugin> will make these
statistics available. Defaults to B<false>.
For example B<name uuid> means to concatenate the guest name and UUID
(with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
+=item B<Instances> B<integer>
+
+How many read instances you want to use for this plugin. The default is one,
+and the sensible setting is a multiple of the B<ReadThreads> value.
+If you are not sure, just use the default setting.
+
=back
=head2 Plugin C<vmem>
Synopsis:
<Plugin write_tsdb>
+ ResolveInterval 60
+ ResolveJitter 60
<Node "example">
Host "tsd-1.my.domain"
Port "4242"
</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<ResolveInterval> I<seconds>
+
+ =item B<ResolveJitter> I<seconds>
+
+ When I<collectd> connects to a TSDB node, it will request the hostname from
+ DNS. This can become a problem if the TSDB node is unavailable or badly
+ configured because collectd will request DNS in order to reconnect for every
+ metric, which can flood your DNS. So you can cache the last value for
+ I<ResolveInterval> seconds.
+ Defaults to the I<Interval> of the I<write_tsdb plugin>, e.g. 10E<nbsp>seconds.
+
+ You can also define a jitter, a random interval to wait in addition to
+ I<ResolveInterval>. This prevents all your collectd servers to resolve the
+ hostname at the same time when the connection fails.
+ Defaults to the I<Interval> of the I<write_tsdb plugin>, e.g. 10E<nbsp>seconds.
+
+ B<Note:> If the DNS resolution has already been successful when the socket
+ closes, the plugin will try to reconnect immediately with the cached
+ information. DNS is queried only when the socket is closed for a longer than
+ I<ResolveInterval> + I<ResolveJitter> seconds.
+
+ =back
+
+ Inside the B<Node> blocks, the following options are recognized:
=over 4
#include "common.h"
#include "plugin.h"
-
#include "utils_cache.h"
+ #include "utils_random.h"
#include <netdb.h>
* Private variables
*/
struct wt_callback {
+ struct addrinfo *ai;
+ cdtime_t ai_last_update;
int sock_fd;
char *node;
cdtime_t send_buf_init_time;
pthread_mutex_t send_lock;
+
+ _Bool connect_failed_log_enabled;
+ int connect_dns_failed_attempts_remaining;
+ cdtime_t next_random_ttl;
};
+ static cdtime_t resolve_interval = 0;
+ static cdtime_t resolve_jitter = 0;
+
/*
* Functions
*/
return status;
}
+ static cdtime_t new_random_ttl() {
+ if (resolve_jitter == 0)
+ return 0;
+
+ return (cdtime_t)cdrand_range(0, (long)resolve_jitter);
+ }
+
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;
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->ai) {
+ /* When we are here, we still have the IP in cache.
+ * If we have remaining attempts without calling the DNS, we update the
+ * last_update date so we keep the info until next time.
+ * If there is no more attempts, we need to flush the cache.
+ */
+
+ if ((cb->ai_last_update + resolve_interval + cb->next_random_ttl) < now) {
+ cb->next_random_ttl = new_random_ttl();
+ if (cb->connect_dns_failed_attempts_remaining > 0) {
+ /* Warning : this is run under send_lock mutex.
+ * This is why we do not use another mutex here.
+ * */
+ cb->ai_last_update = now;
+ cb->connect_dns_failed_attempts_remaining--;
+ } else {
+ freeaddrinfo(cb->ai);
+ cb->ai = 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 (cb->ai == NULL) {
+ if ((cb->ai_last_update + resolve_interval + cb->next_random_ttl) >= now) {
+ DEBUG("write_tsdb plugin: too many getaddrinfo(%s, %s) failures", node,
+ service);
+ return (-1);
+ }
+ cb->ai_last_update = now;
+ cb->next_random_ttl = new_random_ttl();
+
+ struct addrinfo ai_hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_ADDRCONFIG,
+ .ai_socktype = SOCK_STREAM,
+ };
+
+ status = getaddrinfo(node, service, &ai_hints, &cb->ai);
+ if (status != 0) {
+ if (cb->ai) {
+ freeaddrinfo(cb->ai);
+ cb->ai = 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;
- ai_ptr = ai_ptr->ai_next) {
- cb->sock_fd =
- socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+ assert(cb->ai != NULL);
+ for (struct addrinfo *ai = cb->ai; ai != NULL; ai = ai->ai_next) {
+ cb->sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (cb->sock_fd < 0)
continue;
set_sock_opts(cb->sock_fd);
- status = connect(cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ status = connect(cb->sock_fd, ai->ai_addr, ai->ai_addrlen);
if (status != 0) {
close(cb->sock_fd);
cb->sock_fd = -1;
break;
}
- freeaddrinfo(ai_list);
-
if (cb->sock_fd < 0) {
char errbuf[1024];
ERROR("write_tsdb plugin: Connecting to %s:%s failed. "
return -1;
}
+ if (0 == cb->connect_failed_log_enabled) {
+ WARNING("write_tsdb plugin: Connecting to %s:%s succeeded.", node, service);
+ cb->connect_failed_log_enabled = 1;
+ }
+ cb->connect_dns_failed_attempts_remaining = 1;
+
wt_reset_buffer(cb);
return 0;
return -1;
}
cb->sock_fd = -1;
- cb->node = NULL;
- cb->service = NULL;
- cb->host_tags = NULL;
- cb->store_rates = 0;
+ cb->connect_failed_log_enabled = 1;
+ cb->next_random_ttl = new_random_ttl();
pthread_mutex_init(&cb->send_lock, NULL);
}
static int wt_config(oconfig_item_t *ci) {
+ if ((resolve_interval == 0) && (resolve_jitter == 0))
+ resolve_interval = resolve_jitter = plugin_get_interval();
+
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
if (strcasecmp("Node", child->key) == 0)
wt_config_tsd(child);
+ else if (strcasecmp("ResolveInterval", child->key) == 0)
+ cf_util_get_cdtime(child, &resolve_interval);
+ else if (strcasecmp("ResolveJitter", child->key) == 0)
+ cf_util_get_cdtime(child, &resolve_jitter);
else {
ERROR("write_tsdb plugin: Invalid configuration "
"option: %s.",
void module_register(void) {
plugin_register_complex_config("write_tsdb", wt_config);
}
-
-/* vim: set sw=4 ts=4 sts=4 tw=78 et : */