From ef3e099a424694e6247252a40c7b3be343f3cebd Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Tue, 30 Sep 2014 07:40:52 -0700 Subject: [PATCH] When querying services/metrics skip hosts without such children. This takes into account any filters and, thus, might mean that filters have to be evaluated multiple times: once for determining if there are any children and then again while serializing the data. This is because filtering happens during serialization at which point it's too late to skip a host. --- src/core/store.c | 37 +++++++++++++++++++++++++++++++++++ src/frontend/query.c | 6 ++++-- src/include/core/store.h | 11 ++++++++--- t/integration/simple_query.sh | 1 - 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/core/store.c b/src/core/store.c index e14f27c..8cb30ef 100644 --- a/src/core/store.c +++ b/src/core/store.c @@ -928,6 +928,10 @@ sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf, if ((! h) || (h->type != SDB_HOST) || (! buf)) return -1; + /* This function ignores SKIP_EMPTY flags given that the current + * implementation sucks and it's nut currently used when calling this + * function directly. */ + sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name); store_common_tojson(h, buf); @@ -950,6 +954,26 @@ sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf, return 0; } /* sdb_store_host_tojson */ +static _Bool +has_children(sdb_avltree_t *tree, sdb_store_matcher_t *filter) +{ + sdb_avltree_iter_t *iter; + + if (! filter) + return sdb_avltree_size(tree) > 0; + + iter = sdb_avltree_get_iter(tree); + while (sdb_avltree_iter_has_next(iter)) { + sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter)); + if (sdb_store_matcher_matches(filter, sobj, NULL)) { + sdb_avltree_iter_destroy(iter); + return 1; + } + } + sdb_avltree_iter_destroy(iter); + return 0; +} /* has_children */ + int sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags) { @@ -979,6 +1003,19 @@ sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags) if (filter && (! sdb_store_matcher_matches(filter, host, NULL))) continue; + /* + * XXX: This approach sucks but it's the best we can do at the moment. + * In the future, all store lookups should be split into multiple + * steps instead: first, retrieve all relevant objects and apply all + * pre-processing operations and then format it for the wire. + */ + if ((flags & SDB_SKIP_EMPTY_SERVICES) + && (! has_children(HOST(host)->services, filter))) + continue; + if ((flags & SDB_SKIP_EMPTY_METRICS) + && (! has_children(HOST(host)->metrics, filter))) + continue; + if (sdb_strbuf_len(buf) > len) sdb_strbuf_append(buf, ","); len = sdb_strbuf_len(buf); diff --git a/src/frontend/query.c b/src/frontend/query.c index 0ee734c..1751ba5 100644 --- a/src/frontend/query.c +++ b/src/frontend/query.c @@ -271,9 +271,11 @@ sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter) if (type == SDB_HOST) flags = SDB_SKIP_ALL; else if (type == SDB_SERVICE) - flags = SDB_SKIP_ALL & (~SDB_SKIP_SERVICES); + flags = (SDB_SKIP_ALL & (~SDB_SKIP_SERVICES)) + | SDB_SKIP_EMPTY_SERVICES; else if (type == SDB_METRIC) - flags = SDB_SKIP_ALL & (~SDB_SKIP_METRICS); + flags = (SDB_SKIP_ALL & (~SDB_SKIP_METRICS)) + | SDB_SKIP_EMPTY_METRICS; else { sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d " "for LIST command", type); diff --git a/src/include/core/store.h b/src/include/core/store.h index fa974bb..d5a7694 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -518,8 +518,9 @@ sdb_store_scan(sdb_store_matcher_t *m, sdb_store_matcher_t *filter, /* * Flags for serialization functions. * - * By default, the full object will be included in the serialized output. When - * specifying any of the flags, the respective information will be left out. + * By default, the full host object will be included in the serialized output. + * When specifying any of the flags, the respective information will be left + * out. The SKIP_EMPTY flags may be used to skip host objects entirely. */ enum { SDB_SKIP_ATTRIBUTES = 1 << 0, @@ -527,7 +528,11 @@ enum { SDB_SKIP_METRICS = 1 << 2, SDB_SKIP_SERVICE_ATTRIBUTES = 1 << 3, - SDB_SKIP_ALL = 0xffff, + SDB_SKIP_ALL = (1 << 8) - 1, + + /* skip hosts if they do not reference any services/metrics */ + SDB_SKIP_EMPTY_SERVICES = 1 << 8, + SDB_SKIP_EMPTY_METRICS = 1 << 9, }; /* diff --git a/t/integration/simple_query.sh b/t/integration/simple_query.sh index 900db90..b639443 100755 --- a/t/integration/simple_query.sh +++ b/t/integration/simple_query.sh @@ -75,7 +75,6 @@ echo "$output" \ | grep -F '"host1.example.com"' \ | grep -F '"host2.example.com"' \ | grep -F '"localhost"' \ - | grep -F '"other.host.name"' \ | grep -F '"some.host.name"' \ | grep -F '"mock service"' \ | grep -F '"other service"' \ -- 2.30.2