Code

Add support for multiple metric data-stores.
[sysdb.git] / src / core / memstore.c
index d886a1fb4c131f4d094ba2cbda7f4e0e61bfc30e..87f9512745446d0f079ca6792fb12ce40168b1ca 100644 (file)
@@ -42,6 +42,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 
 #include <pthread.h>
 
@@ -65,10 +66,11 @@ typedef struct {
        int type;
        const char *name;
        sdb_time_t last_update;
+       sdb_time_t interval;
        const char * const *backends;
        size_t backends_num;
 } store_obj_t;
-#define STORE_OBJ_INIT { NULL, NULL, 0, NULL, 0, NULL, 0 }
+#define STORE_OBJ_INIT { NULL, NULL, 0, NULL, 0, 0, NULL, 0 }
 
 static sdb_type_t host_type;
 static sdb_type_t service_type;
@@ -109,14 +111,7 @@ static int
 store_obj_init(sdb_object_t *obj, va_list ap)
 {
        sdb_memstore_obj_t *sobj = STORE_OBJ(obj);
-
        sobj->type = va_arg(ap, int);
-
-       sobj->last_update = va_arg(ap, sdb_time_t);
-       sobj->interval = 0;
-       sobj->backends = NULL;
-       sobj->backends_num = 0;
-       sobj->parent = NULL;
        return 0;
 } /* store_obj_init */
 
@@ -221,7 +216,8 @@ metric_init(sdb_object_t *obj, va_list ap)
        if (! sobj->attributes)
                return -1;
 
-       sobj->store.type = sobj->store.id = NULL;
+       sobj->stores = NULL;
+       sobj->stores_num = 0;
        return 0;
 } /* metric_init */
 
@@ -229,17 +225,24 @@ static void
 metric_destroy(sdb_object_t *obj)
 {
        metric_t *sobj = METRIC(obj);
-       assert(obj);
+       size_t i;
 
+       assert(obj);
        store_obj_destroy(obj);
 
        if (sobj->attributes)
                sdb_avltree_destroy(sobj->attributes);
 
-       if (sobj->store.type)
-               free(sobj->store.type);
-       if (sobj->store.id)
-               free(sobj->store.id);
+       for (i = 0; i < sobj->stores_num; ++i) {
+               if (sobj->stores[i].type)
+                       free(sobj->stores[i].type);
+               if (sobj->stores[i].id)
+                       free(sobj->stores[i].id);
+       }
+       if (sobj->stores)
+               free(sobj->stores);
+       sobj->stores = NULL;
+       sobj->stores_num = 0;
 } /* metric_destroy */
 
 static int
@@ -248,8 +251,7 @@ attr_init(sdb_object_t *obj, va_list ap)
        const sdb_data_t *value;
        int ret;
 
-       /* this will consume the first two arguments
-        * (type and last_update) of ap */
+       /* this will consume the first argument (type) of ap */
        ret = store_obj_init(obj, ap);
        if (ret)
                return ret;
@@ -347,37 +349,8 @@ store_obj(store_obj_t *obj, sdb_memstore_obj_t **updated_obj)
 
        assert(obj->parent_tree);
 
-       if (obj->last_update <= 0)
-               obj->last_update = sdb_gettime();
-
        old = STORE_OBJ(sdb_avltree_lookup(obj->parent_tree, obj->name));
        if (old) {
-               if (old->last_update > obj->last_update) {
-                       sdb_log(SDB_LOG_DEBUG, "memstore: Cannot update %s '%s' - "
-                                       "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
-                                       SDB_STORE_TYPE_TO_NAME(obj->type), obj->name,
-                                       obj->last_update, old->last_update);
-                       /* don't report an error; the object may be updated by multiple
-                        * backends */
-                       status = 1;
-               }
-               else if (old->last_update == obj->last_update) {
-                       /* don't report an error and also don't even log this to avoid
-                        * excessive noise on high sampling frequencies */
-                       status = 1;
-               }
-               else {
-                       sdb_time_t interval = obj->last_update - old->last_update;
-                       old->last_update = obj->last_update;
-                       if (interval) {
-                               if (old->interval)
-                                       old->interval = (sdb_time_t)((0.9 * (double)old->interval)
-                                                       + (0.1 * (double)interval));
-                               else
-                                       old->interval = interval;
-                       }
-               }
-
                new = old;
                sdb_object_deref(SDB_OBJ(old));
        }
@@ -385,7 +358,7 @@ store_obj(store_obj_t *obj, sdb_memstore_obj_t **updated_obj)
                if (obj->type == SDB_ATTRIBUTE) {
                        /* the value will be updated by the caller */
                        new = STORE_OBJ(sdb_object_create(obj->name, attribute_type,
-                                               obj->type, obj->last_update, NULL));
+                                               obj->type, NULL));
                }
                else {
                        sdb_type_t t;
@@ -394,8 +367,7 @@ store_obj(store_obj_t *obj, sdb_memstore_obj_t **updated_obj)
                                : obj->type == SDB_SERVICE
                                        ? service_type
                                        : metric_type;
-                       new = STORE_OBJ(sdb_object_create(obj->name, t,
-                                               obj->type, obj->last_update));
+                       new = STORE_OBJ(sdb_object_create(obj->name, t, obj->type));
                }
 
                if (new) {
@@ -417,6 +389,9 @@ store_obj(store_obj_t *obj, sdb_memstore_obj_t **updated_obj)
                return status;
        assert(new);
 
+       new->last_update = obj->last_update;
+       new->interval = obj->interval;
+
        if (new->parent != obj->parent) {
                // Avoid circular self-references which are not handled
                // correctly by the ref-count based management layer.
@@ -434,35 +409,82 @@ store_obj(store_obj_t *obj, sdb_memstore_obj_t **updated_obj)
 } /* store_obj */
 
 static int
-store_metric_store(metric_t *metric, sdb_store_metric_t *m)
+store_metric_update_store(metric_store_t *store,
+               const sdb_metric_store_t __attribute__((unused)) *s,
+               sdb_time_t last_update)
+{
+       if (last_update <= store->last_update)
+               return 0;
+       store->last_update = last_update;
+       return 0;
+} /* store_metric_update_store */
+
+static int
+store_metric_add_store(metric_t *metric, const sdb_metric_store_t *s,
+               sdb_time_t last_update)
 {
-       char *type = metric->store.type;
-       char *id = metric->store.id;
+       char *type = strdup(s->type);
+       char *id = strdup(s->id);
 
-       if ((! metric->store.type) || strcasecmp(metric->store.type, m->store.type)) {
-               if (! (type = strdup(m->store.type)))
-                       return -1;
-       }
-       if ((! metric->store.id) || strcasecmp(metric->store.id, m->store.id)) {
-               if (! (id = strdup(m->store.id))) {
-                       if (type != metric->store.type)
-                               free(type);
-                       return -1;
-               }
+       metric_store_t *new;
+
+       if ((! type) || (! id)) {
+               if (type)
+                       free(type);
+               if (id)
+                       free(id);
+               return -1;
        }
 
-       if (type != metric->store.type) {
-               if (metric->store.type)
-                       free(metric->store.type);
-               metric->store.type = type;
+       new = realloc(metric->stores,
+                       (metric->stores_num + 1) * sizeof(*metric->stores));
+       if (! new) {
+               free(type);
+               free(id);
+               return -1;
        }
-       if (id != metric->store.id) {
-               if (metric->store.id)
-                       free(metric->store.id);
-               metric->store.id = id;
+
+       metric->stores = new;
+       new = metric->stores + metric->stores_num;
+       metric->stores_num++;
+
+       new->type = type;
+       new->id = id;
+       new->last_update = last_update;
+       return 0;
+} /* store_metric_add_store */
+
+static int
+store_metric_stores(metric_t *metric, sdb_store_metric_t *m)
+{
+       size_t i;
+
+       if (! m->stores_num)
+               return 0;
+
+       for (i = 0; i < m->stores_num; ++i) {
+               sdb_time_t last_update = m->stores[i].last_update;
+               size_t j;
+
+               if (last_update < m->last_update)
+                       last_update = m->last_update;
+
+               for (j = 0; j < metric->stores_num; ++j) {
+                       if ((! strcasecmp(metric->stores[j].type, m->stores[i].type))
+                                       && (! strcasecmp(metric->stores[j].id, m->stores[i].id))) {
+                               if (store_metric_update_store(metric->stores + j,
+                                                       m->stores + i, last_update) < 0)
+                                       return -1;
+                               break;
+                       }
+               }
+
+               if (j >= metric->stores_num)
+                       if (store_metric_add_store(metric, m->stores + i, last_update) < 0)
+                               return -1;
        }
        return 0;
-} /* store_metric_store */
+} /* store_metric_stores */
 
 /* The store's host_lock has to be acquired before calling this function. */
 static sdb_avltree_t *
@@ -483,6 +505,18 @@ get_host_children(host_t *host, int type)
                return host->services;
 } /* get_host_children */
 
+static sdb_avltree_t *
+get_obj_attrs(sdb_memstore_obj_t *obj)
+{
+       if (obj->type == SDB_HOST)
+               return HOST(obj)->attributes;
+       else if (obj->type == SDB_SERVICE)
+               return SVC(obj)->attributes;
+       else if (obj->type == SDB_METRIC)
+               return METRIC(obj)->attributes;
+       return NULL;
+} /* get_obj_attrs */
+
 /*
  * store writer API
  */
@@ -522,10 +556,8 @@ store_attribute(sdb_store_attribute_t *attr, sdb_object_t *user_data)
                obj.parent_tree = get_host_children(host, SDB_ATTRIBUTE);
                break;
        case SDB_SERVICE:
-               children = get_host_children(host, SDB_SERVICE);
-               break;
        case SDB_METRIC:
-               children = get_host_children(host, SDB_METRIC);
+               children = get_host_children(host, attr->parent_type);
                break;
        default:
                status = -1;
@@ -550,6 +582,7 @@ store_attribute(sdb_store_attribute_t *attr, sdb_object_t *user_data)
        obj.type = SDB_ATTRIBUTE;
        obj.name = attr->key;
        obj.last_update = attr->last_update;
+       obj.interval = attr->interval;
        obj.backends = attr->backends;
        obj.backends_num = attr->backends_num;
        if (! status)
@@ -575,7 +608,7 @@ static int
 store_host(sdb_store_host_t *host, sdb_object_t *user_data)
 {
        sdb_memstore_t *st = SDB_MEMSTORE(user_data);
-       store_obj_t obj = { NULL, st->hosts, SDB_HOST, NULL, 0, NULL, 0 };
+       store_obj_t obj = { NULL, st->hosts, SDB_HOST, NULL, 0, 0, NULL, 0 };
        int status = 0;
 
        if ((! host) || (! host->name))
@@ -583,6 +616,7 @@ store_host(sdb_store_host_t *host, sdb_object_t *user_data)
 
        obj.name = host->name;
        obj.last_update = host->last_update;
+       obj.interval = host->interval;
        obj.backends = host->backends;
        obj.backends_num = host->backends_num;
        pthread_rwlock_wrlock(&st->host_lock);
@@ -617,6 +651,7 @@ store_service(sdb_store_service_t *service, sdb_object_t *user_data)
 
        obj.name = service->name;
        obj.last_update = service->last_update;
+       obj.interval = service->interval;
        obj.backends = service->backends;
        obj.backends_num = service->backends_num;
        if (! status)
@@ -636,12 +671,14 @@ store_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
        host_t *host;
 
        int status = 0;
+       size_t i;
 
        if ((! metric) || (! metric->hostname) || (! metric->name))
                return -1;
 
-       if ((metric->store.type != NULL) != (metric->store.id != NULL))
-               return -1;
+       for (i = 0; i < metric->stores_num; ++i)
+               if ((metric->stores[i].type == NULL) || (metric->stores[i].id == NULL))
+                       return -1;
 
        pthread_rwlock_wrlock(&st->host_lock);
        host = HOST(sdb_avltree_lookup(st->hosts, metric->hostname));
@@ -656,6 +693,7 @@ store_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
 
        obj.name = metric->name;
        obj.last_update = metric->last_update;
+       obj.interval = metric->interval;
        obj.backends = metric->backends;
        obj.backends_num = metric->backends_num;
        if (! status)
@@ -668,9 +706,8 @@ store_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
        }
 
        assert(new);
-       if (metric->store.type && metric->store.id)
-               if (store_metric_store(METRIC(new), metric))
-                       status = -1;
+       if (store_metric_stores(METRIC(new), metric))
+               status = -1;
        pthread_rwlock_unlock(&st->host_lock);
        return status;
 } /* store_metric */
@@ -715,44 +752,53 @@ sdb_memstore_create(void)
 } /* sdb_memstore_create */
 
 int
-sdb_memstore_host(sdb_memstore_t *store, const char *name, sdb_time_t last_update)
+sdb_memstore_host(sdb_memstore_t *store, const char *name,
+               sdb_time_t last_update, sdb_time_t interval)
 {
        sdb_store_host_t host = {
-               name, last_update, 0, NULL, 0,
+               name, last_update, interval, NULL, 0,
        };
        return store_host(&host, SDB_OBJ(store));
 } /* sdb_memstore_host */
 
 int
 sdb_memstore_service(sdb_memstore_t *store, const char *hostname, const char *name,
-               sdb_time_t last_update)
+               sdb_time_t last_update, sdb_time_t interval)
 {
        sdb_store_service_t service = {
-               hostname, name, last_update, 0, NULL, 0,
+               hostname, name, last_update, interval, NULL, 0,
        };
        return store_service(&service, SDB_OBJ(store));
 } /* sdb_memstore_service */
 
 int
 sdb_memstore_metric(sdb_memstore_t *store, const char *hostname, const char *name,
-               sdb_metric_store_t *metric_store, sdb_time_t last_update)
+               sdb_metric_store_t *metric_store,
+               sdb_time_t last_update, sdb_time_t interval)
 {
        sdb_store_metric_t metric = {
-               hostname, name, { NULL, NULL }, last_update, 0, NULL, 0,
+               hostname, name, /* stores */ NULL, 0,
+               last_update, interval, NULL, 0,
        };
        if (metric_store) {
-               metric.store.type = metric_store->type;
-               metric.store.id = metric_store->id;
+               metric.stores = &(const sdb_metric_store_t){
+                       metric_store->type,
+                       metric_store->id,
+                       metric_store->last_update,
+               };
+               metric.stores_num = 1;
        }
        return store_metric(&metric, SDB_OBJ(store));
 } /* sdb_memstore_metric */
 
 int
 sdb_memstore_attribute(sdb_memstore_t *store, const char *hostname,
-               const char *key, const sdb_data_t *value, sdb_time_t last_update)
+               const char *key, const sdb_data_t *value,
+               sdb_time_t last_update, sdb_time_t interval)
 {
        sdb_store_attribute_t attr = {
-               NULL, SDB_HOST, hostname, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
+               NULL, SDB_HOST, hostname, key, SDB_DATA_INIT,
+               last_update, interval, NULL, 0,
        };
        if (value) {
                attr.value = *value;
@@ -763,10 +809,11 @@ sdb_memstore_attribute(sdb_memstore_t *store, const char *hostname,
 int
 sdb_memstore_service_attr(sdb_memstore_t *store, const char *hostname,
                const char *service, const char *key, const sdb_data_t *value,
-               sdb_time_t last_update)
+               sdb_time_t last_update, sdb_time_t interval)
 {
        sdb_store_attribute_t attr = {
-               hostname, SDB_SERVICE, service, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
+               hostname, SDB_SERVICE, service, key, SDB_DATA_INIT,
+               last_update, interval, NULL, 0,
        };
        if (value) {
                attr.value = *value;
@@ -777,10 +824,11 @@ sdb_memstore_service_attr(sdb_memstore_t *store, const char *hostname,
 int
 sdb_memstore_metric_attr(sdb_memstore_t *store, const char *hostname,
                const char *metric, const char *key, const sdb_data_t *value,
-               sdb_time_t last_update)
+               sdb_time_t last_update, sdb_time_t interval)
 {
        sdb_store_attribute_t attr = {
-               hostname, SDB_METRIC, metric, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
+               hostname, SDB_METRIC, metric, key, SDB_DATA_INIT,
+               last_update, interval, NULL, 0,
        };
        if (value) {
                attr.value = *value;
@@ -804,14 +852,17 @@ sdb_memstore_get_host(sdb_memstore_t *store, const char *name)
 } /* sdb_memstore_get_host */
 
 sdb_memstore_obj_t *
-sdb_memstore_get_child(sdb_memstore_obj_t *host, int type, const char *name)
+sdb_memstore_get_child(sdb_memstore_obj_t *obj, int type, const char *name)
 {
-       sdb_avltree_t *children;
+       sdb_avltree_t *children = NULL;
 
-       if ((! host) || (host->type != SDB_HOST) || (! name))
+       if ((! obj) || (! name))
                return NULL;
 
-       children = get_host_children(HOST(host), type);
+       if (type & SDB_ATTRIBUTE)
+               children = get_obj_attrs(obj);
+       else if (obj->type == SDB_HOST)
+               children = get_host_children(HOST(obj), type);
        if (! children)
                return NULL;
        return STORE_OBJ(sdb_avltree_lookup(children, name));
@@ -861,7 +912,7 @@ sdb_memstore_get_field(sdb_memstore_obj_t *obj, int field, sdb_data_t *res)
                        if (obj->type != SDB_METRIC)
                                return -1;
                        tmp.type = SDB_TYPE_BOOLEAN;
-                       tmp.data.boolean = METRIC(obj)->store.type != NULL;
+                       tmp.data.boolean = METRIC(obj)->stores_num > 0;
                default:
                        return -1;
        }
@@ -876,23 +927,12 @@ int
 sdb_memstore_get_attr(sdb_memstore_obj_t *obj, const char *name, sdb_data_t *res,
                sdb_memstore_matcher_t *filter)
 {
-       sdb_avltree_t *tree = NULL;
        sdb_memstore_obj_t *attr;
 
        if ((! obj) || (! name))
                return -1;
 
-       if (obj->type == SDB_HOST)
-               tree = HOST(obj)->attributes;
-       else if (obj->type == SDB_SERVICE)
-               tree = SVC(obj)->attributes;
-       else if (obj->type == SDB_METRIC)
-               tree = METRIC(obj)->attributes;
-
-       if (! tree)
-               return -1;
-
-       attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
+       attr = STORE_OBJ(sdb_avltree_lookup(get_obj_attrs(obj), name));
        if (! attr)
                return -1;
        if (filter && (! sdb_memstore_matcher_matches(filter, attr, NULL))) {
@@ -1014,18 +1054,25 @@ sdb_memstore_emit(sdb_memstore_obj_t *obj, sdb_store_writer_t *w, sdb_object_t *
                }
        case SDB_METRIC:
                {
+                       sdb_metric_store_t metric_stores[METRIC(obj)->stores_num];
                        sdb_store_metric_t metric = {
                                obj->parent ? obj->parent->_name : NULL,
                                obj->_name,
-                               {
-                                       METRIC(obj)->store.type,
-                                       METRIC(obj)->store.id,
-                               },
+                               metric_stores,
+                               METRIC(obj)->stores_num,
                                obj->last_update,
                                obj->interval,
                                (const char * const *)obj->backends,
                                obj->backends_num,
                        };
+                       size_t i;
+
+                       for (i = 0; i < METRIC(obj)->stores_num; ++i) {
+                               metric_stores[i].type = METRIC(obj)->stores[i].type;
+                               metric_stores[i].id = METRIC(obj)->stores[i].id;
+                               metric_stores[i].last_update = METRIC(obj)->stores[i].last_update;
+                       }
+
                        if (! w->store_metric)
                                return -1;
                        return w->store_metric(&metric, wd);