]> arthur.barton.de Git - netdata.git/commitdiff
lower registry memory requirements by eliminating DICTIONARY structures for PERSON_URLs
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 1 Jan 2017 03:51:42 +0000 (05:51 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 1 Jan 2017 03:51:42 +0000 (05:51 +0200)
src/avl.c
src/avl.h
src/health.c
src/registry.c
src/registry.h
src/registry_db.c
src/registry_init.c
src/registry_internals.c
src/registry_machine.c
src/registry_person.c
src/registry_person.h

index 7efbf6da39f7d78f9aac9fe91cfdce96df2ff389..1ec1b8ad221bc31bf4adc8636512a97db9ccbb6f 100644 (file)
--- a/src/avl.c
+++ b/src/avl.c
@@ -283,18 +283,30 @@ avl *avl_remove(avl_tree *tree, avl *item) {
 // ---------------------------
 // traversing
 
-void avl_walker(avl *node, void (*callback)(void *entry, void *data), void *data) {
-    if(node->avl_link[0])
-        avl_walker(node->avl_link[0], callback, data);
+int avl_walker(avl *node, int (*callback)(void *entry, void *data), void *data) {
+    int total = 0, ret = 0;
 
-    callback(node, data);
+    if(node->avl_link[0]) {
+        ret = avl_walker(node->avl_link[0], callback, data);
+        if(ret < 0) return ret;
+        total += ret;
+    }
+
+    ret = callback(node, data);
+    if(ret < 0) return ret;
+    total += ret;
 
-    if(node->avl_link[1])
-        avl_walker(node->avl_link[1], callback, data);
+    if(node->avl_link[1]) {
+        ret = avl_walker(node->avl_link[1], callback, data);
+        if (ret < 0) return ret;
+        total += ret;
+    }
+
+    return total;
 }
 
-void avl_traverse(avl_tree *t, void (*callback)(void *entry, void *data), void *data) {
-    avl_walker(t->root, callback, data);
+int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *data) {
+    return avl_walker(t->root, callback, data);
 }
 
 // ---------------------------
@@ -372,10 +384,12 @@ avl *avl_insert_lock(avl_tree_lock *t, avl *a) {
     return ret;
 }
 
-void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *entry, void *data), void *data) {
+int avl_traverse_lock(avl_tree_lock *t, int (*callback)(void *entry, void *data), void *data) {
+    int ret;
     avl_read_lock(t);
-    avl_traverse(&t->avl_tree, callback, data);
+    ret = avl_traverse(&t->avl_tree, callback, data);
     avl_unlock(t);
+    return ret;
 }
 
 void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
index 1ca687be510b514a000cfc0b08a257e7d8a1c660..c15291caaecd82d7b2fa5cd9c36410a2f9272c0a 100644 (file)
--- a/src/avl.h
+++ b/src/avl.h
@@ -80,7 +80,7 @@ void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b));
 void avl_init(avl_tree *t, int (*compar)(void *a, void *b));
 
 
-void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *entry, void *data), void *data);
-void avl_traverse(avl_tree *t, void (*callback)(void *entry, void *data), void *data);
+int avl_traverse_lock(avl_tree_lock *t, int (*callback)(void *entry, void *data), void *data);
+int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *data);
 
 #endif /* avl.h */
index 849a3faf9646a07ca997d8393f747ad8c51bb133..14df6dd9ed4e78e5ad29b11230d47f53a76dd26a 100755 (executable)
@@ -663,7 +663,7 @@ struct variable2json_helper {
     size_t counter;
 };
 
-static void single_variable2json(void *entry, void *data) {
+static int single_variable2json(void *entry, void *data) {
     struct variable2json_helper *helper = (struct variable2json_helper *)data;
     RRDVAR *rv = (RRDVAR *)entry;
     calculated_number value = rrdvar2number(rv);
@@ -674,6 +674,8 @@ static void single_variable2json(void *entry, void *data) {
         buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value);
 
     helper->counter++;
+
+    return 0;
 }
 
 void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
index 84dc47aded3fcf522e50af8086e03a443bcf4a60..d223cd6f1e5076efe4714b32deff61ab4820c35d 100644 (file)
@@ -75,7 +75,7 @@ struct registry_json_walk_person_urls_callback {
 };
 
 // callback for rendering PERSON_URLs
-static inline int registry_json_person_url_callback(void *entry, void *data) {
+static int registry_json_person_url_callback(void *entry, void *data) {
     REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
     struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
     struct web_client *w = c->w;
@@ -86,11 +86,11 @@ static inline int registry_json_person_url_callback(void *entry, void *data) {
     buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
             pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name);
 
-    return 1;
+    return 0;
 }
 
 // callback for rendering MACHINE_URLs
-static inline int registry_json_machine_url_callback(void *entry, void *data) {
+static int registry_json_machine_url_callback(void *entry, void *data) {
     REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry;
     struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
     struct web_client *w = c->w;
@@ -113,7 +113,7 @@ struct registry_person_url_callback_verify_machine_exists_data {
     int count;
 };
 
-int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
+static inline int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
     struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
     REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
     REGISTRY_MACHINE *m = d->m;
@@ -181,7 +181,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
 
     buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
     struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
-    dictionary_get_all(p->person_urls, registry_json_person_url_callback, &c);
+    avl_traverse(&p->person_urls, registry_json_person_url_callback, &c);
     buffer_strcat(w->response.data, "\n\t]\n");
 
     registry_json_footer(w);
@@ -284,7 +284,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
     struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
 
     // verify the old person has access to this machine
-    dictionary_get_all(op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
+    avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
     if(!data.count) {
         registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
@@ -294,7 +294,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
 
     // verify the new person has access to this machine
     data.count = 0;
-    dictionary_get_all(np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
+    avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
     if(!data.count) {
         registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
@@ -372,7 +372,7 @@ void registry_statistics(void) {
     rrddim_set(stm, "persons",       registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
     rrddim_set(stm, "machines",      registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
     rrddim_set(stm, "urls",          registry.urls_memory);
-    rrddim_set(stm, "persons_urls",  registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
+    rrddim_set(stm, "persons_urls",  registry.persons_urls_memory);
     rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
     rrdset_done(stm);
 }
index 36bae720185712a748dd30b7553d135688063986..19ed802cb326f437b3888ccc456604c82e221756 100644 (file)
 // should only happen when netdata starts
 extern int registry_init(void);
 
+/*
 // free all data held by the registry
 // should only happen when netdata exits
 extern void registry_free(void);
+*/
 
 // HTTP requests handled by the registry
 extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
index f443989dcdfc71c26250f36a14684ceeabd94d11..de6c634c3547ef3abbe2943287745c88250fadc1 100644 (file)
@@ -86,7 +86,8 @@ static inline int registry_person_save(void *entry, void *file) {
     );
 
     if(ret >= 0) {
-        int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp);
+        //int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp);
+        int ret2 = avl_traverse(&p->person_urls, registry_person_save_url, fp);
         if (ret2 < 0) return ret2;
         ret += ret2;
     }
index f2027b89c659fe176d1b6e8319c6aa3c22acbbc6..1a086b0f72cc119f82d03ed1e7014e21ea450b83 100644 (file)
@@ -80,7 +80,7 @@ int registry_init(void) {
 
     return 0;
 }
-
+/*
 void registry_free(void) {
     if(!registry.enabled) return;
 
@@ -158,3 +158,4 @@ void registry_free(void) {
     debug(D_REGISTRY, "Registry: destroying machines dictionary");
     dictionary_destroy(registry.machines);
 }
+*/
index c7c6b6603f735df7c1e447f6a4e8138621aa3744..e3cd04737d476c4061ae6d30049a29cba577a73b 100644 (file)
@@ -115,7 +115,7 @@ REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_gu
     }
     if(pp) *pp = p;
 
-    REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, url);
+    REGISTRY_PERSON_URL *pu = registry_person_url_find(p, url);
     if(!pu) {
         info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
         return NULL;
@@ -170,7 +170,7 @@ REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid,
         return NULL;
     }
 
-    REGISTRY_PERSON_URL *dpu = dictionary_get(p->person_urls, delete_url);
+    REGISTRY_PERSON_URL *dpu = registry_person_url_find(p, delete_url);
     if(!dpu) {
         info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
         return NULL;
@@ -178,7 +178,7 @@ REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid,
 
     registry_log('D', p, m, pu->url, dpu->url->url);
 
-    dictionary_del(p->person_urls, dpu->url->url);
+    registry_person_url_del(p, dpu);
 
     registry_url_unlink(dpu->url);
 
@@ -240,8 +240,7 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid
     struct machine_request_callback_data rdata = { m, NULL };
 
     // request a walk through on the dictionary
-    // no need for locking here, the underlying dictionary has its own
-    dictionary_get_all(p->person_urls, machine_request_callback, &rdata);
+    avl_traverse(&p->person_urls, machine_request_callback, &rdata);
 
     if(rdata.result)
         return m;
index 6ebe7d6f820c8f9479540c8466ba3c36e9c72629..3510736df4de73ed35ea247c024e194d4e677c14 100644 (file)
@@ -93,7 +93,7 @@ REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY
     if(likely(m->last_t < (uint32_t)when)) m->last_t = (uint32_t)when;
 
     if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
-        info("registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url);
+        debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url);
         mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
     }
 
index 6424e956bc744e4ea2af30ea6d9fdc861d63b790..1ff8fc639ecc3b7ee26035e32a5f68238522c416 100644 (file)
@@ -1,21 +1,42 @@
 #include "registry_internals.h"
 
 // ----------------------------------------------------------------------------
-// PERSON
+// PERSON_URL
 
-REGISTRY_PERSON *registry_person_find(const char *person_guid) {
-    debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
-    return dictionary_get(registry.persons, person_guid);
+int person_url_compare(void *a, void *b) {
+    register uint32_t hash1 = ((REGISTRY_PERSON_URL *)a)->url->hash;
+    register uint32_t hash2 = ((REGISTRY_PERSON_URL *)b)->url->hash;
+
+    if(hash1 < hash2) return -1;
+    else if(hash1 > hash2) return 1;
+    else return strcmp(((REGISTRY_PERSON_URL *)a)->url->url, ((REGISTRY_PERSON_URL *)b)->url->url);
+}
+
+#define registry_person_url_index_add(person, rc) (REGISTRY_PERSON_URL *)avl_insert(&((person)->person_urls), (avl *)(rc))
+#define registry_person_url_index_del(person, rc) (REGISTRY_PERSON_URL *)avl_remove(&((person)->person_urls), (avl *)(rc))
+
+REGISTRY_PERSON_URL *registry_person_url_find(REGISTRY_PERSON *p, const char *url) {
+    debug(D_REGISTRY, "Registry: registry_person_url_find('%s', '%s')", p->guid, url);
+
+    char buf[sizeof(REGISTRY_URL) + strlen(url)];
+
+    REGISTRY_URL *u = (REGISTRY_URL *)&buf;
+    strcpy(u->url, url);
+    u->hash = simple_hash(u->url);
+
+    REGISTRY_PERSON_URL tpu = { .url = u };
+
+    REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)avl_search(&p->person_urls, (void *)&tpu);
+    return pu;
 }
 
 REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) {
+    debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen);
+
     // protection from too big names
     if(namelen > registry.max_name_length)
         namelen = registry.max_name_length;
 
-    debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
-            sizeof(REGISTRY_PERSON_URL) + namelen);
-
     REGISTRY_PERSON_URL *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen);
 
     // a simple strcpy() should do the job
@@ -32,43 +53,54 @@ REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_M
     registry.persons_urls_memory += sizeof(REGISTRY_PERSON_URL) + namelen;
 
     debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
-    dictionary_set(p->person_urls, u->url, pu, sizeof(REGISTRY_PERSON_URL));
+    registry_person_url_index_add(p, pu);
+
     registry_url_link(u);
 
     return pu;
 }
 
+// this function is needed to change the name of a PERSON_URL
 REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu) {
-    // this function is needed to change the name of a PERSON_URL
+    debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen);
 
-    debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
-            sizeof(REGISTRY_PERSON_URL) + namelen);
+    // remove the existing one from the index
+    registry_person_url_index_del(p, pu);
+    registry_url_unlink(pu->url);
+    pu->machine->links--;
+    registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name);
 
+    // allocate a new one
     REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
     tpu->first_t = pu->first_t;
     tpu->last_t = pu->last_t;
     tpu->usages = pu->usages;
-
-    // ok, these are a hack - since the registry_person_url_allocate() is
-    // adding these, we have to subtract them
-    tpu->machine->links--;
-    registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name);
-    registry_url_unlink(u);
+    tpu->flags = pu->flags;
 
     freez(pu);
-
     return tpu;
 }
 
-REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
-    REGISTRY_PERSON *p = NULL;
 
-    debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
+void registry_person_url_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
+    debug(D_REGISTRY, "Registry: registry_person_url_del('%s', '%s')", p->guid, pu->url->url);
+    registry_person_url_index_del(p, pu);
+}
 
-    p = mallocz(sizeof(REGISTRY_PERSON));
+// ----------------------------------------------------------------------------
+// PERSON
 
+REGISTRY_PERSON *registry_person_find(const char *person_guid) {
+    debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
+    return dictionary_get(registry.persons, person_guid);
+}
+
+REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
+    debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
+
+    REGISTRY_PERSON *p = mallocz(sizeof(REGISTRY_PERSON));
     if(!person_guid) {
-        for (; ;) {
+        for(;;) {
             uuid_t uuid;
             uuid_generate(uuid);
             uuid_unparse_lower(uuid, p->guid);
@@ -86,7 +118,7 @@ REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when)
         strncpyz(p->guid, person_guid, GUID_LEN);
 
     debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
-    p->person_urls = dictionary_create(DICTIONARY_FLAGS);
+    avl_init(&p->person_urls, person_url_compare);
 
     p->first_t = p->last_t = (uint32_t)when;
     p->usages = 0;
@@ -105,6 +137,8 @@ REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when)
 // 3. if it is not valid, create a new one
 // 4. return it
 REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
+    debug(D_REGISTRY, "Registry: registry_person_get('%s'): creating dictionary of urls", person_guid);
+
     REGISTRY_PERSON *p = NULL;
 
     if(person_guid && *person_guid) {
@@ -129,7 +163,7 @@ REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
 REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) {
     debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
 
-    REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, u->url);
+    REGISTRY_PERSON_URL *pu = registry_person_url_find(p, u->url);
     if(!pu) {
         debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
         pu = registry_person_url_allocate(p, m, u, name, namelen, when);
@@ -143,12 +177,12 @@ REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MA
         if(pu->machine != m) {
             REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url);
             if(mu) {
-                info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
+                debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
                      p->guid, m->guid, u->url, pu->machine->guid);
                 mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
             }
             else {
-                info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
+                debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
                      p->guid, m->guid, u->url, pu->machine->guid);
             }
 
@@ -166,7 +200,7 @@ REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MA
     if(likely(p->last_t < (uint32_t)when)) p->last_t = (uint32_t)when;
 
     if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
-        info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
+        debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
         pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
     }
 
index 87fd9ad5edf90ac779cab0acbb9343c86a5a3e2c..1355cc7296348f938c3424bb6cef4cdc03848353 100644 (file)
@@ -8,6 +8,8 @@
 
 // for each PERSON-URL pair we keep this
 struct registry_person_url {
+    avl avl;                    // binary tree node
+
     REGISTRY_URL *url;          // de-duplicated URL
     REGISTRY_MACHINE *machine;  // link the MACHINE of this URL
 
@@ -26,11 +28,14 @@ typedef struct registry_person_url REGISTRY_PERSON_URL;
 struct registry_person {
     char guid[GUID_LEN + 1];    // the person GUID
 
-    DICTIONARY *person_urls;    // dictionary of PERSON_URL *
+    avl_tree person_urls;       // dictionary of PERSON_URLs
 
     uint32_t first_t;           // the first time we saw this
     uint32_t last_t;            // the last time we saw this
     uint32_t usages;            // how many times this has been accessed
+
+    //uint32_t flags;
+    //char *email;
 };
 typedef struct registry_person REGISTRY_PERSON;
 
@@ -41,4 +46,7 @@ extern REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t
 extern REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when);
 extern REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when);
 
+extern REGISTRY_PERSON_URL *registry_person_url_find(REGISTRY_PERSON *p, const char *url);
+extern void registry_person_url_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu);
+
 #endif //NETDATA_REGISTRY_PERSON_H