1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2009-2017 Brazil
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License version 2.1 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19#include "grn_cache.h"
20#include "grn_ctx.h"
21#include "grn_ctx_impl.h"
22#include "grn_hash.h"
23#include "grn_pat.h"
24#include "grn_store.h"
25#include "grn_db.h"
26#include "grn_file_lock.h"
27
28#include <sys/stat.h>
29
30typedef struct _grn_cache_entry_memory grn_cache_entry_memory;
31
32struct _grn_cache_entry_memory {
33 grn_cache_entry_memory *next;
34 grn_cache_entry_memory *prev;
35 grn_obj *value;
36 grn_timeval tv;
37 grn_id id;
38};
39
40typedef struct _grn_cache_entry_persistent_data {
41 grn_id next;
42 grn_id prev;
43 grn_timeval modified_time;
44} grn_cache_entry_persistent_data;
45
46/*
47 sizeof(grn_cache_entry_persistent_metadata) should be equal or smaller
48 than sizeof(grn_cache_entry_persistent_data).
49 */
50typedef struct _grn_cache_entry_persistent_metadata {
51 uint32_t max_nentries;
52 uint32_t nfetches;
53 uint32_t nhits;
54} grn_cache_entry_persistent_metadata;
55
56typedef union _grn_cache_entry_persistent {
57 grn_cache_entry_persistent_data data;
58 grn_cache_entry_persistent_metadata metadata;
59} grn_cache_entry_persistent;
60
61struct _grn_cache {
62 union {
63 struct {
64 grn_cache_entry_memory *next;
65 grn_cache_entry_memory *prev;
66 grn_hash *hash;
67 grn_mutex mutex;
68 uint32_t max_nentries;
69 uint32_t nfetches;
70 uint32_t nhits;
71 } memory;
72 struct {
73 grn_hash *keys;
74 grn_ja *values;
75 int timeout;
76 } persistent;
77 } impl;
78 grn_bool is_memory;
79 grn_ctx *ctx;
80};
81
82#define GRN_CACHE_PERSISTENT_ROOT_ID 1
83#define GRN_CACHE_PERSISTENT_ROOT_KEY "\0"
84#define GRN_CACHE_PERSISTENT_ROOT_KEY_LEN \
85 (sizeof(GRN_CACHE_PERSISTENT_ROOT_KEY) - 1)
86#define GRN_CACHE_PERSISTENT_METADATA_ID 2
87#define GRN_CACHE_PERSISTENT_METADATA_KEY "\1"
88#define GRN_CACHE_PERSISTENT_METADATA_KEY_LEN \
89 (sizeof(GRN_CACHE_PERSISTENT_METADATA_KEY) - 1)
90
91static grn_ctx grn_cache_ctx;
92static grn_cache *grn_cache_current = NULL;
93static grn_cache *grn_cache_default = NULL;
94static char grn_cache_default_base_path[PATH_MAX];
95
96void
97grn_set_default_cache_base_path(const char *base_path)
98{
99 if (base_path) {
100 grn_strcpy(grn_cache_default_base_path,
101 PATH_MAX,
102 base_path);
103 } else {
104 grn_cache_default_base_path[0] = '\0';
105 }
106}
107
108const char *
109grn_get_default_cache_base_path(void)
110{
111 if (grn_cache_default_base_path[0] == '\0') {
112 return NULL;
113 } else {
114 return grn_cache_default_base_path;
115 }
116}
117
118static void
119grn_cache_open_memory(grn_ctx *ctx, grn_cache *cache)
120{
121 cache->impl.memory.next = (grn_cache_entry_memory *)cache;
122 cache->impl.memory.prev = (grn_cache_entry_memory *)cache;
123 cache->impl.memory.hash = grn_hash_create(cache->ctx,
124 NULL,
125 GRN_CACHE_MAX_KEY_SIZE,
126 sizeof(grn_cache_entry_memory),
127 GRN_OBJ_KEY_VAR_SIZE);
128 if (!cache->impl.memory.hash) {
129 ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to create hash table");
130 return;
131 }
132 MUTEX_INIT(cache->impl.memory.mutex);
133
134 cache->impl.memory.max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
135 cache->impl.memory.nfetches = 0;
136 cache->impl.memory.nhits = 0;
137}
138
139static void
140grn_cache_open_persistent(grn_ctx *ctx,
141 grn_cache *cache,
142 const char *base_path)
143{
144 grn_file_lock file_lock;
145 char *keys_path = NULL;
146 char *values_path = NULL;
147 char lock_path_buffer[PATH_MAX];
148 char keys_path_buffer[PATH_MAX];
149 char values_path_buffer[PATH_MAX];
150
151 cache->impl.persistent.timeout = 1000;
152
153 if (base_path) {
154 grn_snprintf(lock_path_buffer, PATH_MAX, PATH_MAX, "%s.lock", base_path);
155 grn_file_lock_init(ctx, &file_lock, lock_path_buffer);
156 } else {
157 grn_file_lock_init(ctx, &file_lock, NULL);
158 }
159
160 if (base_path) {
161 struct stat stat_buffer;
162
163 grn_snprintf(keys_path_buffer, PATH_MAX, PATH_MAX, "%s.keys", base_path);
164 grn_snprintf(values_path_buffer, PATH_MAX, PATH_MAX, "%s.values", base_path);
165 keys_path = keys_path_buffer;
166 values_path = values_path_buffer;
167
168 if (!grn_file_lock_acquire(ctx,
169 &file_lock,
170 cache->impl.persistent.timeout,
171 "[cache][persistent][open]")) {
172 goto exit;
173 }
174
175 if (stat(keys_path, &stat_buffer) == 0) {
176 cache->impl.persistent.keys = grn_hash_open(ctx, keys_path);
177 if (cache->impl.persistent.keys) {
178 cache->impl.persistent.values = grn_ja_open(ctx, values_path);
179 }
180 }
181 if (!cache->impl.persistent.keys) {
182 if (cache->impl.persistent.values) {
183 grn_ja_close(ctx, cache->impl.persistent.values);
184 cache->impl.persistent.values = NULL;
185 }
186 if (stat(keys_path, &stat_buffer) == 0) {
187 if (grn_hash_remove(ctx, keys_path) != GRN_SUCCESS) {
188 ERRNO_ERR("[cache][persistent] "
189 "failed to remove path for cache keys: <%s>",
190 keys_path);
191 goto exit;
192 }
193 }
194 if (stat(values_path, &stat_buffer) == 0) {
195 if (grn_ja_remove(ctx, values_path) != GRN_SUCCESS) {
196 ERRNO_ERR("[cache][persistent] "
197 "failed to remove path for cache values: <%s>",
198 values_path);
199 goto exit;
200 }
201 }
202 }
203 }
204
205 if (!cache->impl.persistent.keys) {
206 cache->impl.persistent.keys =
207 grn_hash_create(ctx,
208 keys_path,
209 GRN_CACHE_MAX_KEY_SIZE,
210 sizeof(grn_cache_entry_persistent),
211 GRN_OBJ_KEY_VAR_SIZE);
212 if (!cache->impl.persistent.keys) {
213 ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
214 "[cache][persistent] failed to create cache keys storage: <%s>",
215 keys_path ? keys_path : "(memory)");
216 goto exit;
217 }
218 cache->impl.persistent.values =
219 grn_ja_create(ctx,
220 values_path,
221 1 << 16,
222 0);
223 if (!cache->impl.persistent.values) {
224 grn_hash_close(ctx, cache->impl.persistent.keys);
225 ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
226 "[cache][persistent] failed to create cache values storage: <%s>",
227 values_path ? values_path : "(memory)");
228 goto exit;
229 }
230 }
231
232 {
233 grn_cache_entry_persistent *entry;
234 grn_id root_id;
235 int added;
236
237 root_id = grn_hash_add(ctx,
238 cache->impl.persistent.keys,
239 GRN_CACHE_PERSISTENT_ROOT_KEY,
240 GRN_CACHE_PERSISTENT_ROOT_KEY_LEN,
241 (void **)&entry,
242 &added);
243 if (root_id != GRN_CACHE_PERSISTENT_ROOT_ID) {
244 grn_ja_close(ctx, cache->impl.persistent.values);
245 grn_hash_close(ctx, cache->impl.persistent.keys);
246 if (values_path) {
247 grn_ja_remove(ctx, values_path);
248 }
249 if (keys_path) {
250 grn_hash_remove(ctx, keys_path);
251 }
252 ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
253 "[cache][persistent] broken cache keys storage: broken root: <%s>",
254 keys_path ? keys_path : "(memory)");
255 return;
256 }
257
258 if (added) {
259 entry->data.next = root_id;
260 entry->data.prev = root_id;
261 entry->data.modified_time.tv_sec = 0;
262 entry->data.modified_time.tv_nsec = 0;
263 }
264 }
265
266 {
267 grn_cache_entry_persistent *entry;
268 grn_id metadata_id;
269 int added;
270
271 metadata_id = grn_hash_add(ctx,
272 cache->impl.persistent.keys,
273 GRN_CACHE_PERSISTENT_METADATA_KEY,
274 GRN_CACHE_PERSISTENT_METADATA_KEY_LEN,
275 (void **)&entry,
276 &added);
277 if (metadata_id != GRN_CACHE_PERSISTENT_METADATA_ID) {
278 grn_ja_close(ctx, cache->impl.persistent.values);
279 grn_hash_close(ctx, cache->impl.persistent.keys);
280 if (values_path) {
281 grn_ja_remove(ctx, values_path);
282 }
283 if (keys_path) {
284 grn_hash_remove(ctx, keys_path);
285 }
286 ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
287 "[cache][persistent] broken cache keys storage: broken metadata: <%s>",
288 keys_path ? keys_path : "(memory)");
289 goto exit;
290 }
291
292 if (added) {
293 entry->metadata.max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
294 entry->metadata.nfetches = 0;
295 entry->metadata.nhits = 0;
296 }
297 }
298
299exit :
300 grn_file_lock_release(ctx, &file_lock);
301 grn_file_lock_fin(ctx, &file_lock);
302}
303
304static grn_cache *
305grn_cache_open_raw(grn_ctx *ctx,
306 grn_bool is_memory,
307 const char *base_path)
308{
309 grn_cache *cache = NULL;
310
311 GRN_API_ENTER;
312 cache = GRN_CALLOC(sizeof(grn_cache));
313 if (!cache) {
314 ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to allocate grn_cache");
315 goto exit;
316 }
317
318 cache->ctx = ctx;
319 cache->is_memory = is_memory;
320 if (cache->is_memory) {
321 grn_cache_open_memory(ctx, cache);
322 } else {
323 grn_cache_open_persistent(ctx, cache, base_path);
324 }
325 if (ctx->rc != GRN_SUCCESS) {
326 GRN_FREE(cache);
327 cache = NULL;
328 goto exit;
329 }
330
331exit :
332 GRN_API_RETURN(cache);
333}
334
335grn_cache *
336grn_cache_open(grn_ctx *ctx)
337{
338 const char *base_path = NULL;
339 grn_bool is_memory;
340
341 if (grn_cache_default_base_path[0] != '\0') {
342 base_path = grn_cache_default_base_path;
343 }
344
345 if (base_path) {
346 is_memory = GRN_FALSE;
347 } else {
348 char grn_cache_type_env[GRN_ENV_BUFFER_SIZE];
349 grn_getenv("GRN_CACHE_TYPE", grn_cache_type_env, GRN_ENV_BUFFER_SIZE);
350 if (strcmp(grn_cache_type_env, "persistent") == 0) {
351 is_memory = GRN_FALSE;
352 } else {
353 is_memory = GRN_TRUE;
354 }
355 }
356
357 return grn_cache_open_raw(ctx, is_memory, base_path);
358}
359
360grn_cache *
361grn_persistent_cache_open(grn_ctx *ctx, const char *base_path)
362{
363 grn_bool is_memory = GRN_FALSE;
364 return grn_cache_open_raw(ctx, is_memory, base_path);
365}
366
367
368static void
369grn_cache_close_memory(grn_ctx *ctx, grn_cache *cache)
370{
371 grn_cache_entry_memory *vp;
372
373 GRN_HASH_EACH(ctx, cache->impl.memory.hash, id, NULL, NULL, &vp, {
374 grn_obj_close(ctx, vp->value);
375 });
376 grn_hash_close(ctx, cache->impl.memory.hash);
377 MUTEX_FIN(cache->impl.memory.mutex);
378}
379
380static void
381grn_cache_close_persistent(grn_ctx *ctx, grn_cache *cache)
382{
383 grn_hash_close(ctx, cache->impl.persistent.keys);
384 grn_ja_close(ctx, cache->impl.persistent.values);
385}
386
387grn_rc
388grn_cache_close(grn_ctx *ctx_not_used, grn_cache *cache)
389{
390 grn_ctx *ctx = cache->ctx;
391
392 GRN_API_ENTER;
393
394 if (cache->is_memory) {
395 grn_cache_close_memory(ctx, cache);
396 } else {
397 grn_cache_close_persistent(ctx, cache);
398 }
399 GRN_FREE(cache);
400
401 GRN_API_RETURN(ctx->rc);
402}
403
404grn_rc
405grn_cache_current_set(grn_ctx *ctx, grn_cache *cache)
406{
407 grn_cache_current = cache;
408 return GRN_SUCCESS;
409}
410
411grn_cache *
412grn_cache_current_get(grn_ctx *ctx)
413{
414 return grn_cache_current;
415}
416
417void
418grn_cache_init(void)
419{
420 grn_ctx *ctx = &grn_cache_ctx;
421
422 grn_ctx_init(ctx, 0);
423
424 grn_cache_default = grn_cache_open(ctx);
425 grn_cache_current_set(ctx, grn_cache_default);
426}
427
428grn_rc
429grn_cache_default_reopen(void)
430{
431 grn_ctx *ctx = &grn_cache_ctx;
432 grn_cache *new_default;
433 grn_bool default_is_current;
434
435 GRN_API_ENTER;
436
437 new_default = grn_cache_open(ctx);
438 if (!new_default) {
439 GRN_API_RETURN(ctx->rc);
440 }
441
442 default_is_current = (grn_cache_default == grn_cache_current_get(ctx));
443 if (default_is_current) {
444 grn_cache_current_set(ctx, new_default);
445 }
446
447 if (grn_cache_default) {
448 grn_cache_close(ctx, grn_cache_default);
449 }
450 grn_cache_default = new_default;
451
452 GRN_API_RETURN(ctx->rc);
453}
454
455static void
456grn_cache_expire_entry_memory(grn_cache *cache, grn_cache_entry_memory *ce)
457{
458 ce->prev->next = ce->next;
459 ce->next->prev = ce->prev;
460 grn_obj_close(cache->ctx, ce->value);
461 grn_hash_delete_by_id(cache->ctx, cache->impl.memory.hash, ce->id, NULL);
462}
463
464static void
465grn_cache_entry_persistent_delete_link(grn_cache *cache,
466 grn_cache_entry_persistent *entry)
467{
468 grn_ctx *ctx = cache->ctx;
469 grn_hash *keys = cache->impl.persistent.keys;
470 grn_cache_entry_persistent *prev_entry;
471 grn_cache_entry_persistent *next_entry;
472
473 prev_entry =
474 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
475 keys,
476 entry->data.prev,
477 NULL);
478 next_entry =
479 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
480 keys,
481 entry->data.next,
482 NULL);
483 prev_entry->data.next = entry->data.next;
484 next_entry->data.prev = entry->data.prev;
485}
486
487static void
488grn_cache_entry_persistent_prepend_link(grn_cache *cache,
489 grn_cache_entry_persistent *entry,
490 grn_id entry_id,
491 grn_cache_entry_persistent *head_entry,
492 grn_id head_entry_id)
493{
494 grn_ctx *ctx = cache->ctx;
495 grn_hash *keys = cache->impl.persistent.keys;
496 grn_cache_entry_persistent *head_next_entry;
497
498 entry->data.next = head_entry->data.next;
499 entry->data.prev = head_entry_id;
500 head_next_entry =
501 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
502 keys,
503 head_entry->data.next,
504 NULL);
505 head_next_entry->data.prev = entry_id;
506 head_entry->data.next = entry_id;
507}
508
509static void
510grn_cache_expire_entry_persistent(grn_cache *cache,
511 grn_cache_entry_persistent *entry,
512 grn_id cache_id)
513{
514 grn_hash *keys = cache->impl.persistent.keys;
515 grn_ja *values = cache->impl.persistent.values;
516
517 grn_cache_entry_persistent_delete_link(cache, entry);
518 grn_ja_put(cache->ctx, values, cache_id, NULL, 0, GRN_OBJ_SET, NULL);
519 grn_hash_delete_by_id(cache->ctx, keys, cache_id, NULL);
520}
521
522static void
523grn_cache_expire_memory_without_lock(grn_cache *cache, int32_t size)
524{
525 grn_cache_entry_memory *ce0 =
526 (grn_cache_entry_memory *)(&(cache->impl.memory));
527 while (ce0 != ce0->prev && size--) {
528 grn_cache_expire_entry_memory(cache, ce0->prev);
529 }
530}
531
532static void
533grn_cache_expire_persistent_without_lock(grn_cache *cache, int32_t size)
534{
535 grn_ctx *ctx = cache->ctx;
536 grn_hash *keys = cache->impl.persistent.keys;
537 grn_cache_entry_persistent *head_entry;
538
539 head_entry =
540 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
541 keys,
542 GRN_CACHE_PERSISTENT_ROOT_ID,
543 NULL);
544 while (head_entry->data.prev != GRN_CACHE_PERSISTENT_ROOT_ID &&
545 size > 0) {
546 grn_cache_entry_persistent *tail_entry;
547 tail_entry =
548 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
549 keys,
550 head_entry->data.prev,
551 NULL);
552 grn_cache_expire_entry_persistent(cache, tail_entry, head_entry->data.prev);
553 size--;
554 }
555}
556
557static grn_rc
558grn_cache_set_max_n_entries_memory(grn_ctx *ctx,
559 grn_cache *cache,
560 unsigned int n)
561{
562 uint32_t current_max_n_entries;
563
564 MUTEX_LOCK(cache->impl.memory.mutex);
565 current_max_n_entries = cache->impl.memory.max_nentries;
566 cache->impl.memory.max_nentries = n;
567 if (n < current_max_n_entries) {
568 grn_cache_expire_memory_without_lock(cache, current_max_n_entries - n);
569 }
570 MUTEX_UNLOCK(cache->impl.memory.mutex);
571
572 return GRN_SUCCESS;
573}
574
575static grn_rc
576grn_cache_set_max_n_entries_persistent(grn_ctx *ctx,
577 grn_cache *cache,
578 unsigned int n)
579{
580 grn_rc rc;
581 grn_hash *keys = cache->impl.persistent.keys;
582 grn_cache_entry_persistent *metadata_entry;
583 uint32_t current_max_n_entries;
584
585 rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
586 if (rc != GRN_SUCCESS) {
587 return rc;
588 }
589
590 metadata_entry =
591 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
592 keys,
593 GRN_CACHE_PERSISTENT_METADATA_ID,
594 NULL);
595
596 current_max_n_entries = metadata_entry->metadata.max_nentries;
597 metadata_entry->metadata.max_nentries = n;
598 if (n < current_max_n_entries) {
599 grn_cache_expire_persistent_without_lock(cache, current_max_n_entries - n);
600 }
601 grn_io_unlock(keys->io);
602
603 return GRN_SUCCESS;
604}
605
606grn_rc
607grn_cache_set_max_n_entries(grn_ctx *ctx, grn_cache *cache, unsigned int n)
608{
609 if (!cache) {
610 return GRN_INVALID_ARGUMENT;
611 }
612
613 if (cache->is_memory) {
614 return grn_cache_set_max_n_entries_memory(cache->ctx, cache, n);
615 } else {
616 return grn_cache_set_max_n_entries_persistent(cache->ctx, cache, n);
617 }
618}
619
620static uint32_t
621grn_cache_get_max_n_entries_memory(grn_ctx *ctx, grn_cache *cache)
622{
623 return cache->impl.memory.max_nentries;
624}
625
626static uint32_t
627grn_cache_get_max_n_entries_persistent(grn_ctx *ctx, grn_cache *cache)
628{
629 grn_rc rc;
630 grn_hash *keys = cache->impl.persistent.keys;
631 grn_cache_entry_persistent *metadata_entry;
632 uint32_t current_max_n_entries;
633
634 rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
635 if (rc != GRN_SUCCESS) {
636 return 0;
637 }
638
639 metadata_entry =
640 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
641 keys,
642 GRN_CACHE_PERSISTENT_METADATA_ID,
643 NULL);
644 current_max_n_entries = metadata_entry->metadata.max_nentries;
645 grn_io_unlock(keys->io);
646
647 return current_max_n_entries;
648}
649
650uint32_t
651grn_cache_get_max_n_entries(grn_ctx *ctx, grn_cache *cache)
652{
653 if (!cache) {
654 return 0;
655 }
656
657 if (cache->is_memory) {
658 return grn_cache_get_max_n_entries_memory(cache->ctx, cache);
659 } else {
660 return grn_cache_get_max_n_entries_persistent(cache->ctx, cache);
661 }
662}
663
664static void
665grn_cache_get_statistics_memory(grn_ctx *ctx, grn_cache *cache,
666 grn_cache_statistics *statistics)
667{
668 MUTEX_LOCK(cache->impl.memory.mutex);
669 statistics->nentries = GRN_HASH_SIZE(cache->impl.memory.hash);
670 statistics->max_nentries = cache->impl.memory.max_nentries;
671 statistics->nfetches = cache->impl.memory.nfetches;
672 statistics->nhits = cache->impl.memory.nhits;
673 MUTEX_UNLOCK(cache->impl.memory.mutex);
674}
675
676static void
677grn_cache_get_statistics_persistent(grn_ctx *ctx, grn_cache *cache,
678 grn_cache_statistics *statistics)
679{
680 grn_rc rc = GRN_INVALID_ARGUMENT;
681 grn_hash *keys = cache->impl.persistent.keys;
682 grn_cache_entry_persistent *metadata_entry;
683
684 rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
685 if (rc != GRN_SUCCESS) {
686 return;
687 }
688
689 metadata_entry =
690 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
691 keys,
692 GRN_CACHE_PERSISTENT_METADATA_ID,
693 NULL);
694
695 statistics->nentries = GRN_HASH_SIZE(keys);
696 statistics->max_nentries = metadata_entry->metadata.max_nentries;
697 statistics->nfetches = metadata_entry->metadata.nfetches;
698 statistics->nhits = metadata_entry->metadata.nhits;
699
700 grn_io_unlock(keys->io);
701}
702
703void
704grn_cache_get_statistics(grn_ctx *ctx, grn_cache *cache,
705 grn_cache_statistics *statistics)
706{
707 if (cache->is_memory) {
708 return grn_cache_get_statistics_memory(ctx, cache, statistics);
709 } else {
710 return grn_cache_get_statistics_persistent(ctx, cache, statistics);
711 }
712}
713
714static grn_rc
715grn_cache_fetch_memory(grn_ctx *ctx, grn_cache *cache,
716 const char *key, uint32_t key_len,
717 grn_obj *output)
718{
719 /* TODO: How about GRN_NOT_FOUND? */
720 grn_rc rc = GRN_INVALID_ARGUMENT;
721 grn_cache_entry_memory *ce;
722
723 MUTEX_LOCK(cache->impl.memory.mutex);
724 cache->impl.memory.nfetches++;
725 if (grn_hash_get(cache->ctx, cache->impl.memory.hash, key, key_len,
726 (void **)&ce)) {
727 if (ce->tv.tv_sec <= grn_db_get_last_modified(ctx, ctx->impl->db)) {
728 grn_cache_expire_entry_memory(cache, ce);
729 goto exit;
730 }
731 rc = GRN_SUCCESS;
732 GRN_TEXT_PUT(ctx,
733 output,
734 GRN_TEXT_VALUE(ce->value),
735 GRN_TEXT_LEN(ce->value));
736 ce->prev->next = ce->next;
737 ce->next->prev = ce->prev;
738 {
739 grn_cache_entry_memory *ce0 =
740 (grn_cache_entry_memory *)(&(cache->impl.memory));
741 ce->next = ce0->next;
742 ce->prev = ce0;
743 ce0->next->prev = ce;
744 ce0->next = ce;
745 }
746 cache->impl.memory.nhits++;
747 }
748exit :
749 MUTEX_UNLOCK(cache->impl.memory.mutex);
750 return rc;
751}
752
753static grn_rc
754grn_cache_fetch_persistent(grn_ctx *ctx, grn_cache *cache,
755 const char *key, uint32_t key_len,
756 grn_obj *output)
757{
758 /* TODO: How about GRN_NOT_FOUND? */
759 grn_rc rc = GRN_INVALID_ARGUMENT;
760 grn_hash *keys = cache->impl.persistent.keys;
761 grn_ja *values = cache->impl.persistent.values;
762 grn_id cache_id;
763 grn_cache_entry_persistent *entry;
764 grn_cache_entry_persistent *metadata_entry;
765
766 if (key_len == GRN_CACHE_PERSISTENT_ROOT_KEY_LEN &&
767 memcmp(key,
768 GRN_CACHE_PERSISTENT_ROOT_KEY,
769 GRN_CACHE_PERSISTENT_ROOT_KEY_LEN) == 0) {
770 return rc;
771 }
772
773 rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
774 if (rc != GRN_SUCCESS) {
775 return rc;
776 }
777
778 /* TODO: How about GRN_NOT_FOUND? */
779 rc = GRN_INVALID_ARGUMENT;
780
781 metadata_entry =
782 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
783 keys,
784 GRN_CACHE_PERSISTENT_METADATA_ID,
785 NULL);
786 metadata_entry->metadata.nfetches++;
787
788 cache_id = grn_hash_get(cache->ctx, keys, key, key_len, (void **)&entry);
789 if (cache_id == GRN_ID_NIL) {
790 goto exit;
791 }
792
793 if (cache_id != GRN_ID_NIL) {
794 if (entry->data.modified_time.tv_sec <=
795 grn_db_get_last_modified(ctx, ctx->impl->db)) {
796 grn_cache_expire_entry_persistent(cache, entry, cache_id);
797 goto exit;
798 }
799
800 rc = GRN_SUCCESS;
801 grn_ja_get_value(ctx, values, cache_id, output);
802 grn_cache_entry_persistent_delete_link(cache, entry);
803 {
804 grn_cache_entry_persistent *head_entry;
805 head_entry =
806 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
807 keys,
808 GRN_CACHE_PERSISTENT_ROOT_ID,
809 NULL);
810 grn_cache_entry_persistent_prepend_link(cache,
811 entry,
812 cache_id,
813 head_entry,
814 GRN_CACHE_PERSISTENT_ROOT_ID);
815 }
816 metadata_entry->metadata.nhits++;
817 }
818
819exit :
820 grn_io_unlock(keys->io);
821
822 return rc;
823}
824
825grn_rc
826grn_cache_fetch(grn_ctx *ctx, grn_cache *cache,
827 const char *key, uint32_t key_len,
828 grn_obj *output)
829{
830 if (!ctx->impl || !ctx->impl->db) { return GRN_INVALID_ARGUMENT; }
831
832 if (cache->is_memory) {
833 return grn_cache_fetch_memory(ctx, cache, key, key_len, output);
834 } else {
835 return grn_cache_fetch_persistent(ctx, cache, key, key_len, output);
836 }
837}
838
839static void
840grn_cache_update_memory(grn_ctx *ctx, grn_cache *cache,
841 const char *key, uint32_t key_len,
842 grn_obj *value)
843{
844 grn_id id;
845 int added = 0;
846 grn_cache_entry_memory *ce;
847 grn_rc rc = GRN_SUCCESS;
848 grn_obj *old = NULL;
849 grn_obj *obj = NULL;
850
851 if (cache->impl.memory.max_nentries == 0) {
852 return;
853 }
854
855 MUTEX_LOCK(cache->impl.memory.mutex);
856 obj = grn_obj_open(cache->ctx, GRN_BULK, 0, GRN_DB_TEXT);
857 if (!obj) {
858 goto exit;
859 }
860 GRN_TEXT_PUT(cache->ctx, obj, GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));
861 id = grn_hash_add(cache->ctx, cache->impl.memory.hash, key, key_len,
862 (void **)&ce, &added);
863 if (id) {
864 if (!added) {
865 old = ce->value;
866 ce->prev->next = ce->next;
867 ce->next->prev = ce->prev;
868 }
869 ce->id = id;
870 ce->value = obj;
871 ce->tv = ctx->impl->tv;
872 {
873 grn_cache_entry_memory *ce0 =
874 (grn_cache_entry_memory *)(&(cache->impl.memory));
875 ce->next = ce0->next;
876 ce->prev = ce0;
877 ce0->next->prev = ce;
878 ce0->next = ce;
879 }
880 if (GRN_HASH_SIZE(cache->impl.memory.hash) >
881 cache->impl.memory.max_nentries) {
882 grn_cache_expire_entry_memory(cache, cache->impl.memory.prev);
883 }
884 } else {
885 rc = GRN_NO_MEMORY_AVAILABLE;
886 }
887exit :
888 if (rc) { grn_obj_close(cache->ctx, obj); }
889 if (old) { grn_obj_close(cache->ctx, old); }
890 MUTEX_UNLOCK(cache->impl.memory.mutex);
891}
892
893static void
894grn_cache_update_persistent(grn_ctx *ctx, grn_cache *cache,
895 const char *key, uint32_t key_len,
896 grn_obj *value)
897{
898 grn_rc rc;
899 grn_hash *keys = cache->impl.persistent.keys;
900 grn_ja *values = cache->impl.persistent.values;
901 grn_cache_entry_persistent *metadata_entry;
902 grn_id cache_id;
903 grn_cache_entry_persistent *entry;
904 int added;
905
906 if (key_len == GRN_CACHE_PERSISTENT_ROOT_KEY_LEN &&
907 memcmp(key,
908 GRN_CACHE_PERSISTENT_ROOT_KEY,
909 GRN_CACHE_PERSISTENT_ROOT_KEY_LEN) == 0) {
910 return;
911 }
912
913 if (key_len == GRN_CACHE_PERSISTENT_METADATA_KEY_LEN &&
914 memcmp(key,
915 GRN_CACHE_PERSISTENT_METADATA_KEY,
916 GRN_CACHE_PERSISTENT_METADATA_KEY_LEN) == 0) {
917 return;
918 }
919
920 rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
921 if (rc != GRN_SUCCESS) {
922 return;
923 }
924
925 metadata_entry =
926 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
927 keys,
928 GRN_CACHE_PERSISTENT_METADATA_ID,
929 NULL);
930 if (metadata_entry->metadata.max_nentries == 0) {
931 goto exit;
932 }
933
934 cache_id = grn_hash_add(cache->ctx, keys, key, key_len, (void **)&entry,
935 &added);
936 if (cache_id) {
937 grn_cache_entry_persistent *head_entry;
938
939 if (!added) {
940 grn_cache_entry_persistent_delete_link(cache, entry);
941 }
942 entry->data.modified_time = ctx->impl->tv;
943
944 grn_ja_put(cache->ctx, values, cache_id,
945 GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value),
946 GRN_OBJ_SET, NULL);
947
948 head_entry =
949 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
950 keys,
951 GRN_CACHE_PERSISTENT_ROOT_ID,
952 NULL);
953 grn_cache_entry_persistent_prepend_link(cache,
954 entry,
955 cache_id,
956 head_entry,
957 GRN_CACHE_PERSISTENT_ROOT_ID);
958 if (GRN_HASH_SIZE(keys) > metadata_entry->metadata.max_nentries) {
959 grn_cache_entry_persistent *tail_entry;
960 tail_entry =
961 (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
962 keys,
963 head_entry->data.prev,
964 NULL);
965 grn_cache_expire_entry_persistent(cache,
966 tail_entry,
967 head_entry->data.prev);
968 }
969 }
970
971exit :
972 grn_io_unlock(keys->io);
973}
974
975void
976grn_cache_update(grn_ctx *ctx, grn_cache *cache,
977 const char *key, uint32_t key_len, grn_obj *value)
978{
979 if (!ctx->impl) { return; }
980
981 if (cache->is_memory) {
982 grn_cache_update_memory(ctx, cache, key, key_len, value);
983 } else {
984 grn_cache_update_persistent(ctx, cache, key, key_len, value);
985 }
986}
987
988static void
989grn_cache_expire_memory(grn_cache *cache, int32_t size)
990{
991 MUTEX_LOCK(cache->impl.memory.mutex);
992 grn_cache_expire_memory_without_lock(cache, size);
993 MUTEX_UNLOCK(cache->impl.memory.mutex);
994}
995
996static void
997grn_cache_expire_persistent(grn_cache *cache, int32_t size)
998{
999 grn_rc rc;
1000 grn_ctx *ctx = cache->ctx;
1001 grn_hash *keys = cache->impl.persistent.keys;
1002
1003 rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
1004 if (rc != GRN_SUCCESS) {
1005 return;
1006 }
1007
1008 grn_cache_expire_persistent_without_lock(cache, size);
1009
1010 grn_io_unlock(keys->io);
1011}
1012
1013void
1014grn_cache_expire(grn_cache *cache, int32_t size)
1015{
1016 if (cache->is_memory) {
1017 grn_cache_expire_memory(cache, size);
1018 } else {
1019 grn_cache_expire_persistent(cache, size);
1020 }
1021}
1022
1023void
1024grn_cache_fin(void)
1025{
1026 grn_ctx *ctx = &grn_cache_ctx;
1027
1028 grn_cache_current_set(ctx, NULL);
1029
1030 if (grn_cache_default) {
1031 grn_cache_close(ctx, grn_cache_default);
1032 grn_cache_default = NULL;
1033 }
1034
1035 grn_ctx_fin(ctx);
1036}
1037