Mercurial > dovecot > core-2.2
changeset 14468:0b4295b48941
director: Optimized adding users to linked list during handshake.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 19 Apr 2012 21:51:48 +0300 |
parents | 110673e82868 |
children | 9104a9074a5a |
files | src/director/Makefile.am src/director/director-connection.c src/director/director-request.c src/director/director.c src/director/test-user-directory.c src/director/user-directory.c src/director/user-directory.h |
diffstat | 7 files changed, 199 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/src/director/Makefile.am Thu Apr 19 20:29:25 2012 +0300 +++ b/src/director/Makefile.am Thu Apr 19 21:51:48 2012 +0300 @@ -4,6 +4,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-settings \ @@ -40,10 +41,27 @@ notify-connection.h \ user-directory.h -noinst_PROGRAMS = director-test +noinst_PROGRAMS = director-test $(test_programs) director_test_LDADD = $(LIBDOVECOT) director_test_DEPENDENCIES = $(LIBDOVECOT_DEPS) director_test_SOURCES = \ director-test.c + +test_programs = \ + test-user-directory + +test_libs = \ + ../lib-test/libtest.la \ + ../lib/liblib.la + +test_user_directory_SOURCES = test-user-directory.c +test_user_directory_LDADD = user-directory.o $(test_libs) +test_user_directory_DEPENDENCIES = user-directory.o $(test_libs) + +check: check-am check-test +check-test: all-am + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done
--- a/src/director/director-connection.c Thu Apr 19 20:29:25 2012 +0300 +++ b/src/director/director-connection.c Thu Apr 19 21:51:48 2012 +0300 @@ -540,7 +540,7 @@ } if (director_user_refresh(conn, username_hash, - host, ioloop_time, FALSE, &user)) { + host, (time_t)-1, FALSE, &user)) { i_assert(!user->weak); director_update_user(conn->dir, conn->host, user); } @@ -674,7 +674,7 @@ } if (director_user_refresh(conn, username_hash, - host, ioloop_time, weak, &user)) { + host, (time_t)-1, weak, &user)) { if (!user->weak) director_update_user(conn->dir, src_host, user); else {
--- a/src/director/director-request.c Thu Apr 19 20:29:25 2012 +0300 +++ b/src/director/director-request.c Thu Apr 19 21:51:48 2012 +0300 @@ -225,7 +225,7 @@ return FALSE; } user = user_directory_add(dir->users, request->username_hash, - host, ioloop_time); + host, (time_t)-1); } i_assert(!user->weak);
--- a/src/director/director.c Thu Apr 19 20:29:25 2012 +0300 +++ b/src/director/director.c Thu Apr 19 21:51:48 2012 +0300 @@ -582,7 +582,7 @@ user = user_directory_lookup(dir->users, username_hash); if (user == NULL) { user = user_directory_add(dir->users, username_hash, - host, ioloop_time); + host, (time_t)-1); } else { if (user->host == host) { /* user is already in this host */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/director/test-user-directory.c Thu Apr 19 21:51:48 2012 +0300 @@ -0,0 +1,104 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "mail-user-hash.h" +#include "mail-host.h" +#include "user-directory.h" +#include "test-common.h" + +#include <stdlib.h> + +#define USER_DIR_TIMEOUT 1000000 + +unsigned int mail_user_hash(const char *username ATTR_UNUSED, + const char *format ATTR_UNUSED) { return 0; } + +static void +verify_user_directory(struct user_directory *dir, unsigned int user_count) +{ + struct user_directory_iter *iter; + struct user *user, *prev = NULL; + unsigned int prev_stamp = 0, iter_count = 0; + + iter = user_directory_iter_init(dir); + while ((user = user_directory_iter_next(iter)) != NULL) { + test_assert(prev_stamp <= user->timestamp); + test_assert(user->prev == prev); + test_assert(prev == NULL || user->prev->next == user); + + iter_count++; + prev = user; + } + test_assert(prev == NULL || prev->next == NULL); + user_directory_iter_deinit(&iter); + test_assert(iter_count == user_count); +} + +static void test_user_directory_ascending(void) +{ + const unsigned int count = 100000; + struct user_directory *dir; + struct mail_host *host = t_new(struct mail_host, 1); + unsigned int i; + + test_begin("user directory ascending"); + dir = user_directory_init(USER_DIR_TIMEOUT, "%u"); + user_directory_add(dir, 1, host, ioloop_time + count+1); + + for (i = 0; i < count; i++) + user_directory_add(dir, i+2, host, ioloop_time + i); + verify_user_directory(dir, count+1); + user_directory_deinit(&dir); + test_end(); +} + +static void test_user_directory_descending(void) +{ + const unsigned int count = 1000; + struct user_directory *dir; + struct mail_host *host = t_new(struct mail_host, 1); + unsigned int i; + + test_begin("user directory descending"); + dir = user_directory_init(USER_DIR_TIMEOUT, "%u"); + + for (i = 0; i < count; i++) + user_directory_add(dir, i+1, host, ioloop_time - i); + verify_user_directory(dir, count); + user_directory_deinit(&dir); + test_end(); +} + +static void test_user_directory_random(void) +{ + struct user_directory *dir; + struct mail_host *host = t_new(struct mail_host, 1); + time_t timestamp; + unsigned int i, count = 10000 + rand()%10000; + + test_begin("user directory random"); + dir = user_directory_init(USER_DIR_TIMEOUT, "%u"); + for (i = 0; i < count; i++) { + if (rand() % 10 == 0) + timestamp = (time_t)-1; + else + timestamp = ioloop_time-rand()%100; + user_directory_add(dir, i+1, host, timestamp); + } + verify_user_directory(dir, count); + user_directory_deinit(&dir); + test_end(); +} + +int main(void) +{ + static void (*test_functions[])(void) = { + test_user_directory_ascending, + test_user_directory_descending, + test_user_directory_random, + NULL + }; + ioloop_time = 1234567890; + return test_run(test_functions); +}
--- a/src/director/user-directory.c Thu Apr 19 20:29:25 2012 +0300 +++ b/src/director/user-directory.c Thu Apr 19 21:51:48 2012 +0300 @@ -24,6 +24,7 @@ struct hash_table *hash; /* sorted by time */ struct user *head, *tail; + struct user *prev_insert_pos; ARRAY_DEFINE(iters, struct user_directory_iter *); @@ -42,6 +43,9 @@ if ((*iterp)->pos == user) (*iterp)->pos = user->next; } + + if (dir->prev_insert_pos == user) + dir->prev_insert_pos = user->next; } static void user_free(struct user_directory *dir, struct user *user) @@ -79,11 +83,61 @@ return hash_table_lookup(dir->hash, POINTER_CAST(username_hash)); } +static void +user_directory_insert_backwards(struct user_directory *dir, + struct user *pos, struct user *user) +{ + for (; pos != NULL; pos = pos->prev) { + if ((time_t)pos->timestamp <= user->timestamp) + break; + } + if (pos == NULL) + DLLIST2_PREPEND(&dir->head, &dir->tail, user); + else { + user->prev = pos; + user->next = pos->next; + user->prev->next = user; + if (user->next != NULL) + user->next->prev = user; + else + dir->tail = user; + } +} + +static void +user_directory_insert_forwards(struct user_directory *dir, + struct user *pos, struct user *user) +{ + for (; pos != NULL; pos = pos->next) { + if ((time_t)pos->timestamp >= user->timestamp) + break; + } + if (pos == NULL) + DLLIST2_APPEND(&dir->head, &dir->tail, user); + else { + user->prev = pos->prev; + user->next = pos; + if (user->prev != NULL) + user->prev->next = user; + else + dir->head = user; + user->next->prev = user; + } +} + struct user * user_directory_add(struct user_directory *dir, unsigned int username_hash, struct mail_host *host, time_t timestamp) { - struct user *user, *pos; + struct user *user; + + if (timestamp == (time_t)-1) { + /* make sure we add it at the end */ + timestamp = ioloop_time; + if (dir->tail != NULL && + timestamp < (time_t)dir->tail->timestamp) + timestamp = (time_t)dir->tail->timestamp; + } user = i_new(struct user, 1); user->username_hash = username_hash; @@ -94,21 +148,24 @@ if (dir->tail == NULL || (time_t)dir->tail->timestamp <= timestamp) DLLIST2_APPEND(&dir->head, &dir->tail, user); else { - /* need to insert to correct position */ - for (pos = dir->tail; pos != NULL; pos = pos->prev) { - if ((time_t)pos->timestamp <= timestamp) - break; - } - if (pos == NULL) - DLLIST2_PREPEND(&dir->head, &dir->tail, user); - else { - user->prev = pos; - user->next = pos->next; - user->prev->next = user; - user->next->prev = user; + /* need to insert to correct position. we should get here + only when handshaking. the handshaking USER requests should + come sorted by timestamp. so keep track of the previous + insert position, the next USER should be inserted after + it. */ + if (dir->prev_insert_pos == NULL) { + /* find the position starting from tail */ + user_directory_insert_backwards(dir, dir->tail, user); + } else if (timestamp < dir->prev_insert_pos->timestamp) { + user_directory_insert_backwards(dir, dir->prev_insert_pos, + user); + } else { + user_directory_insert_forwards(dir, dir->prev_insert_pos, + user); } } + dir->prev_insert_pos = user; hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user); return user; }
--- a/src/director/user-directory.h Thu Apr 19 20:29:25 2012 +0300 +++ b/src/director/user-directory.h Thu Apr 19 21:51:48 2012 +0300 @@ -54,7 +54,8 @@ /* Look up username from directory. Returns NULL if not found. */ struct user *user_directory_lookup(struct user_directory *dir, unsigned int username_hash); -/* Add a user to directory and return it. */ +/* Add a user to directory and return it. If timestamp is (time_t)-1, + the current time is used. */ struct user * user_directory_add(struct user_directory *dir, unsigned int username_hash, struct mail_host *host, time_t timestamp);