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 | |
30 | typedef struct _grn_cache_entry_memory grn_cache_entry_memory; |
31 | |
32 | struct _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 | |
40 | typedef 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 | */ |
50 | typedef 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 | |
56 | typedef 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 | |
61 | struct _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 | |
91 | static grn_ctx grn_cache_ctx; |
92 | static grn_cache *grn_cache_current = NULL; |
93 | static grn_cache *grn_cache_default = NULL; |
94 | static char grn_cache_default_base_path[PATH_MAX]; |
95 | |
96 | void |
97 | grn_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 | |
108 | const char * |
109 | grn_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 | |
118 | static void |
119 | grn_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 | |
139 | static void |
140 | grn_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 | |
299 | exit : |
300 | grn_file_lock_release(ctx, &file_lock); |
301 | grn_file_lock_fin(ctx, &file_lock); |
302 | } |
303 | |
304 | static grn_cache * |
305 | grn_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 | |
331 | exit : |
332 | GRN_API_RETURN(cache); |
333 | } |
334 | |
335 | grn_cache * |
336 | grn_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 | |
360 | grn_cache * |
361 | grn_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 | |
368 | static void |
369 | grn_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 | |
380 | static void |
381 | grn_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 | |
387 | grn_rc |
388 | grn_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 | |
404 | grn_rc |
405 | grn_cache_current_set(grn_ctx *ctx, grn_cache *cache) |
406 | { |
407 | grn_cache_current = cache; |
408 | return GRN_SUCCESS; |
409 | } |
410 | |
411 | grn_cache * |
412 | grn_cache_current_get(grn_ctx *ctx) |
413 | { |
414 | return grn_cache_current; |
415 | } |
416 | |
417 | void |
418 | grn_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 | |
428 | grn_rc |
429 | grn_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 | |
455 | static void |
456 | grn_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 | |
464 | static void |
465 | grn_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 | |
487 | static void |
488 | grn_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 | |
509 | static void |
510 | grn_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 | |
522 | static void |
523 | grn_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 | |
532 | static void |
533 | grn_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 | |
557 | static grn_rc |
558 | grn_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 | |
575 | static grn_rc |
576 | grn_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 | |
606 | grn_rc |
607 | grn_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 | |
620 | static uint32_t |
621 | grn_cache_get_max_n_entries_memory(grn_ctx *ctx, grn_cache *cache) |
622 | { |
623 | return cache->impl.memory.max_nentries; |
624 | } |
625 | |
626 | static uint32_t |
627 | grn_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 | |
650 | uint32_t |
651 | grn_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 | |
664 | static void |
665 | grn_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 | |
676 | static void |
677 | grn_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 | |
703 | void |
704 | grn_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 | |
714 | static grn_rc |
715 | grn_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 | } |
748 | exit : |
749 | MUTEX_UNLOCK(cache->impl.memory.mutex); |
750 | return rc; |
751 | } |
752 | |
753 | static grn_rc |
754 | grn_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 | |
819 | exit : |
820 | grn_io_unlock(keys->io); |
821 | |
822 | return rc; |
823 | } |
824 | |
825 | grn_rc |
826 | grn_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 | |
839 | static void |
840 | grn_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 | } |
887 | exit : |
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 | |
893 | static void |
894 | grn_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 | |
971 | exit : |
972 | grn_io_unlock(keys->io); |
973 | } |
974 | |
975 | void |
976 | grn_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 | |
988 | static void |
989 | grn_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 | |
996 | static void |
997 | grn_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 | |
1013 | void |
1014 | grn_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 | |
1023 | void |
1024 | grn_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 | |