summaryrefslogtreecommitdiffstats
path: root/ctdb/common/hash_count.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /ctdb/common/hash_count.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ctdb/common/hash_count.c')
-rw-r--r--ctdb/common/hash_count.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/ctdb/common/hash_count.c b/ctdb/common/hash_count.c
new file mode 100644
index 0000000..f845016
--- /dev/null
+++ b/ctdb/common/hash_count.c
@@ -0,0 +1,219 @@
+/*
+ Using hash table for counting events
+
+ Copyright (C) Amitay Isaacs 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+
+#include <tdb.h>
+
+#include "lib/util/time.h"
+
+#include "common/db_hash.h"
+#include "common/hash_count.h"
+
+struct hash_count_value {
+ struct timeval update_time;
+ uint64_t counter;
+};
+
+struct hash_count_context {
+ struct db_hash_context *dh;
+ struct timeval update_interval;
+ hash_count_update_handler_fn handler;
+ void *private_data;
+};
+
+/*
+ * Initialise hash count map
+ */
+int hash_count_init(TALLOC_CTX *mem_ctx, struct timeval update_interval,
+ hash_count_update_handler_fn handler, void *private_data,
+ struct hash_count_context **result)
+{
+ struct hash_count_context *hcount;
+ int ret;
+
+ if (handler == NULL) {
+ return EINVAL;
+ }
+
+ hcount = talloc_zero(mem_ctx, struct hash_count_context);
+ if (hcount == NULL) {
+ return ENOMEM;
+ }
+
+ ret = db_hash_init(hcount, "hash_count_db", 8192, DB_HASH_COMPLEX,
+ &hcount->dh);
+ if (ret != 0) {
+ talloc_free(hcount);
+ return ret;
+ }
+
+ hcount->update_interval = update_interval;
+ hcount->handler = handler;
+ hcount->private_data = private_data;
+
+ *result = hcount;
+ return 0;
+}
+
+static int hash_count_fetch_parser(uint8_t *keybuf, size_t keylen,
+ uint8_t *databuf, size_t datalen,
+ void *private_data)
+{
+ struct hash_count_value *value =
+ (struct hash_count_value *)private_data;
+
+ if (datalen != sizeof(struct hash_count_value)) {
+ return EIO;
+ }
+
+ *value = *(struct hash_count_value *)databuf;
+ return 0;
+}
+
+static int hash_count_fetch(struct hash_count_context *hcount, TDB_DATA key,
+ struct hash_count_value *value)
+{
+ return db_hash_fetch(hcount->dh, key.dptr, key.dsize,
+ hash_count_fetch_parser, value);
+}
+
+static int hash_count_insert(struct hash_count_context *hcount, TDB_DATA key,
+ struct hash_count_value *value)
+{
+ return db_hash_insert(hcount->dh, key.dptr, key.dsize,
+ (uint8_t *)value,
+ sizeof(struct hash_count_value));
+}
+
+static int hash_count_update(struct hash_count_context *hcount, TDB_DATA key,
+ struct hash_count_value *value)
+{
+ return db_hash_add(hcount->dh, key.dptr, key.dsize,
+ (uint8_t *)value, sizeof(struct hash_count_value));
+}
+
+int hash_count_increment(struct hash_count_context *hcount, TDB_DATA key)
+{
+ struct hash_count_value value;
+ struct timeval current_time = timeval_current();
+ int ret;
+
+ if (hcount == NULL) {
+ return EINVAL;
+ }
+
+ ret = hash_count_fetch(hcount, key, &value);
+ if (ret == 0) {
+ struct timeval tmp_t;
+
+ tmp_t = timeval_sum(&value.update_time,
+ &hcount->update_interval);
+ if (timeval_compare(&current_time, &tmp_t) < 0) {
+ value.counter += 1;
+ } else {
+ value.update_time = current_time;
+ value.counter = 1;
+ }
+
+ hcount->handler(key, value.counter, hcount->private_data);
+ ret = hash_count_update(hcount, key, &value);
+
+ } else if (ret == ENOENT) {
+ value.update_time = current_time;
+ value.counter = 1;
+
+ hcount->handler(key, value.counter, hcount->private_data);
+ ret = hash_count_insert(hcount, key, &value);
+ }
+
+ return ret;
+}
+
+static struct timeval timeval_subtract(const struct timeval *tv1,
+ const struct timeval *tv2)
+{
+ struct timeval tv = *tv1;
+ const unsigned int million = 1000000;
+
+ if (tv.tv_sec > 1) {
+ tv.tv_sec -= 1;
+ tv.tv_usec += million;
+ } else {
+ return tv;
+ }
+
+ tv.tv_sec -= tv2->tv_sec;
+ tv.tv_usec -= tv2->tv_usec;
+
+ tv.tv_sec += tv.tv_usec / million;
+ tv.tv_usec = tv.tv_usec % million;
+
+ return tv;
+}
+
+struct hash_count_expire_state {
+ struct db_hash_context *dh;
+ struct timeval last_time;
+ int count;
+};
+
+static int hash_count_expire_parser(uint8_t *keybuf, size_t keylen,
+ uint8_t *databuf, size_t datalen,
+ void *private_data)
+{
+ struct hash_count_expire_state *state =
+ (struct hash_count_expire_state *)private_data;
+ struct hash_count_value *value;
+ int ret = 0;
+
+ if (datalen != sizeof(struct hash_count_value)) {
+ return EIO;
+ }
+
+ value = (struct hash_count_value *)databuf;
+ if (timeval_compare(&value->update_time, &state->last_time) < 0) {
+ ret = db_hash_delete(state->dh, keybuf, keylen);
+ if (ret == 0) {
+ state->count += 1;
+ }
+ }
+
+ return ret;
+}
+
+void hash_count_expire(struct hash_count_context *hcount, int *delete_count)
+{
+ struct timeval current_time = timeval_current();
+ struct hash_count_expire_state state;
+
+ state.dh = hcount->dh;
+ state.last_time = timeval_subtract(&current_time,
+ &hcount->update_interval);
+ state.count = 0;
+
+ (void) db_hash_traverse_update(hcount->dh, hash_count_expire_parser,
+ &state, NULL);
+
+ if (delete_count != NULL) {
+ *delete_count = state.count;
+ }
+}