@cryptotaxi247 / netdata-1 / commits / 3d239c5bf

lower registry memory requirements by eliminating DICTIONARY structures for PERSON_URLs

Costa Tsaousis (ktsaou) committed Jan 1, 2017 at 05:51 UTC 3d239c5bf9abf9a263409fc8a2dc0c190efcdf70
11 files changed +119 -58
src/avl.c
+24 -10
@@ -283,18 +283,30 @@ avl *avl_remove(avl_tree *tree, avl *item) {
283 // ---------------------------
284 // traversing
285
286 -void avl_walker(avl *node, void (*callback)(void *entry, void *data), void *data) {
287 - if(node->avl_link[0])
288 - avl_walker(node->avl_link[0], callback, data);
286 +int avl_walker(avl *node, int (*callback)(void *entry, void *data), void *data) {
287 + int total = 0, ret = 0;
288
290 - callback(node, data);
289 + if(node->avl_link[0]) {
290 + ret = avl_walker(node->avl_link[0], callback, data);
291 + if(ret < 0) return ret;
292 + total += ret;
293 + }
294 +
295 + ret = callback(node, data);
296 + if(ret < 0) return ret;
297 + total += ret;
298
292 - if(node->avl_link[1])
293 - avl_walker(node->avl_link[1], callback, data);
299 + if(node->avl_link[1]) {
300 + ret = avl_walker(node->avl_link[1], callback, data);
301 + if (ret < 0) return ret;
302 + total += ret;
303 + }
304 +
305 + return total;
306 }
307
296 -void avl_traverse(avl_tree *t, void (*callback)(void *entry, void *data), void *data) {
297 - avl_walker(t->root, callback, data);
308 +int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *data) {
309 + return avl_walker(t->root, callback, data);
310 }
311
312 // ---------------------------
@@ -372,10 +384,12 @@ avl *avl_insert_lock(avl_tree_lock *t, avl *a) {
384 return ret;
385 }
386
375 -void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *entry, void *data), void *data) {
387 +int avl_traverse_lock(avl_tree_lock *t, int (*callback)(void *entry, void *data), void *data) {
388 + int ret;
389 avl_read_lock(t);
377 - avl_traverse(&t->avl_tree, callback, data);
390 + ret = avl_traverse(&t->avl_tree, callback, data);
391 avl_unlock(t);
392 + return ret;
393 }
394
395 void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
src/avl.h
+2 -2
@@ -80,7 +80,7 @@ void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b));
80 void avl_init(avl_tree *t, int (*compar)(void *a, void *b));
81
82
83 -void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *entry, void *data), void *data);
84 -void avl_traverse(avl_tree *t, void (*callback)(void *entry, void *data), void *data);
83 +int avl_traverse_lock(avl_tree_lock *t, int (*callback)(void *entry, void *data), void *data);
84 +int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *data);
85
86 #endif /* avl.h */
src/health.c
+3 -1
@@ -663,7 +663,7 @@ struct variable2json_helper {
663 size_t counter;
664 };
665
666 -static void single_variable2json(void *entry, void *data) {
666 +static int single_variable2json(void *entry, void *data) {
667 struct variable2json_helper *helper = (struct variable2json_helper *)data;
668 RRDVAR *rv = (RRDVAR *)entry;
669 calculated_number value = rrdvar2number(rv);
@@ -674,6 +674,8 @@ static void single_variable2json(void *entry, void *data) {
674 buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value);
675
676 helper->counter++;
677 +
678 + return 0;
679 }
680
681 void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
src/registry.c
+8 -8
@@ -75,7 +75,7 @@ struct registry_json_walk_person_urls_callback {
75 };
76
77 // callback for rendering PERSON_URLs
78 -static inline int registry_json_person_url_callback(void *entry, void *data) {
78 +static int registry_json_person_url_callback(void *entry, void *data) {
79 REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
80 struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
81 struct web_client *w = c->w;
@@ -86,11 +86,11 @@ static inline int registry_json_person_url_callback(void *entry, void *data) {
86 buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
87 pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->machine_name);
88
89 - return 1;
89 + return 0;
90 }
91
92 // callback for rendering MACHINE_URLs
93 -static inline int registry_json_machine_url_callback(void *entry, void *data) {
93 +static int registry_json_machine_url_callback(void *entry, void *data) {
94 REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry;
95 struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
96 struct web_client *w = c->w;
@@ -113,7 +113,7 @@ struct registry_person_url_callback_verify_machine_exists_data {
113 int count;
114 };
115
116 -int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
116 +static inline int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
117 struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
118 REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)entry;
119 REGISTRY_MACHINE *m = d->m;
@@ -181,7 +181,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
181
182 buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
183 struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
184 - dictionary_get_all(p->person_urls, registry_json_person_url_callback, &c);
184 + avl_traverse(&p->person_urls, registry_json_person_url_callback, &c);
185 buffer_strcat(w->response.data, "\n\t]\n");
186
187 registry_json_footer(w);
@@ -284,7 +284,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
284 struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
285
286 // verify the old person has access to this machine
287 - dictionary_get_all(op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
287 + avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
288 if(!data.count) {
289 registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
290 registry_json_footer(w);
@@ -294,7 +294,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
294
295 // verify the new person has access to this machine
296 data.count = 0;
297 - dictionary_get_all(np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
297 + avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
298 if(!data.count) {
299 registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
300 registry_json_footer(w);
@@ -372,7 +372,7 @@ void registry_statistics(void) {
372 rrddim_set(stm, "persons", registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
373 rrddim_set(stm, "machines", registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
374 rrddim_set(stm, "urls", registry.urls_memory);
375 - rrddim_set(stm, "persons_urls", registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
375 + rrddim_set(stm, "persons_urls", registry.persons_urls_memory);
376 rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
377 rrdset_done(stm);
378 }
src/registry.h
+2
@@ -54,9 +54,11 @@
54 // should only happen when netdata starts
55 extern int registry_init(void);
56
57 +/*
58 // free all data held by the registry
59 // should only happen when netdata exits
60 extern void registry_free(void);
61 +*/
62
63 // HTTP requests handled by the registry
64 extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
src/registry_db.c
+2 -1
@@ -86,7 +86,8 @@ static inline int registry_person_save(void *entry, void *file) {
86 );
87
88 if(ret >= 0) {
89 - int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp);
89 + //int ret2 = dictionary_get_all(p->person_urls, registry_person_save_url, fp);
90 + int ret2 = avl_traverse(&p->person_urls, registry_person_save_url, fp);
91 if (ret2 < 0) return ret2;
92 ret += ret2;
93 }
src/registry_init.c
+2 -1
@@ -80,7 +80,7 @@ int registry_init(void) {
80
81 return 0;
82 }
83 -
83 +/*
84 void registry_free(void) {
85 if(!registry.enabled) return;
86
@@ -158,3 +158,4 @@ void registry_free(void) {
158 debug(D_REGISTRY, "Registry: destroying machines dictionary");
159 dictionary_destroy(registry.machines);
160 }
161 +*/
src/registry_internals.c
+4 -5
@@ -115,7 +115,7 @@ REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_gu
115 }
116 if(pp) *pp = p;
117
118 - REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, url);
118 + REGISTRY_PERSON_URL *pu = registry_person_url_find(p, url);
119 if(!pu) {
120 info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
121 return NULL;
@@ -170,7 +170,7 @@ REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid,
170 return NULL;
171 }
172
173 - REGISTRY_PERSON_URL *dpu = dictionary_get(p->person_urls, delete_url);
173 + REGISTRY_PERSON_URL *dpu = registry_person_url_find(p, delete_url);
174 if(!dpu) {
175 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);
176 return NULL;
@@ -178,7 +178,7 @@ REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid,
178
179 registry_log('D', p, m, pu->url, dpu->url->url);
180
181 - dictionary_del(p->person_urls, dpu->url->url);
181 + registry_person_url_del(p, dpu);
182
183 registry_url_unlink(dpu->url);
184
@@ -240,8 +240,7 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid
240 struct machine_request_callback_data rdata = { m, NULL };
241
242 // request a walk through on the dictionary
243 - // no need for locking here, the underlying dictionary has its own
244 - dictionary_get_all(p->person_urls, machine_request_callback, &rdata);
243 + avl_traverse(&p->person_urls, machine_request_callback, &rdata);
244
245 if(rdata.result)
246 return m;
src/registry_machine.c
+1 -1
@@ -93,7 +93,7 @@ REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY
93 if(likely(m->last_t < (uint32_t)when)) m->last_t = (uint32_t)when;
94
95 if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
96 - info("registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url);
96 + debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): accessing an expired URL.", m->guid, u->url);
97 mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
98 }
99
src/registry_person.c
+62 -28
@@ -1,21 +1,42 @@
1 #include "registry_internals.h"
2
3 // ----------------------------------------------------------------------------
4 -// PERSON
4 +// PERSON_URL
5
6 -REGISTRY_PERSON *registry_person_find(const char *person_guid) {
7 - debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
8 - return dictionary_get(registry.persons, person_guid);
6 +int person_url_compare(void *a, void *b) {
7 + register uint32_t hash1 = ((REGISTRY_PERSON_URL *)a)->url->hash;
8 + register uint32_t hash2 = ((REGISTRY_PERSON_URL *)b)->url->hash;
9 +
10 + if(hash1 < hash2) return -1;
11 + else if(hash1 > hash2) return 1;
12 + else return strcmp(((REGISTRY_PERSON_URL *)a)->url->url, ((REGISTRY_PERSON_URL *)b)->url->url);
13 +}
14 +
15 +#define registry_person_url_index_add(person, rc) (REGISTRY_PERSON_URL *)avl_insert(&((person)->person_urls), (avl *)(rc))
16 +#define registry_person_url_index_del(person, rc) (REGISTRY_PERSON_URL *)avl_remove(&((person)->person_urls), (avl *)(rc))
17 +
18 +REGISTRY_PERSON_URL *registry_person_url_find(REGISTRY_PERSON *p, const char *url) {
19 + debug(D_REGISTRY, "Registry: registry_person_url_find('%s', '%s')", p->guid, url);
20 +
21 + char buf[sizeof(REGISTRY_URL) + strlen(url)];
22 +
23 + REGISTRY_URL *u = (REGISTRY_URL *)&buf;
24 + strcpy(u->url, url);
25 + u->hash = simple_hash(u->url);
26 +
27 + REGISTRY_PERSON_URL tpu = { .url = u };
28 +
29 + REGISTRY_PERSON_URL *pu = (REGISTRY_PERSON_URL *)avl_search(&p->person_urls, (void *)&tpu);
30 + return pu;
31 }
32
33 REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when) {
34 + debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen);
35 +
36 // protection from too big names
37 if(namelen > registry.max_name_length)
38 namelen = registry.max_name_length;
39
16 - debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
17 - sizeof(REGISTRY_PERSON_URL) + namelen);
18 -
40 REGISTRY_PERSON_URL *pu = mallocz(sizeof(REGISTRY_PERSON_URL) + namelen);
41
42 // a simple strcpy() should do the job
@@ -32,43 +53,54 @@ REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_M
53 registry.persons_urls_memory += sizeof(REGISTRY_PERSON_URL) + namelen;
54
55 debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
35 - dictionary_set(p->person_urls, u->url, pu, sizeof(REGISTRY_PERSON_URL));
56 + registry_person_url_index_add(p, pu);
57 +
58 registry_url_link(u);
59
60 return pu;
61 }
62
63 +// this function is needed to change the name of a PERSON_URL
64 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) {
42 - // this function is needed to change the name of a PERSON_URL
65 + debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url, sizeof(REGISTRY_PERSON_URL) + namelen);
66
44 - debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
45 - sizeof(REGISTRY_PERSON_URL) + namelen);
67 + // remove the existing one from the index
68 + registry_person_url_index_del(p, pu);
69 + registry_url_unlink(pu->url);
70 + pu->machine->links--;
71 + registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name);
72
73 + // allocate a new one
74 REGISTRY_PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
75 tpu->first_t = pu->first_t;
76 tpu->last_t = pu->last_t;
77 tpu->usages = pu->usages;
51 -
52 - // ok, these are a hack - since the registry_person_url_allocate() is
53 - // adding these, we have to subtract them
54 - tpu->machine->links--;
55 - registry.persons_urls_memory -= sizeof(REGISTRY_PERSON_URL) + strlen(pu->machine_name);
56 - registry_url_unlink(u);
78 + tpu->flags = pu->flags;
79
80 freez(pu);
59 -
81 return tpu;
82 }
83
63 -REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
64 - REGISTRY_PERSON *p = NULL;
84
66 - debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
85 +void registry_person_url_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) {
86 + debug(D_REGISTRY, "Registry: registry_person_url_del('%s', '%s')", p->guid, pu->url->url);
87 + registry_person_url_index_del(p, pu);
88 +}
89
68 - p = mallocz(sizeof(REGISTRY_PERSON));
90 +// ----------------------------------------------------------------------------
91 +// PERSON
92
93 +REGISTRY_PERSON *registry_person_find(const char *person_guid) {
94 + debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
95 + return dictionary_get(registry.persons, person_guid);
96 +}
97 +
98 +REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when) {
99 + debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(REGISTRY_PERSON));
100 +
101 + REGISTRY_PERSON *p = mallocz(sizeof(REGISTRY_PERSON));
102 if(!person_guid) {
71 - for (; ;) {
103 + for(;;) {
104 uuid_t uuid;
105 uuid_generate(uuid);
106 uuid_unparse_lower(uuid, p->guid);
@@ -86,7 +118,7 @@ REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when)
118 strncpyz(p->guid, person_guid, GUID_LEN);
119
120 debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
89 - p->person_urls = dictionary_create(DICTIONARY_FLAGS);
121 + avl_init(&p->person_urls, person_url_compare);
122
123 p->first_t = p->last_t = (uint32_t)when;
124 p->usages = 0;
@@ -105,6 +137,8 @@ REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when)
137 // 3. if it is not valid, create a new one
138 // 4. return it
139 REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
140 + debug(D_REGISTRY, "Registry: registry_person_get('%s'): creating dictionary of urls", person_guid);
141 +
142 REGISTRY_PERSON *p = NULL;
143
144 if(person_guid && *person_guid) {
@@ -129,7 +163,7 @@ REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
163 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) {
164 debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
165
132 - REGISTRY_PERSON_URL *pu = dictionary_get(p->person_urls, u->url);
166 + REGISTRY_PERSON_URL *pu = registry_person_url_find(p, u->url);
167 if(!pu) {
168 debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
169 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
177 if(pu->machine != m) {
178 REGISTRY_MACHINE_URL *mu = dictionary_get(pu->machine->machine_urls, u->url);
179 if(mu) {
146 - info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
180 + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
181 p->guid, m->guid, u->url, pu->machine->guid);
182 mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
183 }
184 else {
151 - 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.",
185 + 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.",
186 p->guid, m->guid, u->url, pu->machine->guid);
187 }
188
@@ -166,7 +200,7 @@ REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MA
200 if(likely(p->last_t < (uint32_t)when)) p->last_t = (uint32_t)when;
201
202 if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
169 - info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
203 + debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
204 pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
205 }
206
src/registry_person.h
+9 -1
@@ -8,6 +8,8 @@
8
9 // for each PERSON-URL pair we keep this
10 struct registry_person_url {
11 + avl avl; // binary tree node
12 +
13 REGISTRY_URL *url; // de-duplicated URL
14 REGISTRY_MACHINE *machine; // link the MACHINE of this URL
15
@@ -26,11 +28,14 @@ typedef struct registry_person_url REGISTRY_PERSON_URL;
28 struct registry_person {
29 char guid[GUID_LEN + 1]; // the person GUID
30
29 - DICTIONARY *person_urls; // dictionary of PERSON_URL *
31 + avl_tree person_urls; // dictionary of PERSON_URLs
32
33 uint32_t first_t; // the first time we saw this
34 uint32_t last_t; // the last time we saw this
35 uint32_t usages; // how many times this has been accessed
36 +
37 + //uint32_t flags;
38 + //char *email;
39 };
40 typedef struct registry_person REGISTRY_PERSON;
41
@@ -41,4 +46,7 @@ extern REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t
46 extern REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when);
47 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);
48
49 +extern REGISTRY_PERSON_URL *registry_person_url_find(REGISTRY_PERSON *p, const char *url);
50 +extern void registry_person_url_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu);
51 +
52 #endif //NETDATA_REGISTRY_PERSON_H