ctdb-common: Optimize sock_queue's memory managament
[vlendec/samba-autobuild/.git] / ctdb / common / hash_count.c
1 /*
2    Using hash table for counting events
3
4    Copyright (C) Amitay Isaacs  2017
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/time.h"
23
24 #include <tdb.h>
25
26 #include "lib/util/time.h"
27
28 #include "common/db_hash.h"
29 #include "common/hash_count.h"
30
31 struct hash_count_value {
32         struct timeval update_time;
33         uint64_t counter;
34 };
35
36 struct hash_count_context {
37         struct db_hash_context *dh;
38         struct timeval update_interval;
39         hash_count_update_handler_fn handler;
40         void *private_data;
41 };
42
43 /*
44  * Initialise hash count map
45  */
46 int hash_count_init(TALLOC_CTX *mem_ctx, struct timeval update_interval,
47                     hash_count_update_handler_fn handler, void *private_data,
48                     struct hash_count_context **result)
49 {
50         struct hash_count_context *hcount;
51         int ret;
52
53         if (handler == NULL) {
54                 return EINVAL;
55         }
56
57         hcount = talloc_zero(mem_ctx, struct hash_count_context);
58         if (hcount == NULL) {
59                 return ENOMEM;
60         }
61
62         ret = db_hash_init(hcount, "hash_count_db", 8192, DB_HASH_COMPLEX,
63                             &hcount->dh);
64         if (ret != 0) {
65                 talloc_free(hcount);
66                 return ret;
67         }
68
69         hcount->update_interval = update_interval;
70         hcount->handler = handler;
71         hcount->private_data = private_data;
72
73         *result = hcount;
74         return 0;
75 }
76
77 static int hash_count_fetch_parser(uint8_t *keybuf, size_t keylen,
78                                    uint8_t *databuf, size_t datalen,
79                                    void *private_data)
80 {
81         struct hash_count_value *value =
82                 (struct hash_count_value *)private_data;
83
84         if (datalen != sizeof(struct hash_count_value)) {
85                 return EIO;
86         }
87
88         *value = *(struct hash_count_value *)databuf;
89         return 0;
90 }
91
92 static int hash_count_fetch(struct hash_count_context *hcount, TDB_DATA key,
93                             struct hash_count_value *value)
94 {
95         return db_hash_fetch(hcount->dh, key.dptr, key.dsize,
96                              hash_count_fetch_parser, value);
97 }
98
99 static int hash_count_insert(struct hash_count_context *hcount, TDB_DATA key,
100                              struct hash_count_value *value)
101 {
102         return db_hash_insert(hcount->dh, key.dptr, key.dsize,
103                               (uint8_t *)value,
104                               sizeof(struct hash_count_value));
105 }
106
107 static int hash_count_update(struct hash_count_context *hcount, TDB_DATA key,
108                              struct hash_count_value *value)
109 {
110         return db_hash_add(hcount->dh, key.dptr, key.dsize,
111                            (uint8_t *)value, sizeof(struct hash_count_value));
112 }
113
114 int hash_count_increment(struct hash_count_context *hcount, TDB_DATA key)
115 {
116         struct hash_count_value value;
117         struct timeval current_time = timeval_current();
118         int ret;
119
120         if (hcount == NULL) {
121                 return EINVAL;
122         }
123
124         ret = hash_count_fetch(hcount, key, &value);
125         if (ret == 0) {
126                 struct timeval tmp_t;
127
128                 tmp_t = timeval_sum(&value.update_time,
129                                     &hcount->update_interval);
130                 if (timeval_compare(&current_time, &tmp_t) < 0) {
131                         value.counter += 1;
132                 } else {
133                         value.update_time = current_time;
134                         value.counter = 1;
135                 }
136
137                 hcount->handler(key, value.counter, hcount->private_data);
138                 ret = hash_count_update(hcount, key, &value);
139
140         } else if (ret == ENOENT) {
141                 value.update_time = current_time;
142                 value.counter = 1;
143
144                 hcount->handler(key, value.counter, hcount->private_data);
145                 ret = hash_count_insert(hcount, key, &value);
146         }
147
148         return ret;
149 }
150
151 static struct timeval timeval_subtract(const struct timeval *tv1,
152                                        const struct timeval *tv2)
153 {
154         struct timeval tv = *tv1;
155         const unsigned int million = 1000000;
156
157         if (tv.tv_sec > 1) {
158                 tv.tv_sec -= 1;
159                 tv.tv_usec += million;
160         } else {
161                 return tv;
162         }
163
164         tv.tv_sec -= tv2->tv_sec;
165         tv.tv_usec -= tv2->tv_usec;
166
167         tv.tv_sec += tv.tv_usec / million;
168         tv.tv_usec = tv.tv_usec % million;
169
170         return tv;
171 }
172
173 struct hash_count_expire_state {
174         struct db_hash_context *dh;
175         struct timeval last_time;
176         int count;
177 };
178
179 static int hash_count_expire_parser(uint8_t *keybuf, size_t keylen,
180                                     uint8_t *databuf, size_t datalen,
181                                     void *private_data)
182 {
183         struct hash_count_expire_state *state =
184                 (struct hash_count_expire_state *)private_data;
185         struct hash_count_value *value;
186         int ret = 0;
187
188         if (datalen != sizeof(struct hash_count_value)) {
189                 return EIO;
190         }
191
192         value = (struct hash_count_value *)databuf;
193         if (timeval_compare(&value->update_time, &state->last_time) < 0) {
194                 ret = db_hash_delete(state->dh, keybuf, keylen);
195                 if (ret == 0) {
196                         state->count += 1;
197                 }
198         }
199
200         return ret;
201 }
202
203 void hash_count_expire(struct hash_count_context *hcount, int *delete_count)
204 {
205         struct timeval current_time = timeval_current();
206         struct hash_count_expire_state state;
207
208         state.dh = hcount->dh;
209         state.last_time = timeval_subtract(&current_time,
210                                            &hcount->update_interval);
211         state.count = 0;
212
213         (void) db_hash_traverse_update(hcount->dh, hash_count_expire_parser,
214                                        &state, NULL);
215
216         if (delete_count != NULL) {
217                 *delete_count = state.count;
218         }
219 }