ctdb-common: Add hash_count abstraction
[Samba.git] / ctdb / common / hash_count.c
blobf84501663c191f6f6097fa15739cd3a59ea77afc
1 /*
2 Using hash table for counting events
4 Copyright (C) Amitay Isaacs 2017
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.
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.
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/>.
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/time.h"
24 #include <tdb.h>
26 #include "lib/util/time.h"
28 #include "common/db_hash.h"
29 #include "common/hash_count.h"
31 struct hash_count_value {
32 struct timeval update_time;
33 uint64_t counter;
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;
44 * Initialise hash count map
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)
50 struct hash_count_context *hcount;
51 int ret;
53 if (handler == NULL) {
54 return EINVAL;
57 hcount = talloc_zero(mem_ctx, struct hash_count_context);
58 if (hcount == NULL) {
59 return ENOMEM;
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;
69 hcount->update_interval = update_interval;
70 hcount->handler = handler;
71 hcount->private_data = private_data;
73 *result = hcount;
74 return 0;
77 static int hash_count_fetch_parser(uint8_t *keybuf, size_t keylen,
78 uint8_t *databuf, size_t datalen,
79 void *private_data)
81 struct hash_count_value *value =
82 (struct hash_count_value *)private_data;
84 if (datalen != sizeof(struct hash_count_value)) {
85 return EIO;
88 *value = *(struct hash_count_value *)databuf;
89 return 0;
92 static int hash_count_fetch(struct hash_count_context *hcount, TDB_DATA key,
93 struct hash_count_value *value)
95 return db_hash_fetch(hcount->dh, key.dptr, key.dsize,
96 hash_count_fetch_parser, value);
99 static int hash_count_insert(struct hash_count_context *hcount, TDB_DATA key,
100 struct hash_count_value *value)
102 return db_hash_insert(hcount->dh, key.dptr, key.dsize,
103 (uint8_t *)value,
104 sizeof(struct hash_count_value));
107 static int hash_count_update(struct hash_count_context *hcount, TDB_DATA key,
108 struct hash_count_value *value)
110 return db_hash_add(hcount->dh, key.dptr, key.dsize,
111 (uint8_t *)value, sizeof(struct hash_count_value));
114 int hash_count_increment(struct hash_count_context *hcount, TDB_DATA key)
116 struct hash_count_value value;
117 struct timeval current_time = timeval_current();
118 int ret;
120 if (hcount == NULL) {
121 return EINVAL;
124 ret = hash_count_fetch(hcount, key, &value);
125 if (ret == 0) {
126 struct timeval tmp_t;
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;
137 hcount->handler(key, value.counter, hcount->private_data);
138 ret = hash_count_update(hcount, key, &value);
140 } else if (ret == ENOENT) {
141 value.update_time = current_time;
142 value.counter = 1;
144 hcount->handler(key, value.counter, hcount->private_data);
145 ret = hash_count_insert(hcount, key, &value);
148 return ret;
151 static struct timeval timeval_subtract(const struct timeval *tv1,
152 const struct timeval *tv2)
154 struct timeval tv = *tv1;
155 const unsigned int million = 1000000;
157 if (tv.tv_sec > 1) {
158 tv.tv_sec -= 1;
159 tv.tv_usec += million;
160 } else {
161 return tv;
164 tv.tv_sec -= tv2->tv_sec;
165 tv.tv_usec -= tv2->tv_usec;
167 tv.tv_sec += tv.tv_usec / million;
168 tv.tv_usec = tv.tv_usec % million;
170 return tv;
173 struct hash_count_expire_state {
174 struct db_hash_context *dh;
175 struct timeval last_time;
176 int count;
179 static int hash_count_expire_parser(uint8_t *keybuf, size_t keylen,
180 uint8_t *databuf, size_t datalen,
181 void *private_data)
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;
188 if (datalen != sizeof(struct hash_count_value)) {
189 return EIO;
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;
200 return ret;
203 void hash_count_expire(struct hash_count_context *hcount, int *delete_count)
205 struct timeval current_time = timeval_current();
206 struct hash_count_expire_state state;
208 state.dh = hcount->dh;
209 state.last_time = timeval_subtract(&current_time,
210 &hcount->update_interval);
211 state.count = 0;
213 (void) db_hash_traverse_update(hcount->dh, hash_count_expire_parser,
214 &state, NULL);
216 if (delete_count != NULL) {
217 *delete_count = state.count;