bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen#include "lib.h"
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen#include "array.h"
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen#include "hash.h"
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen#include "sql-api-private.h"
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen#include "sql-db-cache.h"
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen#define SQL_DB_CACHE_CONTEXT(obj) \
883da299d066d73f2a7995b6718a19aea2f542efAki Tuomi MODULE_CONTEXT_REQUIRE(obj, sql_db_cache_module)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstruct sql_db_cache_context {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen union sql_db_module_context module_ctx;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db *prev, *next; /* These are set while refcount=0 */
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache *cache;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen int refcount;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen char *key;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen void (*orig_deinit)(struct sql_db *db);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen};
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstruct sql_db_cache {
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen HASH_TABLE(char *, struct sql_db *) dbs;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen unsigned int unused_count, max_unused_connections;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db *unused_tail, *unused_head;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen};
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(sql_db_cache_module, &sql_db_module_register);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstatic void sql_db_cache_db_deinit(struct sql_db *db)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen{
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache_context *ctx = SQL_DB_CACHE_CONTEXT(db);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache_context *head_ctx;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen if (--ctx->refcount > 0)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen return;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache->unused_count++;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen if (ctx->cache->unused_tail == NULL)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache->unused_tail = db;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen else {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen head_ctx = SQL_DB_CACHE_CONTEXT(ctx->cache->unused_head);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen head_ctx->next = db;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen }
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->prev = ctx->cache->unused_head;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache->unused_head = db;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen}
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstatic void sql_db_cache_unlink(struct sql_db_cache_context *ctx)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen{
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache_context *prev_ctx, *next_ctx;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen i_assert(ctx->refcount == 0);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen if (ctx->prev == NULL)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache->unused_tail = ctx->next;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen else {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen prev_ctx = SQL_DB_CACHE_CONTEXT(ctx->prev);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen prev_ctx->next = ctx->next;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen }
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen if (ctx->next == NULL)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache->unused_head = ctx->prev;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen else {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen next_ctx = SQL_DB_CACHE_CONTEXT(ctx->next);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen next_ctx->prev = ctx->prev;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen }
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache->unused_count--;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen}
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainenstatic void sql_db_cache_free_tail(struct sql_db_cache *cache)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen{
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db *db;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache_context *ctx;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen db = cache->unused_tail;
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen ctx = SQL_DB_CACHE_CONTEXT(db);
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen sql_db_cache_unlink(ctx);
0aaea0919a4980f31d2f43bc81de986b08dcc7dfTimo Sirainen hash_table_remove(cache->dbs, ctx->key);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen i_free(ctx->key);
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen ctx->orig_deinit(db);
300c5b822cde4a1d7247f49c9a0ab9f14c3ae37bTimo Sirainen i_free(ctx);
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen}
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainenstatic void sql_db_cache_drop_oldest(struct sql_db_cache *cache)
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen{
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen while (cache->unused_count >= cache->max_unused_connections)
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen sql_db_cache_free_tail(cache);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen}
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstruct sql_db *
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainensql_db_cache_new(struct sql_db_cache *cache,
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen const char *db_driver, const char *connect_string)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen{
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache_context *ctx;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db *db;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen char *key;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen key = i_strdup_printf("%s\t%s", db_driver, connect_string);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen db = hash_table_lookup(cache->dbs, key);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen if (db != NULL) {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx = SQL_DB_CACHE_CONTEXT(db);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen if (ctx->refcount == 0) {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen sql_db_cache_unlink(ctx);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->prev = ctx->next = NULL;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen }
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen i_free(key);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen } else {
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen sql_db_cache_drop_oldest(cache);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx = i_new(struct sql_db_cache_context, 1);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->cache = cache;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->key = key;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen db = sql_init(db_driver, connect_string);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->orig_deinit = db->v.deinit;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen db->v.deinit = sql_db_cache_db_deinit;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen MODULE_CONTEXT_SET(db, sql_db_cache_module, ctx);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen hash_table_insert(cache->dbs, ctx->key, db);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen }
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen ctx->refcount++;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen return db;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen}
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenstruct sql_db_cache *sql_db_cache_init(unsigned int max_unused_connections)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen{
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache *cache;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen cache = i_new(struct sql_db_cache, 1);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&cache->dbs, default_pool, 0, str_hash, strcmp);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen cache->max_unused_connections = max_unused_connections;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen return cache;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen}
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainenvoid sql_db_cache_deinit(struct sql_db_cache **_cache)
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen{
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen struct sql_db_cache *cache = *_cache;
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen *_cache = NULL;
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen while (cache->unused_tail != NULL)
51bfd4fbaf2b2148f8229f6a1650f28b61794effTimo Sirainen sql_db_cache_free_tail(cache);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen hash_table_destroy(&cache->dbs);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen i_free(cache);
b3e4b3e7585f644a4d95293ca7bca19bcbf70c50Timo Sirainen}