9 | 9 |
#include <ngx_md5.h>
|
10 | 10 |
|
11 | 11 |
|
12 | |
static ngx_int_t ngx_http_file_cache_exists(ngx_http_request_t *r,
|
13 | |
ngx_http_file_cache_t *cache);
|
|
12 |
static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
|
|
13 |
ngx_http_cache_t *c);
|
14 | 14 |
static ngx_http_file_cache_node_t *
|
15 | 15 |
ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
|
16 | 16 |
static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
|
17 | 17 |
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
|
18 | 18 |
static void ngx_http_file_cache_cleanup(void *data);
|
19 | |
static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache,
|
20 | |
ngx_uint_t force);
|
21 | |
static ngx_int_t ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx,
|
|
19 |
static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
|
|
20 |
static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
|
|
21 |
static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
|
|
22 |
ngx_queue_t *q, u_char *name);
|
|
23 |
static ngx_int_t
|
|
24 |
ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache);
|
|
25 |
static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
|
22 | 26 |
ngx_str_t *path);
|
23 | |
static ngx_int_t ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx,
|
|
27 |
static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
|
|
28 |
ngx_str_t *path);
|
|
29 |
static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
|
|
30 |
ngx_str_t *path);
|
|
31 |
static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
|
|
32 |
ngx_http_cache_t *c);
|
|
33 |
static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
|
24 | 34 |
ngx_str_t *path);
|
25 | 35 |
|
26 | 36 |
|
|
52 | 62 |
cache->rbtree = ocache->rbtree;
|
53 | 63 |
cache->queue = ocache->queue;
|
54 | 64 |
cache->shpool = ocache->shpool;
|
55 | |
cache->created = ocache->created;
|
|
65 |
cache->cold = ocache->cold;
|
|
66 |
cache->size = ocache->size;
|
|
67 |
cache->bsize = ocache->bsize;
|
|
68 |
|
|
69 |
cache->max_size /= cache->bsize;
|
56 | 70 |
|
57 | 71 |
return NGX_OK;
|
58 | 72 |
}
|
|
79 | 93 |
|
80 | 94 |
ngx_queue_init(cache->queue);
|
81 | 95 |
|
|
96 |
cache->cold = ngx_slab_alloc(cache->shpool, sizeof(ngx_atomic_t));
|
|
97 |
if (cache->cold == NULL) {
|
|
98 |
return NGX_ERROR;
|
|
99 |
}
|
|
100 |
|
|
101 |
*cache->cold = 1;
|
|
102 |
|
|
103 |
cache->size = ngx_slab_alloc(cache->shpool, sizeof(off_t));
|
|
104 |
if (cache->size == NULL) {
|
|
105 |
return NGX_ERROR;
|
|
106 |
}
|
|
107 |
|
|
108 |
*cache->size = 0;
|
|
109 |
|
|
110 |
cache->bsize = ngx_fs_bsize(cache->path->name.data);
|
|
111 |
|
|
112 |
cache->max_size /= cache->bsize;
|
|
113 |
|
82 | 114 |
len = sizeof(" in cache keys zone \"\"") + shm_zone->name.len;
|
83 | 115 |
|
84 | 116 |
cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
|
|
88 | 120 |
|
89 | 121 |
ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
|
90 | 122 |
&shm_zone->name);
|
91 | |
|
92 | |
cache->created = ngx_time();
|
93 | 123 |
|
94 | 124 |
return NGX_OK;
|
95 | 125 |
}
|
|
154 | 184 |
return NGX_ERROR;
|
155 | 185 |
}
|
156 | 186 |
|
157 | |
rc = ngx_http_file_cache_exists(r, cache);
|
|
187 |
rc = ngx_http_file_cache_exists(cache, c);
|
158 | 188 |
|
159 | 189 |
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
160 | 190 |
"http file cache exists: %i u:%ui e:%d",
|
|
171 | 201 |
return rc;
|
172 | 202 |
}
|
173 | 203 |
|
174 | |
now = ngx_time();
|
175 | |
|
176 | |
cold = (now - cache->created < cache->inactive) ? 1 : 0;
|
|
204 |
cold = *cache->cold;
|
177 | 205 |
|
178 | 206 |
if (rc == NGX_OK) {
|
179 | 207 |
|
|
303 | 331 |
|
304 | 332 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
305 | 333 |
|
306 | |
c->node->uses = c->min_uses;
|
307 | |
c->node->body_start = c->body_start;
|
308 | |
c->node->exists = 1;
|
|
334 |
if (!c->node->exists) {
|
|
335 |
c->node->uses = 1;
|
|
336 |
c->node->body_start = c->body_start;
|
|
337 |
c->node->exists = 1;
|
|
338 |
|
|
339 |
*cache->size += (c->length + cache->bsize - 1) / cache->bsize;
|
|
340 |
}
|
309 | 341 |
|
310 | 342 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
311 | 343 |
}
|
|
344 |
|
|
345 |
now = ngx_time();
|
312 | 346 |
|
313 | 347 |
if (c->valid_sec < now) {
|
314 | 348 |
|
|
327 | 361 |
|
328 | 362 |
|
329 | 363 |
static ngx_int_t
|
330 | |
ngx_http_file_cache_exists(ngx_http_request_t *r, ngx_http_file_cache_t *cache)
|
|
364 |
ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
|
331 | 365 |
{
|
332 | 366 |
ngx_int_t rc;
|
333 | 367 |
ngx_http_file_cache_node_t *fcn;
|
334 | 368 |
|
335 | 369 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
336 | 370 |
|
337 | |
fcn = ngx_http_file_cache_lookup(cache, r->cache->key);
|
|
371 |
fcn = ngx_http_file_cache_lookup(cache, c->key);
|
338 | 372 |
|
339 | 373 |
if (fcn) {
|
340 | 374 |
ngx_queue_remove(&fcn->queue);
|
|
355 | 389 |
|
356 | 390 |
if (fcn->exists) {
|
357 | 391 |
|
358 | |
r->cache->exists = fcn->exists;
|
359 | |
r->cache->body_start = fcn->body_start;
|
|
392 |
c->exists = fcn->exists;
|
|
393 |
c->body_start = fcn->body_start;
|
360 | 394 |
|
361 | 395 |
rc = NGX_OK;
|
362 | 396 |
|
363 | 397 |
goto done;
|
364 | 398 |
}
|
365 | 399 |
|
366 | |
if (fcn->uses >= r->cache->min_uses) {
|
367 | |
|
368 | |
r->cache->exists = fcn->exists;
|
369 | |
r->cache->body_start = fcn->body_start;
|
|
400 |
if (fcn->uses >= c->min_uses) {
|
|
401 |
|
|
402 |
c->exists = fcn->exists;
|
|
403 |
c->body_start = fcn->body_start;
|
370 | 404 |
|
371 | 405 |
rc = NGX_OK;
|
372 | 406 |
|
|
382 | 416 |
if (fcn == NULL) {
|
383 | 417 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
384 | 418 |
|
385 | |
(void) ngx_http_file_cache_expire(cache, 1);
|
|
419 |
ngx_http_file_cache_forced_expire(cache);
|
386 | 420 |
|
387 | 421 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
388 | 422 |
|
|
394 | 428 |
}
|
395 | 429 |
}
|
396 | 430 |
|
397 | |
ngx_memcpy((u_char *) &fcn->node.key, r->cache->key,
|
398 | |
sizeof(ngx_rbtree_key_t));
|
399 | |
|
400 | |
ngx_memcpy(fcn->key, &r->cache->key[sizeof(ngx_rbtree_key_t)],
|
|
431 |
ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
|
|
432 |
|
|
433 |
ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
|
401 | 434 |
NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
|
402 | 435 |
|
403 | 436 |
ngx_rbtree_insert(cache->rbtree, &fcn->node);
|
|
421 | 454 |
|
422 | 455 |
ngx_queue_insert_head(cache->queue, &fcn->queue);
|
423 | 456 |
|
424 | |
r->cache->uniq = fcn->uniq;
|
425 | |
r->cache->uses = fcn->uses;
|
426 | |
r->cache->error = fcn->error;
|
427 | |
r->cache->node = fcn;
|
|
457 |
c->uniq = fcn->uniq;
|
|
458 |
c->uses = fcn->uses;
|
|
459 |
c->error = fcn->error;
|
|
460 |
c->node = fcn;
|
428 | 461 |
|
429 | 462 |
failed:
|
430 | 463 |
|
|
566 | 599 |
void
|
567 | 600 |
ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
|
568 | 601 |
{
|
|
602 |
off_t size;
|
569 | 603 |
ngx_int_t rc;
|
570 | 604 |
ngx_file_uniq_t uniq;
|
571 | 605 |
ngx_file_info_t fi;
|
|
615 | 649 |
}
|
616 | 650 |
}
|
617 | 651 |
|
|
652 |
size = (c->length + cache->bsize - 1) / cache->bsize;
|
|
653 |
|
618 | 654 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
619 | 655 |
|
620 | 656 |
c->node->count--;
|
621 | 657 |
c->node->uniq = uniq;
|
622 | 658 |
c->node->body_start = c->body_start;
|
|
659 |
|
|
660 |
size = size - (c->node->length + cache->bsize - 1) / cache->bsize;
|
|
661 |
|
|
662 |
c->node->length = c->length;
|
|
663 |
|
|
664 |
*cache->size += size;
|
623 | 665 |
|
624 | 666 |
if (rc == NGX_OK) {
|
625 | 667 |
c->node->exists = 1;
|
|
756 | 798 |
|
757 | 799 |
|
758 | 800 |
static time_t
|
759 | |
ngx_http_file_cache_expire(ngx_http_file_cache_t *cache, ngx_uint_t forced)
|
760 | |
{
|
761 | |
u_char *name, *p;
|
|
801 |
ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
|
|
802 |
{
|
|
803 |
u_char *name;
|
762 | 804 |
size_t len;
|
763 | |
time_t now, wait;
|
|
805 |
time_t wait;
|
764 | 806 |
ngx_uint_t tries;
|
765 | 807 |
ngx_path_t *path;
|
766 | 808 |
ngx_queue_t *q;
|
767 | 809 |
ngx_http_file_cache_node_t *fcn;
|
|
810 |
|
|
811 |
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
812 |
"http file cache forced expire");
|
|
813 |
|
|
814 |
path = cache->path;
|
|
815 |
len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
|
|
816 |
|
|
817 |
name = ngx_alloc(len + 1, ngx_cycle->log);
|
|
818 |
if (name == NULL) {
|
|
819 |
return 60;
|
|
820 |
}
|
|
821 |
|
|
822 |
ngx_memcpy(name, path->name.data, path->name.len);
|
|
823 |
|
|
824 |
wait = 60;
|
|
825 |
tries = 0;
|
|
826 |
|
|
827 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
|
828 |
|
|
829 |
for (q = ngx_queue_last(cache->queue);
|
|
830 |
q != ngx_queue_sentinel(cache->queue);
|
|
831 |
q = ngx_queue_prev(q))
|
|
832 |
{
|
|
833 |
fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
|
|
834 |
|
|
835 |
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
836 |
"http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
|
|
837 |
fcn->count, fcn->exists,
|
|
838 |
fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
|
|
839 |
|
|
840 |
if (fcn->count) {
|
|
841 |
|
|
842 |
if (tries++ < 20) {
|
|
843 |
continue;
|
|
844 |
}
|
|
845 |
|
|
846 |
wait = 1;
|
|
847 |
|
|
848 |
break;
|
|
849 |
}
|
|
850 |
|
|
851 |
if (!fcn->exists) {
|
|
852 |
|
|
853 |
ngx_queue_remove(q);
|
|
854 |
ngx_rbtree_delete(cache->rbtree, &fcn->node);
|
|
855 |
ngx_slab_free_locked(cache->shpool, fcn);
|
|
856 |
|
|
857 |
break;
|
|
858 |
}
|
|
859 |
|
|
860 |
ngx_http_file_cache_delete(cache, q, name);
|
|
861 |
|
|
862 |
break;
|
|
863 |
}
|
|
864 |
|
|
865 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
|
866 |
|
|
867 |
ngx_free(name);
|
|
868 |
|
|
869 |
return wait;
|
|
870 |
}
|
|
871 |
|
|
872 |
|
|
873 |
static time_t
|
|
874 |
ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
|
|
875 |
{
|
|
876 |
u_char *name, *p;
|
|
877 |
size_t len;
|
|
878 |
time_t now, wait;
|
|
879 |
ngx_path_t *path;
|
|
880 |
ngx_queue_t *q;
|
|
881 |
ngx_http_file_cache_node_t *fcn;
|
768 | 882 |
u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
|
769 | 883 |
|
770 | 884 |
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
773 | 887 |
path = cache->path;
|
774 | 888 |
len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
|
775 | 889 |
|
|
890 |
name = ngx_alloc(len + 1, ngx_cycle->log);
|
|
891 |
if (name == NULL) {
|
|
892 |
return 60;
|
|
893 |
}
|
|
894 |
|
|
895 |
ngx_memcpy(name, path->name.data, path->name.len);
|
|
896 |
|
776 | 897 |
now = ngx_time();
|
777 | 898 |
|
778 | 899 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
779 | 900 |
|
780 | |
tries = 0;
|
781 | |
|
782 | 901 |
for ( ;; ) {
|
783 | 902 |
|
784 | 903 |
if (ngx_queue_empty(cache->queue)) {
|
785 | |
wait = cache->inactive;
|
|
904 |
wait = 60;
|
786 | 905 |
break;
|
787 | 906 |
}
|
788 | 907 |
|
|
790 | 909 |
|
791 | 910 |
fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
|
792 | 911 |
|
793 | |
if (!forced) {
|
794 | |
wait = fcn->expire - now;
|
795 | |
|
796 | |
if (wait > 0) {
|
797 | |
break;
|
798 | |
}
|
|
912 |
wait = fcn->expire - now;
|
|
913 |
|
|
914 |
if (wait > 0) {
|
|
915 |
wait = wait > 60 ? 60 : wait;
|
|
916 |
break;
|
799 | 917 |
}
|
800 | 918 |
|
801 | 919 |
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
805 | 923 |
|
806 | 924 |
if (fcn->count) {
|
807 | 925 |
|
808 | |
if (!forced) {
|
809 | |
|
810 | |
p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
|
811 | |
sizeof(ngx_rbtree_key_t));
|
812 | |
(void) ngx_hex_dump(p, fcn->key,
|
813 | |
NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
|
814 | |
|
815 | |
/*
|
816 | |
* abnormally exited workers may leave locked cache entries,
|
817 | |
* and although it may be safe to remove them completely,
|
818 | |
* we prefer to remove them from inactive queue and rbtree
|
819 | |
* only, and to allow other leaks
|
820 | |
*/
|
821 | |
|
822 | |
ngx_queue_remove(q);
|
823 | |
|
824 | |
ngx_rbtree_delete(cache->rbtree, &fcn->node);
|
825 | |
|
826 | |
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
|
926 |
p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
|
|
927 |
sizeof(ngx_rbtree_key_t));
|
|
928 |
|
|
929 |
len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
|
|
930 |
(void) ngx_hex_dump(p, fcn->key, len);
|
|
931 |
|
|
932 |
/*
|
|
933 |
* abnormally exited workers may leave locked cache entries,
|
|
934 |
* and although it may be safe to remove them completely,
|
|
935 |
* we prefer to remove them from inactive queue and rbtree
|
|
936 |
* only, and to allow other leaks
|
|
937 |
*/
|
|
938 |
|
|
939 |
ngx_queue_remove(q);
|
|
940 |
ngx_rbtree_delete(cache->rbtree, &fcn->node);
|
|
941 |
|
|
942 |
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
827 | 943 |
"ignore long locked inactive cache entry %*s, count:%d",
|
828 | 944 |
2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
|
829 | |
}
|
830 | |
|
831 | |
if (tries++ < 10) {
|
832 | |
continue;
|
833 | |
}
|
834 | |
|
835 | |
wait = 1;
|
836 | |
break;
|
837 | |
}
|
838 | |
|
839 | |
forced = 0;
|
|
945 |
|
|
946 |
continue;
|
|
947 |
}
|
840 | 948 |
|
841 | 949 |
if (!fcn->exists) {
|
842 | 950 |
|
843 | 951 |
ngx_queue_remove(q);
|
844 | |
|
845 | 952 |
ngx_rbtree_delete(cache->rbtree, &fcn->node);
|
846 | |
|
847 | 953 |
ngx_slab_free_locked(cache->shpool, fcn);
|
848 | 954 |
|
849 | 955 |
continue;
|
850 | 956 |
}
|
851 | 957 |
|
852 | |
name = ngx_alloc(len + 1, ngx_cycle->log);
|
853 | |
if (name == NULL) {
|
854 | |
wait = 60;
|
855 | |
break;
|
856 | |
}
|
857 | |
|
858 | |
ngx_memcpy(name, path->name.data, path->name.len);
|
859 | |
|
860 | |
p = name + path->name.len + 1 + path->len;
|
861 | |
p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
|
862 | |
sizeof(ngx_rbtree_key_t));
|
863 | |
p = ngx_hex_dump(p, fcn->key,
|
864 | |
NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
|
865 | |
*p = '\0';
|
866 | |
|
867 | |
ngx_queue_remove(q);
|
868 | |
|
869 | |
ngx_rbtree_delete(cache->rbtree, &fcn->node);
|
870 | |
|
871 | |
ngx_slab_free_locked(cache->shpool, fcn);
|
872 | |
|
873 | |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
874 | |
|
875 | |
ngx_create_hashed_filename(path, name, len);
|
876 | |
|
877 | |
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
878 | |
"http file cache expire: \"%s\"", name);
|
879 | |
|
880 | |
if (ngx_delete_file(name) == NGX_FILE_ERROR) {
|
881 | |
ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
|
882 | |
ngx_delete_file_n " \"%s\" failed", name);
|
883 | |
}
|
884 | |
|
885 | |
ngx_free(name);
|
886 | |
|
887 | |
ngx_shmtx_lock(&cache->shpool->mutex);
|
|
958 |
ngx_http_file_cache_delete(cache, q, name);
|
888 | 959 |
}
|
889 | 960 |
|
890 | 961 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
891 | 962 |
|
|
963 |
ngx_free(name);
|
|
964 |
|
892 | 965 |
return wait;
|
893 | 966 |
}
|
894 | 967 |
|
895 | 968 |
|
896 | |
time_t
|
897 | |
ngx_http_file_cache_cleaner(void *data)
|
|
969 |
static void
|
|
970 |
ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
|
|
971 |
u_char *name)
|
|
972 |
{
|
|
973 |
u_char *p;
|
|
974 |
size_t len;
|
|
975 |
ngx_path_t *path;
|
|
976 |
ngx_http_file_cache_node_t *fcn;
|
|
977 |
|
|
978 |
fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
|
|
979 |
|
|
980 |
*cache->size -= (fcn->length + cache->bsize - 1) / cache->bsize;
|
|
981 |
|
|
982 |
path = cache->path;
|
|
983 |
|
|
984 |
p = name + path->name.len + 1 + path->len;
|
|
985 |
|
|
986 |
p = ngx_hex_dump(p, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t));
|
|
987 |
|
|
988 |
len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
|
|
989 |
p = ngx_hex_dump(p, fcn->key, len);
|
|
990 |
*p = '\0';
|
|
991 |
|
|
992 |
ngx_queue_remove(q);
|
|
993 |
|
|
994 |
ngx_rbtree_delete(cache->rbtree, &fcn->node);
|
|
995 |
|
|
996 |
ngx_slab_free_locked(cache->shpool, fcn);
|
|
997 |
|
|
998 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
|
999 |
|
|
1000 |
len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
|
|
1001 |
|
|
1002 |
ngx_create_hashed_filename(path, name, len);
|
|
1003 |
|
|
1004 |
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
1005 |
"http file cache expire: \"%s\"", name);
|
|
1006 |
|
|
1007 |
if (ngx_delete_file(name) == NGX_FILE_ERROR) {
|
|
1008 |
ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
|
|
1009 |
ngx_delete_file_n " \"%s\" failed", name);
|
|
1010 |
}
|
|
1011 |
|
|
1012 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
|
1013 |
}
|
|
1014 |
|
|
1015 |
|
|
1016 |
static time_t
|
|
1017 |
ngx_http_file_cache_manager(void *data)
|
898 | 1018 |
{
|
899 | 1019 |
ngx_http_file_cache_t *cache = data;
|
900 | 1020 |
|
901 | |
time_t now, next;
|
|
1021 |
off_t size;
|
|
1022 |
time_t next;
|
902 | 1023 |
ngx_tree_ctx_t tree;
|
903 | 1024 |
|
904 | |
now = ngx_time();
|
905 | |
|
906 | |
if (now >= cache->next_clean_time
|
907 | |
&& now >= cache->created + cache->inactive)
|
908 | |
{
|
909 | |
ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
|
910 | |
"clean unused cache files");
|
|
1025 |
if (*cache->cold) {
|
|
1026 |
|
|
1027 |
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
1028 |
"http file cache manager update");
|
911 | 1029 |
|
912 | 1030 |
tree.init_handler = NULL;
|
913 | |
tree.file_handler = ngx_http_file_cache_clean_file;
|
914 | |
tree.pre_tree_handler = ngx_http_file_cache_clean_noop;
|
915 | |
tree.post_tree_handler = ngx_http_file_cache_clean_noop;
|
916 | |
tree.spec_handler = ngx_http_file_cache_clean_file;
|
|
1031 |
tree.file_handler = ngx_http_file_cache_manage_file;
|
|
1032 |
tree.pre_tree_handler = ngx_http_file_cache_noop;
|
|
1033 |
tree.post_tree_handler = ngx_http_file_cache_noop;
|
|
1034 |
tree.spec_handler = ngx_http_file_cache_delete_file;
|
917 | 1035 |
tree.data = cache;
|
918 | 1036 |
tree.alloc = 0;
|
919 | 1037 |
tree.log = ngx_cycle->log;
|
920 | 1038 |
|
921 | |
(void) ngx_walk_tree(&tree, &cache->path->name);
|
|
1039 |
cache->last = ngx_current_msec;
|
|
1040 |
cache->files = 0;
|
|
1041 |
|
|
1042 |
if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
|
|
1043 |
return 60;
|
|
1044 |
}
|
|
1045 |
|
|
1046 |
*cache->cold = 0;
|
|
1047 |
|
|
1048 |
ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
|
|
1049 |
"http file cache: %V %.3fM, bsize: %uz",
|
|
1050 |
&cache->path->name,
|
|
1051 |
((double) *cache->size * cache->bsize) / (1024 * 1024),
|
|
1052 |
cache->bsize);
|
|
1053 |
}
|
|
1054 |
|
|
1055 |
next = ngx_http_file_cache_expire(cache);
|
|
1056 |
|
|
1057 |
cache->last = ngx_current_msec;
|
|
1058 |
cache->files = 0;
|
|
1059 |
|
|
1060 |
for ( ;; ) {
|
|
1061 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
|
1062 |
|
|
1063 |
size = *cache->size;
|
|
1064 |
|
|
1065 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
|
1066 |
|
|
1067 |
if (size < cache->max_size) {
|
|
1068 |
return next;
|
|
1069 |
}
|
|
1070 |
|
|
1071 |
next = ngx_http_file_cache_forced_expire(cache);
|
|
1072 |
|
|
1073 |
if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) {
|
|
1074 |
return next;
|
|
1075 |
}
|
|
1076 |
}
|
|
1077 |
}
|
|
1078 |
|
|
1079 |
|
|
1080 |
static ngx_int_t
|
|
1081 |
ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
|
|
1082 |
{
|
|
1083 |
ngx_msec_t elapsed;
|
|
1084 |
|
|
1085 |
if (cache->files++ > 100) {
|
922 | 1086 |
|
923 | 1087 |
ngx_time_update(0, 0);
|
924 | 1088 |
|
925 | |
next = ngx_next_time(cache->clean_time);
|
926 | |
|
927 | |
if (next == -1) {
|
928 | |
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
|
929 | |
ngx_next_time_n " failed");
|
930 | |
return 60;
|
931 | |
}
|
932 | |
|
933 | |
cache->next_clean_time = next;
|
934 | |
}
|
935 | |
|
936 | |
next = ngx_http_file_cache_expire(cache, 0);
|
937 | |
|
938 | |
now = ngx_time();
|
939 | |
|
940 | |
return (now + next < cache->next_clean_time) ? next:
|
941 | |
cache->next_clean_time - now;
|
|
1089 |
elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
|
|
1090 |
|
|
1091 |
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
|
|
1092 |
"http file cache manager time: %M", elapsed);
|
|
1093 |
|
|
1094 |
if (elapsed > 200) {
|
|
1095 |
|
|
1096 |
/*
|
|
1097 |
* if processing 100 files takes more than 200ms,
|
|
1098 |
* it seems that many operations require disk i/o,
|
|
1099 |
* therefore sleep 200ms
|
|
1100 |
*/
|
|
1101 |
|
|
1102 |
ngx_msleep(200);
|
|
1103 |
|
|
1104 |
ngx_time_update(0, 0);
|
|
1105 |
}
|
|
1106 |
|
|
1107 |
cache->last = ngx_current_msec;
|
|
1108 |
cache->files = 0;
|
|
1109 |
}
|
|
1110 |
|
|
1111 |
return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
|
942 | 1112 |
}
|
943 | 1113 |
|
944 | 1114 |
|
945 | 1115 |
static ngx_int_t
|
946 | |
ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
|
|
1116 |
ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
|
947 | 1117 |
{
|
948 | 1118 |
return NGX_OK;
|
949 | 1119 |
}
|
950 | 1120 |
|
951 | 1121 |
|
952 | 1122 |
static ngx_int_t
|
953 | |
ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
|
954 | |
{
|
955 | |
u_char *p, key[2 * NGX_HTTP_CACHE_KEY_LEN];
|
956 | |
ngx_int_t n;
|
957 | |
ngx_uint_t i;
|
958 | |
ngx_http_file_cache_t *cache;
|
959 | |
ngx_http_file_cache_node_t *node;
|
960 | |
|
961 | |
if (path->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
|
962 | |
goto clean;
|
963 | |
}
|
964 | |
|
965 | |
p = &path->data[path->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
|
|
1123 |
ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
|
|
1124 |
{
|
|
1125 |
ngx_http_file_cache_t *cache;
|
|
1126 |
|
|
1127 |
cache = ctx->data;
|
|
1128 |
|
|
1129 |
if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
|
|
1130 |
(void) ngx_http_file_cache_delete_file(ctx, path);
|
|
1131 |
}
|
|
1132 |
|
|
1133 |
return ngx_http_file_cache_manager_sleep(cache);
|
|
1134 |
}
|
|
1135 |
|
|
1136 |
|
|
1137 |
static ngx_int_t
|
|
1138 |
ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
|
|
1139 |
{
|
|
1140 |
u_char *p;
|
|
1141 |
ngx_fd_t fd;
|
|
1142 |
ngx_int_t n;
|
|
1143 |
ngx_uint_t i;
|
|
1144 |
ngx_file_info_t fi;
|
|
1145 |
ngx_http_cache_t c;
|
|
1146 |
ngx_http_file_cache_t *cache;
|
|
1147 |
ngx_http_file_cache_header_t h;
|
|
1148 |
|
|
1149 |
if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
|
|
1150 |
return NGX_ERROR;
|
|
1151 |
}
|
|
1152 |
|
|
1153 |
ngx_memzero(&c, sizeof(ngx_http_cache_t));
|
|
1154 |
|
|
1155 |
fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
|
|
1156 |
|
|
1157 |
if (fd == NGX_INVALID_FILE) {
|
|
1158 |
ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
|
|
1159 |
ngx_open_file_n " \"%s\" failed", name->data);
|
|
1160 |
return NGX_ERROR;
|
|
1161 |
}
|
|
1162 |
|
|
1163 |
c.file.fd = fd;
|
|
1164 |
c.file.name = *name;
|
|
1165 |
c.file.log = ctx->log;
|
|
1166 |
|
|
1167 |
n = ngx_read_file(&c.file, (u_char *) &h,
|
|
1168 |
sizeof(ngx_http_file_cache_header_t), 0);
|
|
1169 |
if (n == NGX_ERROR) {
|
|
1170 |
return NGX_ERROR;
|
|
1171 |
}
|
|
1172 |
|
|
1173 |
if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) {
|
|
1174 |
ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
|
|
1175 |
"cache file \"%s\" is too small", name->data);
|
|
1176 |
return NGX_ERROR;
|
|
1177 |
}
|
|
1178 |
|
|
1179 |
if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
|
|
1180 |
ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
|
|
1181 |
ngx_fd_info_n " \"%s\" failed", name->data);
|
|
1182 |
|
|
1183 |
} else {
|
|
1184 |
c.uniq = ngx_file_uniq(&fi);
|
|
1185 |
c.valid_sec = h.valid_sec;
|
|
1186 |
c.valid_msec = h.valid_msec;
|
|
1187 |
c.body_start = h.body_start;
|
|
1188 |
c.length = ngx_file_size(&fi);
|
|
1189 |
}
|
|
1190 |
|
|
1191 |
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
|
|
1192 |
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
|
|
1193 |
ngx_close_file_n " \"%s\" failed", name->data);
|
|
1194 |
}
|
|
1195 |
|
|
1196 |
if (c.body_start == 0) {
|
|
1197 |
return NGX_ERROR;
|
|
1198 |
}
|
|
1199 |
|
|
1200 |
p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
|
966 | 1201 |
|
967 | 1202 |
for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
|
968 | 1203 |
n = ngx_hextoi(p, 2);
|
969 | 1204 |
|
970 | 1205 |
if (n == NGX_ERROR) {
|
971 | |
goto clean;
|
|
1206 |
return NGX_ERROR;
|
972 | 1207 |
}
|
973 | 1208 |
|
974 | 1209 |
p += 2;
|
975 | 1210 |
|
976 | |
key[i] = (u_char) n;
|
|
1211 |
c.key[i] = (u_char) n;
|
977 | 1212 |
}
|
978 | 1213 |
|
979 | 1214 |
cache = ctx->data;
|
980 | 1215 |
|
|
1216 |
return ngx_http_file_cache_add(cache, &c);
|
|
1217 |
}
|
|
1218 |
|
|
1219 |
|
|
1220 |
static ngx_int_t
|
|
1221 |
ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
|
|
1222 |
{
|
|
1223 |
ngx_http_file_cache_node_t *fcn;
|
|
1224 |
|
981 | 1225 |
ngx_shmtx_lock(&cache->shpool->mutex);
|
982 | 1226 |
|
983 | |
node = ngx_http_file_cache_lookup(cache, key);
|
|
1227 |
fcn = ngx_http_file_cache_lookup(cache, c->key);
|
|
1228 |
|
|
1229 |
if (fcn == NULL) {
|
|
1230 |
|
|
1231 |
fcn = ngx_slab_alloc_locked(cache->shpool,
|
|
1232 |
sizeof(ngx_http_file_cache_node_t));
|
|
1233 |
if (fcn == NULL) {
|
|
1234 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
|
1235 |
return NGX_ERROR;
|
|
1236 |
}
|
|
1237 |
|
|
1238 |
ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
|
|
1239 |
|
|
1240 |
ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
|
|
1241 |
NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
|
|
1242 |
|
|
1243 |
ngx_rbtree_insert(cache->rbtree, &fcn->node);
|
|
1244 |
|
|
1245 |
fcn->uses = 1;
|
|
1246 |
fcn->count = 0;
|
|
1247 |
fcn->valid_msec = c->valid_msec;
|
|
1248 |
fcn->error = 0;
|
|
1249 |
fcn->exists = 1;
|
|
1250 |
fcn->uniq = c->uniq;
|
|
1251 |
fcn->valid_sec = c->valid_sec;
|
|
1252 |
fcn->body_start = c->body_start;
|
|
1253 |
fcn->length = c->length;
|
|
1254 |
|
|
1255 |
*cache->size += (c->length + cache->bsize - 1) / cache->bsize;
|
|
1256 |
|
|
1257 |
} else {
|
|
1258 |
ngx_queue_remove(&fcn->queue);
|
|
1259 |
}
|
|
1260 |
|
|
1261 |
fcn->expire = ngx_time() + cache->inactive;
|
|
1262 |
|
|
1263 |
ngx_queue_insert_head(cache->queue, &fcn->queue);
|
984 | 1264 |
|
985 | 1265 |
ngx_shmtx_unlock(&cache->shpool->mutex);
|
986 | 1266 |
|
987 | |
if (node) {
|
988 | |
return NGX_OK;
|
989 | |
}
|
990 | |
|
991 | |
clean:
|
992 | |
|
|
1267 |
return NGX_OK;
|
|
1268 |
}
|
|
1269 |
|
|
1270 |
|
|
1271 |
static ngx_int_t
|
|
1272 |
ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
|
|
1273 |
{
|
993 | 1274 |
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
|
994 | |
"http file cache clean: \"%s\"", path->data);
|
|
1275 |
"http file cache delete: \"%s\"", path->data);
|
995 | 1276 |
|
996 | 1277 |
if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
|
997 | 1278 |
ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
|
998 | 1279 |
ngx_delete_file_n " \"%s\" failed", path->data);
|
999 | |
return NGX_ERROR;
|
1000 | 1280 |
}
|
1001 | 1281 |
|
1002 | 1282 |
return NGX_OK;
|
|
1028 | 1308 |
char *
|
1029 | 1309 |
ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
1030 | 1310 |
{
|
|
1311 |
off_t max_size;
|
1031 | 1312 |
u_char *last, *p;
|
1032 | |
time_t inactive, clean_time, next;
|
|
1313 |
time_t inactive;
|
1033 | 1314 |
ssize_t size;
|
1034 | 1315 |
ngx_str_t s, name, *value;
|
1035 | 1316 |
ngx_uint_t i, n;
|
|
1046 | 1327 |
}
|
1047 | 1328 |
|
1048 | 1329 |
inactive = 600;
|
1049 | |
clean_time = 5 * 60 * 60;
|
1050 | 1330 |
|
1051 | 1331 |
name.len = 0;
|
1052 | 1332 |
size = 0;
|
|
1333 |
max_size = NGX_MAX_OFF_T_VALUE;
|
1053 | 1334 |
|
1054 | 1335 |
value = cf->args->elts;
|
1055 | 1336 |
|
|
1152 | 1433 |
continue;
|
1153 | 1434 |
}
|
1154 | 1435 |
|
1155 | |
if (ngx_strncmp(value[i].data, "clean_time=", 11) == 0) {
|
1156 | |
|
1157 | |
s.len = value[i].len - 11;
|
1158 | |
s.data = value[i].data + 11;
|
1159 | |
|
1160 | |
clean_time = ngx_parse_time(&s, 1);
|
1161 | |
if (clean_time < 0 || clean_time > 24 * 60 * 60) {
|
|
1436 |
if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
|
|
1437 |
|
|
1438 |
s.len = value[i].len - 9;
|
|
1439 |
s.data = value[i].data + 9;
|
|
1440 |
|
|
1441 |
max_size = ngx_parse_offset(&s);
|
|
1442 |
if (max_size < 0) {
|
1162 | 1443 |
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
1163 | |
"invalid clean_time value \"%V\"", &value[i]);
|
|
1444 |
"invalid max_size value \"%V\"", &value[i]);
|
1164 | 1445 |
return NGX_CONF_ERROR;
|
1165 | 1446 |
}
|
1166 | 1447 |
|
|
1179 | 1460 |
return NGX_CONF_ERROR;
|
1180 | 1461 |
}
|
1181 | 1462 |
|
1182 | |
cache->path->cleaner = ngx_http_file_cache_cleaner;
|
|
1463 |
cache->path->manager = ngx_http_file_cache_manager;
|
1183 | 1464 |
cache->path->data = cache;
|
1184 | 1465 |
|
1185 | 1466 |
if (ngx_add_path(cf, &cache->path) != NGX_OK) {
|
|
1197 | 1478 |
return NGX_CONF_ERROR;
|
1198 | 1479 |
}
|
1199 | 1480 |
|
1200 | |
next = ngx_next_time(clean_time);
|
1201 | |
|
1202 | |
if (next == -1) {
|
1203 | |
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
|
1204 | |
ngx_next_time_n " failed");
|
1205 | |
return NGX_CONF_ERROR;
|
1206 | |
}
|
1207 | 1481 |
|
1208 | 1482 |
cache->shm_zone->init = ngx_http_file_cache_init;
|
1209 | 1483 |
cache->shm_zone->data = cache;
|
1210 | 1484 |
|
1211 | 1485 |
cache->inactive = inactive;
|
1212 | |
cache->clean_time = clean_time;
|
1213 | |
cache->next_clean_time = next;
|
|
1486 |
cache->max_size = max_size;
|
1214 | 1487 |
|
1215 | 1488 |
return NGX_CONF_OK;
|
1216 | 1489 |
}
|