mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1036 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1036 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- c-basic-offset: 2 -*- */
 | |
| /*
 | |
|   Copyright(C) 2009-2017 Brazil
 | |
| 
 | |
|   This library is free software; you can redistribute it and/or
 | |
|   modify it under the terms of the GNU Lesser General Public
 | |
|   License version 2.1 as published by the Free Software Foundation.
 | |
| 
 | |
|   This library 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
 | |
|   Lesser General Public License for more details.
 | |
| 
 | |
|   You should have received a copy of the GNU Lesser General Public
 | |
|   License along with this library; if not, write to the Free Software
 | |
|   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | |
| */
 | |
| 
 | |
| #include "grn_cache.h"
 | |
| #include "grn_ctx.h"
 | |
| #include "grn_ctx_impl.h"
 | |
| #include "grn_hash.h"
 | |
| #include "grn_pat.h"
 | |
| #include "grn_store.h"
 | |
| #include "grn_db.h"
 | |
| #include "grn_file_lock.h"
 | |
| 
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| typedef struct _grn_cache_entry_memory grn_cache_entry_memory;
 | |
| 
 | |
| struct _grn_cache_entry_memory {
 | |
|   grn_cache_entry_memory *next;
 | |
|   grn_cache_entry_memory *prev;
 | |
|   grn_obj *value;
 | |
|   grn_timeval tv;
 | |
|   grn_id id;
 | |
| };
 | |
| 
 | |
| typedef struct _grn_cache_entry_persistent_data {
 | |
|   grn_id next;
 | |
|   grn_id prev;
 | |
|   grn_timeval modified_time;
 | |
| } grn_cache_entry_persistent_data;
 | |
| 
 | |
| /*
 | |
|   sizeof(grn_cache_entry_persistent_metadata) should be equal or smaller
 | |
|   than sizeof(grn_cache_entry_persistent_data).
 | |
|  */
 | |
| typedef struct _grn_cache_entry_persistent_metadata {
 | |
|   uint32_t max_nentries;
 | |
|   uint32_t nfetches;
 | |
|   uint32_t nhits;
 | |
| } grn_cache_entry_persistent_metadata;
 | |
| 
 | |
| typedef union _grn_cache_entry_persistent {
 | |
|   grn_cache_entry_persistent_data data;
 | |
|   grn_cache_entry_persistent_metadata metadata;
 | |
| } grn_cache_entry_persistent;
 | |
| 
 | |
| struct _grn_cache {
 | |
|   union {
 | |
|     struct {
 | |
|       grn_cache_entry_memory *next;
 | |
|       grn_cache_entry_memory *prev;
 | |
|       grn_hash *hash;
 | |
|       grn_mutex mutex;
 | |
|       uint32_t max_nentries;
 | |
|       uint32_t nfetches;
 | |
|       uint32_t nhits;
 | |
|     } memory;
 | |
|     struct {
 | |
|       grn_hash *keys;
 | |
|       grn_ja *values;
 | |
|       int timeout;
 | |
|     } persistent;
 | |
|   } impl;
 | |
|   grn_bool is_memory;
 | |
|   grn_ctx *ctx;
 | |
| };
 | |
| 
 | |
| #define GRN_CACHE_PERSISTENT_ROOT_ID 1
 | |
| #define GRN_CACHE_PERSISTENT_ROOT_KEY "\0"
 | |
| #define GRN_CACHE_PERSISTENT_ROOT_KEY_LEN \
 | |
|   (sizeof(GRN_CACHE_PERSISTENT_ROOT_KEY) - 1)
 | |
| #define GRN_CACHE_PERSISTENT_METADATA_ID 2
 | |
| #define GRN_CACHE_PERSISTENT_METADATA_KEY "\1"
 | |
| #define GRN_CACHE_PERSISTENT_METADATA_KEY_LEN \
 | |
|   (sizeof(GRN_CACHE_PERSISTENT_METADATA_KEY) - 1)
 | |
| 
 | |
| static grn_ctx grn_cache_ctx;
 | |
| static grn_cache *grn_cache_current = NULL;
 | |
| static grn_cache *grn_cache_default = NULL;
 | |
| static char grn_cache_default_base_path[PATH_MAX];
 | |
| 
 | |
| void
 | |
| grn_set_default_cache_base_path(const char *base_path)
 | |
| {
 | |
|   if (base_path) {
 | |
|     grn_strcpy(grn_cache_default_base_path,
 | |
|                PATH_MAX,
 | |
|                base_path);
 | |
|   } else {
 | |
|     grn_cache_default_base_path[0] = '\0';
 | |
|   }
 | |
| }
 | |
| 
 | |
| const char *
 | |
| grn_get_default_cache_base_path(void)
 | |
| {
 | |
|   if (grn_cache_default_base_path[0] == '\0') {
 | |
|     return NULL;
 | |
|   } else {
 | |
|     return grn_cache_default_base_path;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_open_memory(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   cache->impl.memory.next = (grn_cache_entry_memory *)cache;
 | |
|   cache->impl.memory.prev = (grn_cache_entry_memory *)cache;
 | |
|   cache->impl.memory.hash = grn_hash_create(cache->ctx,
 | |
|                                             NULL,
 | |
|                                             GRN_CACHE_MAX_KEY_SIZE,
 | |
|                                             sizeof(grn_cache_entry_memory),
 | |
|                                             GRN_OBJ_KEY_VAR_SIZE);
 | |
|   if (!cache->impl.memory.hash) {
 | |
|     ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to create hash table");
 | |
|     return;
 | |
|   }
 | |
|   MUTEX_INIT(cache->impl.memory.mutex);
 | |
| 
 | |
|   cache->impl.memory.max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
 | |
|   cache->impl.memory.nfetches = 0;
 | |
|   cache->impl.memory.nhits = 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_open_persistent(grn_ctx *ctx,
 | |
|                           grn_cache *cache,
 | |
|                           const char *base_path)
 | |
| {
 | |
|   grn_file_lock file_lock;
 | |
|   char *keys_path = NULL;
 | |
|   char *values_path = NULL;
 | |
|   char lock_path_buffer[PATH_MAX];
 | |
|   char keys_path_buffer[PATH_MAX];
 | |
|   char values_path_buffer[PATH_MAX];
 | |
| 
 | |
|   cache->impl.persistent.timeout = 1000;
 | |
| 
 | |
|   if (base_path) {
 | |
|     grn_snprintf(lock_path_buffer, PATH_MAX, PATH_MAX, "%s.lock", base_path);
 | |
|     grn_file_lock_init(ctx, &file_lock, lock_path_buffer);
 | |
|   } else {
 | |
|     grn_file_lock_init(ctx, &file_lock, NULL);
 | |
|   }
 | |
| 
 | |
|   if (base_path) {
 | |
|     struct stat stat_buffer;
 | |
| 
 | |
|     grn_snprintf(keys_path_buffer, PATH_MAX, PATH_MAX, "%s.keys", base_path);
 | |
|     grn_snprintf(values_path_buffer, PATH_MAX, PATH_MAX, "%s.values", base_path);
 | |
|     keys_path = keys_path_buffer;
 | |
|     values_path = values_path_buffer;
 | |
| 
 | |
|     if (!grn_file_lock_acquire(ctx,
 | |
|                                &file_lock,
 | |
|                                cache->impl.persistent.timeout,
 | |
|                                "[cache][persistent][open]")) {
 | |
|       goto exit;
 | |
|     }
 | |
| 
 | |
|     if (stat(keys_path, &stat_buffer) == 0) {
 | |
|       cache->impl.persistent.keys = grn_hash_open(ctx, keys_path);
 | |
|       if (cache->impl.persistent.keys) {
 | |
|         cache->impl.persistent.values = grn_ja_open(ctx, values_path);
 | |
|       }
 | |
|     }
 | |
|     if (!cache->impl.persistent.keys) {
 | |
|       if (cache->impl.persistent.values) {
 | |
|         grn_ja_close(ctx, cache->impl.persistent.values);
 | |
|         cache->impl.persistent.values = NULL;
 | |
|       }
 | |
|       if (stat(keys_path, &stat_buffer) == 0) {
 | |
|         if (grn_hash_remove(ctx, keys_path) != GRN_SUCCESS) {
 | |
|           ERRNO_ERR("[cache][persistent] "
 | |
|                     "failed to remove path for cache keys: <%s>",
 | |
|                     keys_path);
 | |
|           goto exit;
 | |
|         }
 | |
|       }
 | |
|       if (stat(values_path, &stat_buffer) == 0) {
 | |
|         if (grn_ja_remove(ctx, values_path) != GRN_SUCCESS) {
 | |
|           ERRNO_ERR("[cache][persistent] "
 | |
|                     "failed to remove path for cache values: <%s>",
 | |
|                     values_path);
 | |
|           goto exit;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!cache->impl.persistent.keys) {
 | |
|     cache->impl.persistent.keys =
 | |
|       grn_hash_create(ctx,
 | |
|                       keys_path,
 | |
|                       GRN_CACHE_MAX_KEY_SIZE,
 | |
|                       sizeof(grn_cache_entry_persistent),
 | |
|                       GRN_OBJ_KEY_VAR_SIZE);
 | |
|     if (!cache->impl.persistent.keys) {
 | |
|       ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | |
|           "[cache][persistent] failed to create cache keys storage: <%s>",
 | |
|           keys_path ? keys_path : "(memory)");
 | |
|       goto exit;
 | |
|     }
 | |
|     cache->impl.persistent.values =
 | |
|       grn_ja_create(ctx,
 | |
|                     values_path,
 | |
|                     1 << 16,
 | |
|                     0);
 | |
|     if (!cache->impl.persistent.values) {
 | |
|       grn_hash_close(ctx, cache->impl.persistent.keys);
 | |
|       ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | |
|           "[cache][persistent] failed to create cache values storage: <%s>",
 | |
|           values_path ? values_path : "(memory)");
 | |
|       goto exit;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     grn_cache_entry_persistent *entry;
 | |
|     grn_id root_id;
 | |
|     int added;
 | |
| 
 | |
|     root_id = grn_hash_add(ctx,
 | |
|                            cache->impl.persistent.keys,
 | |
|                            GRN_CACHE_PERSISTENT_ROOT_KEY,
 | |
|                            GRN_CACHE_PERSISTENT_ROOT_KEY_LEN,
 | |
|                            (void **)&entry,
 | |
|                            &added);
 | |
|     if (root_id != GRN_CACHE_PERSISTENT_ROOT_ID) {
 | |
|       grn_ja_close(ctx, cache->impl.persistent.values);
 | |
|       grn_hash_close(ctx, cache->impl.persistent.keys);
 | |
|       if (values_path) {
 | |
|         grn_ja_remove(ctx, values_path);
 | |
|       }
 | |
|       if (keys_path) {
 | |
|         grn_hash_remove(ctx, keys_path);
 | |
|       }
 | |
|       ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | |
|           "[cache][persistent] broken cache keys storage: broken root: <%s>",
 | |
|           keys_path ? keys_path : "(memory)");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (added) {
 | |
|       entry->data.next = root_id;
 | |
|       entry->data.prev = root_id;
 | |
|       entry->data.modified_time.tv_sec = 0;
 | |
|       entry->data.modified_time.tv_nsec = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     grn_cache_entry_persistent *entry;
 | |
|     grn_id metadata_id;
 | |
|     int added;
 | |
| 
 | |
|     metadata_id = grn_hash_add(ctx,
 | |
|                                cache->impl.persistent.keys,
 | |
|                                GRN_CACHE_PERSISTENT_METADATA_KEY,
 | |
|                                GRN_CACHE_PERSISTENT_METADATA_KEY_LEN,
 | |
|                                (void **)&entry,
 | |
|                                &added);
 | |
|     if (metadata_id != GRN_CACHE_PERSISTENT_METADATA_ID) {
 | |
|       grn_ja_close(ctx, cache->impl.persistent.values);
 | |
|       grn_hash_close(ctx, cache->impl.persistent.keys);
 | |
|       if (values_path) {
 | |
|         grn_ja_remove(ctx, values_path);
 | |
|       }
 | |
|       if (keys_path) {
 | |
|         grn_hash_remove(ctx, keys_path);
 | |
|       }
 | |
|       ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | |
|           "[cache][persistent] broken cache keys storage: broken metadata: <%s>",
 | |
|           keys_path ? keys_path : "(memory)");
 | |
|       goto exit;
 | |
|     }
 | |
| 
 | |
|     if (added) {
 | |
|       entry->metadata.max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
 | |
|       entry->metadata.nfetches = 0;
 | |
|       entry->metadata.nhits = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| exit :
 | |
|   grn_file_lock_release(ctx, &file_lock);
 | |
|   grn_file_lock_fin(ctx, &file_lock);
 | |
| }
 | |
| 
 | |
| static grn_cache *
 | |
| grn_cache_open_raw(grn_ctx *ctx,
 | |
|                    grn_bool is_memory,
 | |
|                    const char *base_path)
 | |
| {
 | |
|   grn_cache *cache = NULL;
 | |
| 
 | |
|   GRN_API_ENTER;
 | |
|   cache = GRN_CALLOC(sizeof(grn_cache));
 | |
|   if (!cache) {
 | |
|     ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to allocate grn_cache");
 | |
|     goto exit;
 | |
|   }
 | |
| 
 | |
|   cache->ctx = ctx;
 | |
|   cache->is_memory = is_memory;
 | |
|   if (cache->is_memory) {
 | |
|     grn_cache_open_memory(ctx, cache);
 | |
|   } else {
 | |
|     grn_cache_open_persistent(ctx, cache, base_path);
 | |
|   }
 | |
|   if (ctx->rc != GRN_SUCCESS) {
 | |
|     GRN_FREE(cache);
 | |
|     cache = NULL;
 | |
|     goto exit;
 | |
|   }
 | |
| 
 | |
| exit :
 | |
|   GRN_API_RETURN(cache);
 | |
| }
 | |
| 
 | |
| grn_cache *
 | |
| grn_cache_open(grn_ctx *ctx)
 | |
| {
 | |
|   const char *base_path = NULL;
 | |
|   grn_bool is_memory;
 | |
| 
 | |
|   if (grn_cache_default_base_path[0] != '\0') {
 | |
|     base_path = grn_cache_default_base_path;
 | |
|   }
 | |
| 
 | |
|   if (base_path) {
 | |
|     is_memory = GRN_FALSE;
 | |
|   } else {
 | |
|     char grn_cache_type_env[GRN_ENV_BUFFER_SIZE];
 | |
|     grn_getenv("GRN_CACHE_TYPE", grn_cache_type_env, GRN_ENV_BUFFER_SIZE);
 | |
|     if (strcmp(grn_cache_type_env, "persistent") == 0) {
 | |
|       is_memory = GRN_FALSE;
 | |
|     } else {
 | |
|       is_memory = GRN_TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return grn_cache_open_raw(ctx, is_memory, base_path);
 | |
| }
 | |
| 
 | |
| grn_cache *
 | |
| grn_persistent_cache_open(grn_ctx *ctx, const char *base_path)
 | |
| {
 | |
|   grn_bool is_memory = GRN_FALSE;
 | |
|   return grn_cache_open_raw(ctx, is_memory, base_path);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| grn_cache_close_memory(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   grn_cache_entry_memory *vp;
 | |
| 
 | |
|   GRN_HASH_EACH(ctx, cache->impl.memory.hash, id, NULL, NULL, &vp, {
 | |
|     grn_obj_close(ctx, vp->value);
 | |
|   });
 | |
|   grn_hash_close(ctx, cache->impl.memory.hash);
 | |
|   MUTEX_FIN(cache->impl.memory.mutex);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_close_persistent(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   grn_hash_close(ctx, cache->impl.persistent.keys);
 | |
|   grn_ja_close(ctx, cache->impl.persistent.values);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_cache_close(grn_ctx *ctx_not_used, grn_cache *cache)
 | |
| {
 | |
|   grn_ctx *ctx = cache->ctx;
 | |
| 
 | |
|   GRN_API_ENTER;
 | |
| 
 | |
|   if (cache->is_memory) {
 | |
|     grn_cache_close_memory(ctx, cache);
 | |
|   } else {
 | |
|     grn_cache_close_persistent(ctx, cache);
 | |
|   }
 | |
|   GRN_FREE(cache);
 | |
| 
 | |
|   GRN_API_RETURN(ctx->rc);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_cache_current_set(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   grn_cache_current = cache;
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| grn_cache *
 | |
| grn_cache_current_get(grn_ctx *ctx)
 | |
| {
 | |
|   return grn_cache_current;
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_cache_init(void)
 | |
| {
 | |
|   grn_ctx *ctx = &grn_cache_ctx;
 | |
| 
 | |
|   grn_ctx_init(ctx, 0);
 | |
| 
 | |
|   grn_cache_default = grn_cache_open(ctx);
 | |
|   grn_cache_current_set(ctx, grn_cache_default);
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_cache_default_reopen(void)
 | |
| {
 | |
|   grn_ctx *ctx = &grn_cache_ctx;
 | |
|   grn_cache *new_default;
 | |
|   grn_bool default_is_current;
 | |
| 
 | |
|   GRN_API_ENTER;
 | |
| 
 | |
|   new_default = grn_cache_open(ctx);
 | |
|   if (!new_default) {
 | |
|     GRN_API_RETURN(ctx->rc);
 | |
|   }
 | |
| 
 | |
|   default_is_current = (grn_cache_default == grn_cache_current_get(ctx));
 | |
|   if (default_is_current) {
 | |
|     grn_cache_current_set(ctx, new_default);
 | |
|   }
 | |
| 
 | |
|   if (grn_cache_default) {
 | |
|     grn_cache_close(ctx, grn_cache_default);
 | |
|   }
 | |
|   grn_cache_default = new_default;
 | |
| 
 | |
|   GRN_API_RETURN(ctx->rc);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_expire_entry_memory(grn_cache *cache, grn_cache_entry_memory *ce)
 | |
| {
 | |
|   ce->prev->next = ce->next;
 | |
|   ce->next->prev = ce->prev;
 | |
|   grn_obj_close(cache->ctx, ce->value);
 | |
|   grn_hash_delete_by_id(cache->ctx, cache->impl.memory.hash, ce->id, NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_entry_persistent_delete_link(grn_cache *cache,
 | |
|                                        grn_cache_entry_persistent *entry)
 | |
| {
 | |
|   grn_ctx *ctx = cache->ctx;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_cache_entry_persistent *prev_entry;
 | |
|   grn_cache_entry_persistent *next_entry;
 | |
| 
 | |
|   prev_entry =
 | |
|     (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                       keys,
 | |
|                                                       entry->data.prev,
 | |
|                                                       NULL);
 | |
|   next_entry =
 | |
|     (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                       keys,
 | |
|                                                       entry->data.next,
 | |
|                                                       NULL);
 | |
|   prev_entry->data.next = entry->data.next;
 | |
|   next_entry->data.prev = entry->data.prev;
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_entry_persistent_prepend_link(grn_cache *cache,
 | |
|                                         grn_cache_entry_persistent *entry,
 | |
|                                         grn_id entry_id,
 | |
|                                         grn_cache_entry_persistent *head_entry,
 | |
|                                         grn_id head_entry_id)
 | |
| {
 | |
|   grn_ctx *ctx = cache->ctx;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_cache_entry_persistent *head_next_entry;
 | |
| 
 | |
|   entry->data.next = head_entry->data.next;
 | |
|   entry->data.prev = head_entry_id;
 | |
|   head_next_entry =
 | |
|     (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                       keys,
 | |
|                                                       head_entry->data.next,
 | |
|                                                       NULL);
 | |
|   head_next_entry->data.prev = entry_id;
 | |
|   head_entry->data.next = entry_id;
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_expire_entry_persistent(grn_cache *cache,
 | |
|                                   grn_cache_entry_persistent *entry,
 | |
|                                   grn_id cache_id)
 | |
| {
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_ja *values = cache->impl.persistent.values;
 | |
| 
 | |
|   grn_cache_entry_persistent_delete_link(cache, entry);
 | |
|   grn_ja_put(cache->ctx, values, cache_id, NULL, 0, GRN_OBJ_SET, NULL);
 | |
|   grn_hash_delete_by_id(cache->ctx, keys, cache_id, NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_expire_memory_without_lock(grn_cache *cache, int32_t size)
 | |
| {
 | |
|   grn_cache_entry_memory *ce0 =
 | |
|     (grn_cache_entry_memory *)(&(cache->impl.memory));
 | |
|   while (ce0 != ce0->prev && size--) {
 | |
|     grn_cache_expire_entry_memory(cache, ce0->prev);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_expire_persistent_without_lock(grn_cache *cache, int32_t size)
 | |
| {
 | |
|   grn_ctx *ctx = cache->ctx;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_cache_entry_persistent *head_entry;
 | |
| 
 | |
|   head_entry =
 | |
|     (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                       keys,
 | |
|                                                       GRN_CACHE_PERSISTENT_ROOT_ID,
 | |
|                                                       NULL);
 | |
|   while (head_entry->data.prev != GRN_CACHE_PERSISTENT_ROOT_ID &&
 | |
|          size > 0) {
 | |
|     grn_cache_entry_persistent *tail_entry;
 | |
|     tail_entry =
 | |
|       (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                         keys,
 | |
|                                                         head_entry->data.prev,
 | |
|                                                         NULL);
 | |
|     grn_cache_expire_entry_persistent(cache, tail_entry, head_entry->data.prev);
 | |
|     size--;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static grn_rc
 | |
| grn_cache_set_max_n_entries_memory(grn_ctx *ctx,
 | |
|                                    grn_cache *cache,
 | |
|                                    unsigned int n)
 | |
| {
 | |
|   uint32_t current_max_n_entries;
 | |
| 
 | |
|   MUTEX_LOCK(cache->impl.memory.mutex);
 | |
|   current_max_n_entries = cache->impl.memory.max_nentries;
 | |
|   cache->impl.memory.max_nentries = n;
 | |
|   if (n < current_max_n_entries) {
 | |
|     grn_cache_expire_memory_without_lock(cache, current_max_n_entries - n);
 | |
|   }
 | |
|   MUTEX_UNLOCK(cache->impl.memory.mutex);
 | |
| 
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| static grn_rc
 | |
| grn_cache_set_max_n_entries_persistent(grn_ctx *ctx,
 | |
|                                        grn_cache *cache,
 | |
|                                        unsigned int n)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_cache_entry_persistent *metadata_entry;
 | |
|   uint32_t current_max_n_entries;
 | |
| 
 | |
|   rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
| 
 | |
|   metadata_entry =
 | |
|       (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                         keys,
 | |
|                                                         GRN_CACHE_PERSISTENT_METADATA_ID,
 | |
|                                                         NULL);
 | |
| 
 | |
|   current_max_n_entries = metadata_entry->metadata.max_nentries;
 | |
|   metadata_entry->metadata.max_nentries = n;
 | |
|   if (n < current_max_n_entries) {
 | |
|     grn_cache_expire_persistent_without_lock(cache, current_max_n_entries - n);
 | |
|   }
 | |
|   grn_io_unlock(keys->io);
 | |
| 
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_cache_set_max_n_entries(grn_ctx *ctx, grn_cache *cache, unsigned int n)
 | |
| {
 | |
|   if (!cache) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
| 
 | |
|   if (cache->is_memory) {
 | |
|     return grn_cache_set_max_n_entries_memory(cache->ctx, cache, n);
 | |
|   } else {
 | |
|     return grn_cache_set_max_n_entries_persistent(cache->ctx, cache, n);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static uint32_t
 | |
| grn_cache_get_max_n_entries_memory(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   return cache->impl.memory.max_nentries;
 | |
| }
 | |
| 
 | |
| static uint32_t
 | |
| grn_cache_get_max_n_entries_persistent(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_cache_entry_persistent *metadata_entry;
 | |
|   uint32_t current_max_n_entries;
 | |
| 
 | |
|   rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   metadata_entry =
 | |
|       (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                         keys,
 | |
|                                                         GRN_CACHE_PERSISTENT_METADATA_ID,
 | |
|                                                         NULL);
 | |
|   current_max_n_entries = metadata_entry->metadata.max_nentries;
 | |
|   grn_io_unlock(keys->io);
 | |
| 
 | |
|   return current_max_n_entries;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| grn_cache_get_max_n_entries(grn_ctx *ctx, grn_cache *cache)
 | |
| {
 | |
|   if (!cache) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   if (cache->is_memory) {
 | |
|     return grn_cache_get_max_n_entries_memory(cache->ctx, cache);
 | |
|   } else {
 | |
|     return grn_cache_get_max_n_entries_persistent(cache->ctx, cache);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_get_statistics_memory(grn_ctx *ctx, grn_cache *cache,
 | |
|                                 grn_cache_statistics *statistics)
 | |
| {
 | |
|   MUTEX_LOCK(cache->impl.memory.mutex);
 | |
|   statistics->nentries = GRN_HASH_SIZE(cache->impl.memory.hash);
 | |
|   statistics->max_nentries = cache->impl.memory.max_nentries;
 | |
|   statistics->nfetches = cache->impl.memory.nfetches;
 | |
|   statistics->nhits = cache->impl.memory.nhits;
 | |
|   MUTEX_UNLOCK(cache->impl.memory.mutex);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_get_statistics_persistent(grn_ctx *ctx, grn_cache *cache,
 | |
|                                     grn_cache_statistics *statistics)
 | |
| {
 | |
|   grn_rc rc = GRN_INVALID_ARGUMENT;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_cache_entry_persistent *metadata_entry;
 | |
| 
 | |
|   rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   metadata_entry =
 | |
|       (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                         keys,
 | |
|                                                         GRN_CACHE_PERSISTENT_METADATA_ID,
 | |
|                                                         NULL);
 | |
| 
 | |
|   statistics->nentries = GRN_HASH_SIZE(keys);
 | |
|   statistics->max_nentries = metadata_entry->metadata.max_nentries;
 | |
|   statistics->nfetches = metadata_entry->metadata.nfetches;
 | |
|   statistics->nhits = metadata_entry->metadata.nhits;
 | |
| 
 | |
|   grn_io_unlock(keys->io);
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_cache_get_statistics(grn_ctx *ctx, grn_cache *cache,
 | |
|                          grn_cache_statistics *statistics)
 | |
| {
 | |
|   if (cache->is_memory) {
 | |
|     return grn_cache_get_statistics_memory(ctx, cache, statistics);
 | |
|   } else {
 | |
|     return grn_cache_get_statistics_persistent(ctx, cache, statistics);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static grn_rc
 | |
| grn_cache_fetch_memory(grn_ctx *ctx, grn_cache *cache,
 | |
|                        const char *key, uint32_t key_len,
 | |
|                        grn_obj *output)
 | |
| {
 | |
|   /* TODO: How about GRN_NOT_FOUND? */
 | |
|   grn_rc rc = GRN_INVALID_ARGUMENT;
 | |
|   grn_cache_entry_memory *ce;
 | |
| 
 | |
|   MUTEX_LOCK(cache->impl.memory.mutex);
 | |
|   cache->impl.memory.nfetches++;
 | |
|   if (grn_hash_get(cache->ctx, cache->impl.memory.hash, key, key_len,
 | |
|                    (void **)&ce)) {
 | |
|     if (ce->tv.tv_sec <= grn_db_get_last_modified(ctx, ctx->impl->db)) {
 | |
|       grn_cache_expire_entry_memory(cache, ce);
 | |
|       goto exit;
 | |
|     }
 | |
|     rc = GRN_SUCCESS;
 | |
|     GRN_TEXT_PUT(ctx,
 | |
|                  output,
 | |
|                  GRN_TEXT_VALUE(ce->value),
 | |
|                  GRN_TEXT_LEN(ce->value));
 | |
|     ce->prev->next = ce->next;
 | |
|     ce->next->prev = ce->prev;
 | |
|     {
 | |
|       grn_cache_entry_memory *ce0 =
 | |
|         (grn_cache_entry_memory *)(&(cache->impl.memory));
 | |
|       ce->next = ce0->next;
 | |
|       ce->prev = ce0;
 | |
|       ce0->next->prev = ce;
 | |
|       ce0->next = ce;
 | |
|     }
 | |
|     cache->impl.memory.nhits++;
 | |
|   }
 | |
| exit :
 | |
|   MUTEX_UNLOCK(cache->impl.memory.mutex);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| static grn_rc
 | |
| grn_cache_fetch_persistent(grn_ctx *ctx, grn_cache *cache,
 | |
|                            const char *key, uint32_t key_len,
 | |
|                            grn_obj *output)
 | |
| {
 | |
|   /* TODO: How about GRN_NOT_FOUND? */
 | |
|   grn_rc rc = GRN_INVALID_ARGUMENT;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_ja *values = cache->impl.persistent.values;
 | |
|   grn_id cache_id;
 | |
|   grn_cache_entry_persistent *entry;
 | |
|   grn_cache_entry_persistent *metadata_entry;
 | |
| 
 | |
|   if (key_len == GRN_CACHE_PERSISTENT_ROOT_KEY_LEN &&
 | |
|       memcmp(key,
 | |
|              GRN_CACHE_PERSISTENT_ROOT_KEY,
 | |
|              GRN_CACHE_PERSISTENT_ROOT_KEY_LEN) == 0) {
 | |
|     return rc;
 | |
|   }
 | |
| 
 | |
|   rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
| 
 | |
|   /* TODO: How about GRN_NOT_FOUND? */
 | |
|   rc = GRN_INVALID_ARGUMENT;
 | |
| 
 | |
|   metadata_entry =
 | |
|       (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                         keys,
 | |
|                                                         GRN_CACHE_PERSISTENT_METADATA_ID,
 | |
|                                                         NULL);
 | |
|   metadata_entry->metadata.nfetches++;
 | |
| 
 | |
|   cache_id = grn_hash_get(cache->ctx, keys, key, key_len, (void **)&entry);
 | |
|   if (cache_id == GRN_ID_NIL) {
 | |
|     goto exit;
 | |
|   }
 | |
| 
 | |
|   if (cache_id != GRN_ID_NIL) {
 | |
|     if (entry->data.modified_time.tv_sec <=
 | |
|         grn_db_get_last_modified(ctx, ctx->impl->db)) {
 | |
|       grn_cache_expire_entry_persistent(cache, entry, cache_id);
 | |
|       goto exit;
 | |
|     }
 | |
| 
 | |
|     rc = GRN_SUCCESS;
 | |
|     grn_ja_get_value(ctx, values, cache_id, output);
 | |
|     grn_cache_entry_persistent_delete_link(cache, entry);
 | |
|     {
 | |
|       grn_cache_entry_persistent *head_entry;
 | |
|       head_entry =
 | |
|         (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                           keys,
 | |
|                                                           GRN_CACHE_PERSISTENT_ROOT_ID,
 | |
|                                                           NULL);
 | |
|       grn_cache_entry_persistent_prepend_link(cache,
 | |
|                                               entry,
 | |
|                                               cache_id,
 | |
|                                               head_entry,
 | |
|                                               GRN_CACHE_PERSISTENT_ROOT_ID);
 | |
|     }
 | |
|     metadata_entry->metadata.nhits++;
 | |
|   }
 | |
| 
 | |
| exit :
 | |
|   grn_io_unlock(keys->io);
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| grn_rc
 | |
| grn_cache_fetch(grn_ctx *ctx, grn_cache *cache,
 | |
|                 const char *key, uint32_t key_len,
 | |
|                 grn_obj *output)
 | |
| {
 | |
|   if (!ctx->impl || !ctx->impl->db) { return GRN_INVALID_ARGUMENT; }
 | |
| 
 | |
|   if (cache->is_memory) {
 | |
|     return grn_cache_fetch_memory(ctx, cache, key, key_len, output);
 | |
|   } else {
 | |
|     return grn_cache_fetch_persistent(ctx, cache, key, key_len, output);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_update_memory(grn_ctx *ctx, grn_cache *cache,
 | |
|                         const char *key, uint32_t key_len,
 | |
|                         grn_obj *value)
 | |
| {
 | |
|   grn_id id;
 | |
|   int added = 0;
 | |
|   grn_cache_entry_memory *ce;
 | |
|   grn_rc rc = GRN_SUCCESS;
 | |
|   grn_obj *old = NULL;
 | |
|   grn_obj *obj = NULL;
 | |
| 
 | |
|   if (cache->impl.memory.max_nentries == 0) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MUTEX_LOCK(cache->impl.memory.mutex);
 | |
|   obj = grn_obj_open(cache->ctx, GRN_BULK, 0, GRN_DB_TEXT);
 | |
|   if (!obj) {
 | |
|     goto exit;
 | |
|   }
 | |
|   GRN_TEXT_PUT(cache->ctx, obj, GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));
 | |
|   id = grn_hash_add(cache->ctx, cache->impl.memory.hash, key, key_len,
 | |
|                     (void **)&ce, &added);
 | |
|   if (id) {
 | |
|     if (!added) {
 | |
|       old = ce->value;
 | |
|       ce->prev->next = ce->next;
 | |
|       ce->next->prev = ce->prev;
 | |
|     }
 | |
|     ce->id = id;
 | |
|     ce->value = obj;
 | |
|     ce->tv = ctx->impl->tv;
 | |
|     {
 | |
|       grn_cache_entry_memory *ce0 =
 | |
|         (grn_cache_entry_memory *)(&(cache->impl.memory));
 | |
|       ce->next = ce0->next;
 | |
|       ce->prev = ce0;
 | |
|       ce0->next->prev = ce;
 | |
|       ce0->next = ce;
 | |
|     }
 | |
|     if (GRN_HASH_SIZE(cache->impl.memory.hash) >
 | |
|         cache->impl.memory.max_nentries) {
 | |
|       grn_cache_expire_entry_memory(cache, cache->impl.memory.prev);
 | |
|     }
 | |
|   } else {
 | |
|     rc = GRN_NO_MEMORY_AVAILABLE;
 | |
|   }
 | |
| exit :
 | |
|   if (rc) { grn_obj_close(cache->ctx, obj); }
 | |
|   if (old) { grn_obj_close(cache->ctx, old); }
 | |
|   MUTEX_UNLOCK(cache->impl.memory.mutex);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_update_persistent(grn_ctx *ctx, grn_cache *cache,
 | |
|                             const char *key, uint32_t key_len,
 | |
|                             grn_obj *value)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
|   grn_ja *values = cache->impl.persistent.values;
 | |
|   grn_cache_entry_persistent *metadata_entry;
 | |
|   grn_id cache_id;
 | |
|   grn_cache_entry_persistent *entry;
 | |
|   int added;
 | |
| 
 | |
|   if (key_len == GRN_CACHE_PERSISTENT_ROOT_KEY_LEN &&
 | |
|       memcmp(key,
 | |
|              GRN_CACHE_PERSISTENT_ROOT_KEY,
 | |
|              GRN_CACHE_PERSISTENT_ROOT_KEY_LEN) == 0) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (key_len == GRN_CACHE_PERSISTENT_METADATA_KEY_LEN &&
 | |
|       memcmp(key,
 | |
|              GRN_CACHE_PERSISTENT_METADATA_KEY,
 | |
|              GRN_CACHE_PERSISTENT_METADATA_KEY_LEN) == 0) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   metadata_entry =
 | |
|     (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                       keys,
 | |
|                                                       GRN_CACHE_PERSISTENT_METADATA_ID,
 | |
|                                                       NULL);
 | |
|   if (metadata_entry->metadata.max_nentries == 0) {
 | |
|     goto exit;
 | |
|   }
 | |
| 
 | |
|   cache_id = grn_hash_add(cache->ctx, keys, key, key_len, (void **)&entry,
 | |
|                           &added);
 | |
|   if (cache_id) {
 | |
|     grn_cache_entry_persistent *head_entry;
 | |
| 
 | |
|     if (!added) {
 | |
|       grn_cache_entry_persistent_delete_link(cache, entry);
 | |
|     }
 | |
|     entry->data.modified_time = ctx->impl->tv;
 | |
| 
 | |
|     grn_ja_put(cache->ctx, values, cache_id,
 | |
|                GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value),
 | |
|                GRN_OBJ_SET, NULL);
 | |
| 
 | |
|     head_entry =
 | |
|       (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                         keys,
 | |
|                                                         GRN_CACHE_PERSISTENT_ROOT_ID,
 | |
|                                                         NULL);
 | |
|     grn_cache_entry_persistent_prepend_link(cache,
 | |
|                                             entry,
 | |
|                                             cache_id,
 | |
|                                             head_entry,
 | |
|                                             GRN_CACHE_PERSISTENT_ROOT_ID);
 | |
|     if (GRN_HASH_SIZE(keys) > metadata_entry->metadata.max_nentries) {
 | |
|       grn_cache_entry_persistent *tail_entry;
 | |
|       tail_entry =
 | |
|         (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | |
|                                                           keys,
 | |
|                                                           head_entry->data.prev,
 | |
|                                                           NULL);
 | |
|       grn_cache_expire_entry_persistent(cache,
 | |
|                                         tail_entry,
 | |
|                                         head_entry->data.prev);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| exit :
 | |
|   grn_io_unlock(keys->io);
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_cache_update(grn_ctx *ctx, grn_cache *cache,
 | |
|                  const char *key, uint32_t key_len, grn_obj *value)
 | |
| {
 | |
|   if (!ctx->impl) { return; }
 | |
| 
 | |
|   if (cache->is_memory) {
 | |
|     grn_cache_update_memory(ctx, cache, key, key_len, value);
 | |
|   } else {
 | |
|     grn_cache_update_persistent(ctx, cache, key, key_len, value);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_expire_memory(grn_cache *cache, int32_t size)
 | |
| {
 | |
|   MUTEX_LOCK(cache->impl.memory.mutex);
 | |
|   grn_cache_expire_memory_without_lock(cache, size);
 | |
|   MUTEX_UNLOCK(cache->impl.memory.mutex);
 | |
| }
 | |
| 
 | |
| static void
 | |
| grn_cache_expire_persistent(grn_cache *cache, int32_t size)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ctx *ctx = cache->ctx;
 | |
|   grn_hash *keys = cache->impl.persistent.keys;
 | |
| 
 | |
|   rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   grn_cache_expire_persistent_without_lock(cache, size);
 | |
| 
 | |
|   grn_io_unlock(keys->io);
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_cache_expire(grn_cache *cache, int32_t size)
 | |
| {
 | |
|   if (cache->is_memory) {
 | |
|     grn_cache_expire_memory(cache, size);
 | |
|   } else {
 | |
|     grn_cache_expire_persistent(cache, size);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| grn_cache_fin(void)
 | |
| {
 | |
|   grn_ctx *ctx = &grn_cache_ctx;
 | |
| 
 | |
|   grn_cache_current_set(ctx, NULL);
 | |
| 
 | |
|   if (grn_cache_default) {
 | |
|     grn_cache_close(ctx, grn_cache_default);
 | |
|     grn_cache_default = NULL;
 | |
|   }
 | |
| 
 | |
|   grn_ctx_fin(ctx);
 | |
| }
 | 
