3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
14 static ngx_int_t
ngx_http_file_cache_lock(ngx_http_request_t
*r
,
16 static void ngx_http_file_cache_lock_wait_handler(ngx_event_t
*ev
);
17 static ngx_int_t
ngx_http_file_cache_read(ngx_http_request_t
*r
,
19 static ssize_t
ngx_http_file_cache_aio_read(ngx_http_request_t
*r
,
21 #if (NGX_HAVE_FILE_AIO)
22 static void ngx_http_cache_aio_event_handler(ngx_event_t
*ev
);
24 static ngx_int_t
ngx_http_file_cache_exists(ngx_http_file_cache_t
*cache
,
26 static ngx_int_t
ngx_http_file_cache_name(ngx_http_request_t
*r
,
28 static ngx_http_file_cache_node_t
*
29 ngx_http_file_cache_lookup(ngx_http_file_cache_t
*cache
, u_char
*key
);
30 static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t
*temp
,
31 ngx_rbtree_node_t
*node
, ngx_rbtree_node_t
*sentinel
);
32 static void ngx_http_file_cache_cleanup(void *data
);
33 static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t
*cache
);
34 static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t
*cache
);
35 static void ngx_http_file_cache_delete(ngx_http_file_cache_t
*cache
,
36 ngx_queue_t
*q
, u_char
*name
);
37 static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t
*cache
);
38 static ngx_int_t
ngx_http_file_cache_noop(ngx_tree_ctx_t
*ctx
,
40 static ngx_int_t
ngx_http_file_cache_manage_file(ngx_tree_ctx_t
*ctx
,
42 static ngx_int_t
ngx_http_file_cache_add_file(ngx_tree_ctx_t
*ctx
,
44 static ngx_int_t
ngx_http_file_cache_add(ngx_http_file_cache_t
*cache
,
46 static ngx_int_t
ngx_http_file_cache_delete_file(ngx_tree_ctx_t
*ctx
,
50 ngx_str_t ngx_http_cache_status
[] = {
53 ngx_string("EXPIRED"),
55 ngx_string("UPDATING"),
60 static u_char ngx_http_file_cache_key
[] = { LF
, 'K', 'E', 'Y', ':', ' ' };
64 ngx_http_file_cache_init(ngx_shm_zone_t
*shm_zone
, void *data
)
66 ngx_http_file_cache_t
*ocache
= data
;
70 ngx_http_file_cache_t
*cache
;
72 cache
= shm_zone
->data
;
75 if (ngx_strcmp(cache
->path
->name
.data
, ocache
->path
->name
.data
) != 0) {
76 ngx_log_error(NGX_LOG_EMERG
, shm_zone
->shm
.log
, 0,
77 "cache \"%V\" uses the \"%V\" cache path "
78 "while previously it used the \"%V\" cache path",
79 &shm_zone
->shm
.name
, &cache
->path
->name
,
85 for (n
= 0; n
< 3; n
++) {
86 if (cache
->path
->level
[n
] != ocache
->path
->level
[n
]) {
87 ngx_log_error(NGX_LOG_EMERG
, shm_zone
->shm
.log
, 0,
88 "cache \"%V\" had previously different levels",
94 cache
->sh
= ocache
->sh
;
96 cache
->shpool
= ocache
->shpool
;
97 cache
->bsize
= ocache
->bsize
;
99 cache
->max_size
/= cache
->bsize
;
101 if (!cache
->sh
->cold
|| cache
->sh
->loading
) {
102 cache
->path
->loader
= NULL
;
108 cache
->shpool
= (ngx_slab_pool_t
*) shm_zone
->shm
.addr
;
110 if (shm_zone
->shm
.exists
) {
111 cache
->sh
= cache
->shpool
->data
;
112 cache
->bsize
= ngx_fs_bsize(cache
->path
->name
.data
);
117 cache
->sh
= ngx_slab_alloc(cache
->shpool
, sizeof(ngx_http_file_cache_sh_t
));
118 if (cache
->sh
== NULL
) {
122 cache
->shpool
->data
= cache
->sh
;
124 ngx_rbtree_init(&cache
->sh
->rbtree
, &cache
->sh
->sentinel
,
125 ngx_http_file_cache_rbtree_insert_value
);
127 ngx_queue_init(&cache
->sh
->queue
);
130 cache
->sh
->loading
= 0;
133 cache
->bsize
= ngx_fs_bsize(cache
->path
->name
.data
);
135 cache
->max_size
/= cache
->bsize
;
137 len
= sizeof(" in cache keys zone \"\"") + shm_zone
->shm
.name
.len
;
139 cache
->shpool
->log_ctx
= ngx_slab_alloc(cache
->shpool
, len
);
140 if (cache
->shpool
->log_ctx
== NULL
) {
144 ngx_sprintf(cache
->shpool
->log_ctx
, " in cache keys zone \"%V\"%Z",
145 &shm_zone
->shm
.name
);
152 ngx_http_file_cache_new(ngx_http_request_t
*r
)
156 c
= ngx_pcalloc(r
->pool
, sizeof(ngx_http_cache_t
));
161 if (ngx_array_init(&c
->keys
, r
->pool
, 4, sizeof(ngx_str_t
)) != NGX_OK
) {
166 c
->file
.log
= r
->connection
->log
;
167 c
->file
.fd
= NGX_INVALID_FILE
;
174 ngx_http_file_cache_create(ngx_http_request_t
*r
)
177 ngx_pool_cleanup_t
*cln
;
178 ngx_http_file_cache_t
*cache
;
181 cache
= c
->file_cache
;
183 cln
= ngx_pool_cleanup_add(r
->pool
, 0);
188 cln
->handler
= ngx_http_file_cache_cleanup
;
191 if (ngx_http_file_cache_exists(cache
, c
) == NGX_ERROR
) {
195 if (ngx_http_file_cache_name(r
, cache
->path
) != NGX_OK
) {
204 ngx_http_file_cache_create_key(ngx_http_request_t
*r
)
216 ngx_crc32_init(c
->crc32
);
220 for (i
= 0; i
< c
->keys
.nelts
; i
++) {
221 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
222 "http cache key: \"%V\"", &key
[i
]);
226 ngx_crc32_update(&c
->crc32
, key
[i
].data
, key
[i
].len
);
227 ngx_md5_update(&md5
, key
[i
].data
, key
[i
].len
);
230 c
->header_start
= sizeof(ngx_http_file_cache_header_t
)
231 + sizeof(ngx_http_file_cache_key
) + len
+ 1;
233 ngx_crc32_final(c
->crc32
);
234 ngx_md5_final(c
->key
, &md5
);
239 ngx_http_file_cache_open(ngx_http_request_t
*r
)
242 ngx_uint_t cold
, test
;
244 ngx_pool_cleanup_t
*cln
;
245 ngx_open_file_info_t of
;
246 ngx_http_file_cache_t
*cache
;
247 ngx_http_core_loc_conf_t
*clcf
;
256 return ngx_http_file_cache_read(r
, c
);
259 cache
= c
->file_cache
;
261 if (c
->node
== NULL
) {
262 cln
= ngx_pool_cleanup_add(r
->pool
, 0);
267 cln
->handler
= ngx_http_file_cache_cleanup
;
271 rc
= ngx_http_file_cache_exists(cache
, c
);
273 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
274 "http file cache exists: %i e:%d", rc
, c
->exists
);
276 if (rc
== NGX_ERROR
) {
280 if (rc
== NGX_AGAIN
) {
281 return NGX_HTTP_CACHE_SCARCE
;
284 cold
= cache
->sh
->cold
;
293 test
= c
->exists
? 1 : 0;
296 } else { /* rc == NGX_DECLINED */
298 if (c
->min_uses
> 1) {
301 return NGX_HTTP_CACHE_SCARCE
;
305 rv
= NGX_HTTP_CACHE_SCARCE
;
314 if (ngx_http_file_cache_name(r
, cache
->path
) != NGX_OK
) {
322 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
324 ngx_memzero(&of
, sizeof(ngx_open_file_info_t
));
327 of
.valid
= clcf
->open_file_cache_valid
;
328 of
.min_uses
= clcf
->open_file_cache_min_uses
;
329 of
.events
= clcf
->open_file_cache_events
;
330 of
.directio
= NGX_OPEN_FILE_DIRECTIO_OFF
;
331 of
.read_ahead
= clcf
->read_ahead
;
333 if (ngx_open_cached_file(clcf
->open_file_cache
, &c
->file
.name
, &of
, r
->pool
)
346 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, of
.err
,
347 ngx_open_file_n
" \"%s\" failed", c
->file
.name
.data
);
352 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
353 "http file cache fd: %d", of
.fd
);
356 c
->file
.log
= r
->connection
->log
;
359 c
->fs_size
= (of
.fs_size
+ cache
->bsize
- 1) / cache
->bsize
;
361 c
->buf
= ngx_create_temp_buf(r
->pool
, c
->body_start
);
362 if (c
->buf
== NULL
) {
366 return ngx_http_file_cache_read(r
, c
);
370 if (rv
== NGX_DECLINED
) {
371 return ngx_http_file_cache_lock(r
, c
);
379 ngx_http_file_cache_lock(ngx_http_request_t
*r
, ngx_http_cache_t
*c
)
381 ngx_msec_t now
, timer
;
382 ngx_http_file_cache_t
*cache
;
388 cache
= c
->file_cache
;
390 ngx_shmtx_lock(&cache
->shpool
->mutex
);
392 if (!c
->node
->updating
) {
393 c
->node
->updating
= 1;
397 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
399 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
400 "http file cache lock u:%d wt:%M",
401 c
->updating
, c
->wait_time
);
409 now
= ngx_current_msec
;
411 if (c
->wait_time
== 0) {
412 c
->wait_time
= now
+ c
->lock_timeout
;
414 c
->wait_event
.handler
= ngx_http_file_cache_lock_wait_handler
;
415 c
->wait_event
.data
= r
;
416 c
->wait_event
.log
= r
->connection
->log
;
419 timer
= c
->wait_time
- now
;
421 ngx_add_timer(&c
->wait_event
, (timer
> 500) ? 500 : timer
);
430 ngx_http_file_cache_lock_wait_handler(ngx_event_t
*ev
)
435 ngx_http_request_t
*r
;
436 ngx_http_file_cache_t
*cache
;
441 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, ev
->log
, 0,
442 "http file cache wait handler wt:%M cur:%M",
443 c
->wait_time
, ngx_current_msec
);
445 timer
= c
->wait_time
- ngx_current_msec
;
447 if ((ngx_msec_int_t
) timer
<= 0) {
448 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, ev
->log
, 0,
449 "http file cache lock timeout");
454 cache
= c
->file_cache
;
457 ngx_shmtx_lock(&cache
->shpool
->mutex
);
459 if (c
->node
->updating
) {
463 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
466 ngx_add_timer(ev
, (timer
> 500) ? 500 : timer
);
474 r
->connection
->write
->handler(r
->connection
->write
);
479 ngx_http_file_cache_read(ngx_http_request_t
*r
, ngx_http_cache_t
*c
)
484 ngx_http_file_cache_t
*cache
;
485 ngx_http_file_cache_header_t
*h
;
487 n
= ngx_http_file_cache_aio_read(r
, c
);
493 if ((size_t) n
< c
->header_start
) {
494 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, 0,
495 "cache file \"%s\" is too small", c
->file
.name
.data
);
499 h
= (ngx_http_file_cache_header_t
*) c
->buf
->pos
;
501 if (h
->crc32
!= c
->crc32
) {
502 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, 0,
503 "cache file \"%s\" has md5 collision", c
->file
.name
.data
);
507 if (h
->body_start
> c
->body_start
) {
508 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, 0,
509 "cache file \"%s\" has too long header",
516 c
->valid_sec
= h
->valid_sec
;
517 c
->last_modified
= h
->last_modified
;
519 c
->valid_msec
= h
->valid_msec
;
520 c
->header_start
= h
->header_start
;
521 c
->body_start
= h
->body_start
;
525 cache
= c
->file_cache
;
527 if (cache
->sh
->cold
) {
529 ngx_shmtx_lock(&cache
->shpool
->mutex
);
531 if (!c
->node
->exists
) {
533 c
->node
->body_start
= c
->body_start
;
535 c
->node
->uniq
= c
->uniq
;
536 c
->node
->fs_size
= c
->fs_size
;
538 cache
->sh
->size
+= c
->fs_size
;
541 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
546 if (c
->valid_sec
< now
) {
548 ngx_shmtx_lock(&cache
->shpool
->mutex
);
550 if (c
->node
->updating
) {
551 rc
= NGX_HTTP_CACHE_UPDATING
;
554 c
->node
->updating
= 1;
556 rc
= NGX_HTTP_CACHE_STALE
;
559 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
561 ngx_log_debug3(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
562 "http file cache expired: %i %T %T",
563 rc
, c
->valid_sec
, now
);
573 ngx_http_file_cache_aio_read(ngx_http_request_t
*r
, ngx_http_cache_t
*c
)
575 #if (NGX_HAVE_FILE_AIO)
577 ngx_http_core_loc_conf_t
*clcf
;
583 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
589 n
= ngx_file_aio_read(&c
->file
, c
->buf
->pos
, c
->body_start
, 0, r
->pool
);
591 if (n
!= NGX_AGAIN
) {
595 c
->file
.aio
->data
= r
;
596 c
->file
.aio
->handler
= ngx_http_cache_aio_event_handler
;
607 return ngx_read_file(&c
->file
, c
->buf
->pos
, c
->body_start
, 0);
611 #if (NGX_HAVE_FILE_AIO)
614 ngx_http_cache_aio_event_handler(ngx_event_t
*ev
)
616 ngx_event_aio_t
*aio
;
617 ngx_http_request_t
*r
;
625 r
->connection
->write
->handler(r
->connection
->write
);
632 ngx_http_file_cache_exists(ngx_http_file_cache_t
*cache
, ngx_http_cache_t
*c
)
635 ngx_http_file_cache_node_t
*fcn
;
637 ngx_shmtx_lock(&cache
->shpool
->mutex
);
642 fcn
= ngx_http_file_cache_lookup(cache
, c
->key
);
646 ngx_queue_remove(&fcn
->queue
);
648 if (c
->node
== NULL
) {
655 if (fcn
->valid_sec
< ngx_time()) {
664 if (fcn
->exists
|| fcn
->uses
>= c
->min_uses
) {
666 c
->exists
= fcn
->exists
;
667 if (fcn
->body_start
) {
668 c
->body_start
= fcn
->body_start
;
681 fcn
= ngx_slab_alloc_locked(cache
->shpool
,
682 sizeof(ngx_http_file_cache_node_t
));
684 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
686 (void) ngx_http_file_cache_forced_expire(cache
);
688 ngx_shmtx_lock(&cache
->shpool
->mutex
);
690 fcn
= ngx_slab_alloc_locked(cache
->shpool
,
691 sizeof(ngx_http_file_cache_node_t
));
698 ngx_memcpy((u_char
*) &fcn
->node
.key
, c
->key
, sizeof(ngx_rbtree_key_t
));
700 ngx_memcpy(fcn
->key
, &c
->key
[sizeof(ngx_rbtree_key_t
)],
701 NGX_HTTP_CACHE_KEY_LEN
- sizeof(ngx_rbtree_key_t
));
703 ngx_rbtree_insert(&cache
->sh
->rbtree
, &fcn
->node
);
724 fcn
->expire
= ngx_time() + cache
->inactive
;
726 ngx_queue_insert_head(&cache
->sh
->queue
, &fcn
->queue
);
729 c
->error
= fcn
->error
;
734 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
741 ngx_http_file_cache_name(ngx_http_request_t
*r
, ngx_path_t
*path
)
748 if (c
->file
.name
.len
) {
752 c
->file
.name
.len
= path
->name
.len
+ 1 + path
->len
753 + 2 * NGX_HTTP_CACHE_KEY_LEN
;
755 c
->file
.name
.data
= ngx_pnalloc(r
->pool
, c
->file
.name
.len
+ 1);
756 if (c
->file
.name
.data
== NULL
) {
760 ngx_memcpy(c
->file
.name
.data
, path
->name
.data
, path
->name
.len
);
762 p
= c
->file
.name
.data
+ path
->name
.len
+ 1 + path
->len
;
763 p
= ngx_hex_dump(p
, c
->key
, NGX_HTTP_CACHE_KEY_LEN
);
766 ngx_create_hashed_filename(path
, c
->file
.name
.data
, c
->file
.name
.len
);
768 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
769 "cache file: \"%s\"", c
->file
.name
.data
);
775 static ngx_http_file_cache_node_t
*
776 ngx_http_file_cache_lookup(ngx_http_file_cache_t
*cache
, u_char
*key
)
779 ngx_rbtree_key_t node_key
;
780 ngx_rbtree_node_t
*node
, *sentinel
;
781 ngx_http_file_cache_node_t
*fcn
;
783 ngx_memcpy((u_char
*) &node_key
, key
, sizeof(ngx_rbtree_key_t
));
785 node
= cache
->sh
->rbtree
.root
;
786 sentinel
= cache
->sh
->rbtree
.sentinel
;
788 while (node
!= sentinel
) {
790 if (node_key
< node
->key
) {
795 if (node_key
> node
->key
) {
800 /* node_key == node->key */
802 fcn
= (ngx_http_file_cache_node_t
*) node
;
804 rc
= ngx_memcmp(&key
[sizeof(ngx_rbtree_key_t
)], fcn
->key
,
805 NGX_HTTP_CACHE_KEY_LEN
- sizeof(ngx_rbtree_key_t
));
811 node
= (rc
< 0) ? node
->left
: node
->right
;
821 ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t
*temp
,
822 ngx_rbtree_node_t
*node
, ngx_rbtree_node_t
*sentinel
)
824 ngx_rbtree_node_t
**p
;
825 ngx_http_file_cache_node_t
*cn
, *cnt
;
829 if (node
->key
< temp
->key
) {
833 } else if (node
->key
> temp
->key
) {
837 } else { /* node->key == temp->key */
839 cn
= (ngx_http_file_cache_node_t
*) node
;
840 cnt
= (ngx_http_file_cache_node_t
*) temp
;
842 p
= (ngx_memcmp(cn
->key
, cnt
->key
,
843 NGX_HTTP_CACHE_KEY_LEN
- sizeof(ngx_rbtree_key_t
))
845 ? &temp
->left
: &temp
->right
;
848 if (*p
== sentinel
) {
857 node
->left
= sentinel
;
858 node
->right
= sentinel
;
864 ngx_http_file_cache_set_header(ngx_http_request_t
*r
, u_char
*buf
)
866 ngx_http_file_cache_header_t
*h
= (ngx_http_file_cache_header_t
*) buf
;
873 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
874 "http file cache set header");
878 h
->valid_sec
= c
->valid_sec
;
879 h
->last_modified
= c
->last_modified
;
882 h
->valid_msec
= (u_short
) c
->valid_msec
;
883 h
->header_start
= (u_short
) c
->header_start
;
884 h
->body_start
= (u_short
) c
->body_start
;
886 p
= buf
+ sizeof(ngx_http_file_cache_header_t
);
888 p
= ngx_cpymem(p
, ngx_http_file_cache_key
, sizeof(ngx_http_file_cache_key
));
891 for (i
= 0; i
< c
->keys
.nelts
; i
++) {
892 p
= ngx_copy(p
, key
[i
].data
, key
[i
].len
);
900 ngx_http_file_cache_update(ngx_http_request_t
*r
, ngx_temp_file_t
*tf
)
904 ngx_file_uniq_t uniq
;
907 ngx_ext_rename_file_t ext
;
908 ngx_http_file_cache_t
*cache
;
916 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
917 "http file cache update");
922 cache
= c
->file_cache
;
927 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
928 "http file cache rename: \"%s\" to \"%s\"",
929 tf
->file
.name
.data
, c
->file
.name
.data
);
931 ext
.access
= NGX_FILE_OWNER_ACCESS
;
932 ext
.path_access
= NGX_FILE_OWNER_ACCESS
;
936 ext
.log
= r
->connection
->log
;
938 rc
= ngx_ext_rename_file(&tf
->file
.name
, &c
->file
.name
, &ext
);
942 if (ngx_fd_info(tf
->file
.fd
, &fi
) == NGX_FILE_ERROR
) {
943 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, ngx_errno
,
944 ngx_fd_info_n
" \"%s\" failed", tf
->file
.name
.data
);
949 uniq
= ngx_file_uniq(&fi
);
950 fs_size
= (ngx_file_fs_size(&fi
) + cache
->bsize
- 1) / cache
->bsize
;
954 ngx_shmtx_lock(&cache
->shpool
->mutex
);
957 c
->node
->uniq
= uniq
;
958 c
->node
->body_start
= c
->body_start
;
960 cache
->sh
->size
+= fs_size
- c
->node
->fs_size
;
961 c
->node
->fs_size
= fs_size
;
967 c
->node
->updating
= 0;
969 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
974 ngx_http_cache_send(ngx_http_request_t
*r
)
983 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
984 "http file cache send: %s", c
->file
.name
.data
);
986 if (r
!= r
->main
&& c
->length
- c
->body_start
== 0) {
987 return ngx_http_send_header(r
);
990 /* we need to allocate all before the header would be sent */
992 b
= ngx_pcalloc(r
->pool
, sizeof(ngx_buf_t
));
994 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
997 b
->file
= ngx_pcalloc(r
->pool
, sizeof(ngx_file_t
));
998 if (b
->file
== NULL
) {
999 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
1002 rc
= ngx_http_send_header(r
);
1004 if (rc
== NGX_ERROR
|| rc
> NGX_OK
|| r
->header_only
) {
1008 b
->file_pos
= c
->body_start
;
1009 b
->file_last
= c
->length
;
1011 b
->in_file
= (c
->length
- c
->body_start
) ? 1: 0;
1012 b
->last_buf
= (r
== r
->main
) ? 1: 0;
1013 b
->last_in_chain
= 1;
1015 b
->file
->fd
= c
->file
.fd
;
1016 b
->file
->name
= c
->file
.name
;
1017 b
->file
->log
= r
->connection
->log
;
1022 return ngx_http_output_filter(r
, &out
);
1027 ngx_http_file_cache_free(ngx_http_cache_t
*c
, ngx_temp_file_t
*tf
)
1029 ngx_http_file_cache_t
*cache
;
1030 ngx_http_file_cache_node_t
*fcn
;
1032 if (c
->updated
|| c
->node
== NULL
) {
1036 cache
= c
->file_cache
;
1038 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, c
->file
.log
, 0,
1039 "http file cache free, fd: %d", c
->file
.fd
);
1041 ngx_shmtx_lock(&cache
->shpool
->mutex
);
1051 fcn
->error
= c
->error
;
1054 fcn
->valid_sec
= c
->valid_sec
;
1055 fcn
->valid_msec
= c
->valid_msec
;
1058 } else if (!fcn
->exists
&& fcn
->count
== 0 && c
->min_uses
== 1) {
1059 ngx_queue_remove(&fcn
->queue
);
1060 ngx_rbtree_delete(&cache
->sh
->rbtree
, &fcn
->node
);
1061 ngx_slab_free_locked(cache
->shpool
, fcn
);
1065 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1071 if (tf
&& tf
->file
.fd
!= NGX_INVALID_FILE
) {
1072 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, c
->file
.log
, 0,
1073 "http file cache incomplete: \"%s\"",
1074 tf
->file
.name
.data
);
1076 if (ngx_delete_file(tf
->file
.name
.data
) == NGX_FILE_ERROR
) {
1077 ngx_log_error(NGX_LOG_CRIT
, c
->file
.log
, ngx_errno
,
1078 ngx_delete_file_n
" \"%s\" failed",
1079 tf
->file
.name
.data
);
1084 if (c
->wait_event
.timer_set
) {
1085 ngx_del_timer(&c
->wait_event
);
1091 ngx_http_file_cache_cleanup(void *data
)
1093 ngx_http_cache_t
*c
= data
;
1099 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, c
->file
.log
, 0,
1100 "http file cache cleanup");
1103 ngx_log_error(NGX_LOG_ALERT
, c
->file
.log
, 0,
1104 "stalled cache updating, error:%ui", c
->error
);
1107 ngx_http_file_cache_free(c
, NULL
);
1112 ngx_http_file_cache_forced_expire(ngx_http_file_cache_t
*cache
)
1120 ngx_http_file_cache_node_t
*fcn
;
1122 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1123 "http file cache forced expire");
1126 len
= path
->name
.len
+ 1 + path
->len
+ 2 * NGX_HTTP_CACHE_KEY_LEN
;
1128 name
= ngx_alloc(len
+ 1, ngx_cycle
->log
);
1133 ngx_memcpy(name
, path
->name
.data
, path
->name
.len
);
1138 ngx_shmtx_lock(&cache
->shpool
->mutex
);
1140 for (q
= ngx_queue_last(&cache
->sh
->queue
);
1141 q
!= ngx_queue_sentinel(&cache
->sh
->queue
);
1142 q
= ngx_queue_prev(q
))
1144 fcn
= ngx_queue_data(q
, ngx_http_file_cache_node_t
, queue
);
1146 ngx_log_debug6(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1147 "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
1148 fcn
->count
, fcn
->exists
,
1149 fcn
->key
[0], fcn
->key
[1], fcn
->key
[2], fcn
->key
[3]);
1151 if (fcn
->count
== 0) {
1152 ngx_http_file_cache_delete(cache
, q
, name
);
1166 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1175 ngx_http_file_cache_expire(ngx_http_file_cache_t
*cache
)
1182 ngx_http_file_cache_node_t
*fcn
;
1183 u_char key
[2 * NGX_HTTP_CACHE_KEY_LEN
];
1185 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1186 "http file cache expire");
1189 len
= path
->name
.len
+ 1 + path
->len
+ 2 * NGX_HTTP_CACHE_KEY_LEN
;
1191 name
= ngx_alloc(len
+ 1, ngx_cycle
->log
);
1196 ngx_memcpy(name
, path
->name
.data
, path
->name
.len
);
1200 ngx_shmtx_lock(&cache
->shpool
->mutex
);
1204 if (ngx_queue_empty(&cache
->sh
->queue
)) {
1209 q
= ngx_queue_last(&cache
->sh
->queue
);
1211 fcn
= ngx_queue_data(q
, ngx_http_file_cache_node_t
, queue
);
1213 wait
= fcn
->expire
- now
;
1216 wait
= wait
> 10 ? 10 : wait
;
1220 ngx_log_debug6(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1221 "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
1222 fcn
->count
, fcn
->exists
,
1223 fcn
->key
[0], fcn
->key
[1], fcn
->key
[2], fcn
->key
[3]);
1225 if (fcn
->count
== 0) {
1226 ngx_http_file_cache_delete(cache
, q
, name
);
1230 if (fcn
->deleting
) {
1235 p
= ngx_hex_dump(key
, (u_char
*) &fcn
->node
.key
,
1236 sizeof(ngx_rbtree_key_t
));
1237 len
= NGX_HTTP_CACHE_KEY_LEN
- sizeof(ngx_rbtree_key_t
);
1238 (void) ngx_hex_dump(p
, fcn
->key
, len
);
1241 * abnormally exited workers may leave locked cache entries,
1242 * and although it may be safe to remove them completely,
1243 * we prefer to just move them to the top of the inactive queue
1246 ngx_queue_remove(q
);
1247 fcn
->expire
= ngx_time() + cache
->inactive
;
1248 ngx_queue_insert_head(&cache
->sh
->queue
, &fcn
->queue
);
1250 ngx_log_error(NGX_LOG_ALERT
, ngx_cycle
->log
, 0,
1251 "ignore long locked inactive cache entry %*s, count:%d",
1252 2 * NGX_HTTP_CACHE_KEY_LEN
, key
, fcn
->count
);
1255 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1264 ngx_http_file_cache_delete(ngx_http_file_cache_t
*cache
, ngx_queue_t
*q
,
1270 ngx_http_file_cache_node_t
*fcn
;
1272 fcn
= ngx_queue_data(q
, ngx_http_file_cache_node_t
, queue
);
1275 cache
->sh
->size
-= fcn
->fs_size
;
1278 p
= name
+ path
->name
.len
+ 1 + path
->len
;
1279 p
= ngx_hex_dump(p
, (u_char
*) &fcn
->node
.key
,
1280 sizeof(ngx_rbtree_key_t
));
1281 len
= NGX_HTTP_CACHE_KEY_LEN
- sizeof(ngx_rbtree_key_t
);
1282 p
= ngx_hex_dump(p
, fcn
->key
, len
);
1287 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1289 len
= path
->name
.len
+ 1 + path
->len
+ 2 * NGX_HTTP_CACHE_KEY_LEN
;
1290 ngx_create_hashed_filename(path
, name
, len
);
1292 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1293 "http file cache expire: \"%s\"", name
);
1295 if (ngx_delete_file(name
) == NGX_FILE_ERROR
) {
1296 ngx_log_error(NGX_LOG_CRIT
, ngx_cycle
->log
, ngx_errno
,
1297 ngx_delete_file_n
" \"%s\" failed", name
);
1300 ngx_shmtx_lock(&cache
->shpool
->mutex
);
1305 if (fcn
->count
== 0) {
1306 ngx_queue_remove(q
);
1307 ngx_rbtree_delete(&cache
->sh
->rbtree
, &fcn
->node
);
1308 ngx_slab_free_locked(cache
->shpool
, fcn
);
1314 ngx_http_file_cache_manager(void *data
)
1316 ngx_http_file_cache_t
*cache
= data
;
1321 next
= ngx_http_file_cache_expire(cache
);
1323 cache
->last
= ngx_current_msec
;
1327 ngx_shmtx_lock(&cache
->shpool
->mutex
);
1329 size
= cache
->sh
->size
;
1331 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1333 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1334 "http file cache size: %O", size
);
1336 if (size
< cache
->max_size
) {
1340 wait
= ngx_http_file_cache_forced_expire(cache
);
1346 if (ngx_quit
|| ngx_terminate
) {
1354 ngx_http_file_cache_loader(void *data
)
1356 ngx_http_file_cache_t
*cache
= data
;
1358 ngx_tree_ctx_t tree
;
1360 if (!cache
->sh
->cold
|| cache
->sh
->loading
) {
1364 if (!ngx_atomic_cmp_set(&cache
->sh
->loading
, 0, ngx_pid
)) {
1368 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1369 "http file cache loader");
1371 tree
.init_handler
= NULL
;
1372 tree
.file_handler
= ngx_http_file_cache_manage_file
;
1373 tree
.pre_tree_handler
= ngx_http_file_cache_noop
;
1374 tree
.post_tree_handler
= ngx_http_file_cache_noop
;
1375 tree
.spec_handler
= ngx_http_file_cache_delete_file
;
1378 tree
.log
= ngx_cycle
->log
;
1380 cache
->last
= ngx_current_msec
;
1383 if (ngx_walk_tree(&tree
, &cache
->path
->name
) == NGX_ABORT
) {
1384 cache
->sh
->loading
= 0;
1388 cache
->sh
->cold
= 0;
1389 cache
->sh
->loading
= 0;
1391 ngx_log_error(NGX_LOG_NOTICE
, ngx_cycle
->log
, 0,
1392 "http file cache: %V %.3fM, bsize: %uz",
1394 ((double) cache
->sh
->size
* cache
->bsize
) / (1024 * 1024),
1400 ngx_http_file_cache_noop(ngx_tree_ctx_t
*ctx
, ngx_str_t
*path
)
1407 ngx_http_file_cache_manage_file(ngx_tree_ctx_t
*ctx
, ngx_str_t
*path
)
1410 ngx_http_file_cache_t
*cache
;
1414 if (ngx_http_file_cache_add_file(ctx
, path
) != NGX_OK
) {
1415 (void) ngx_http_file_cache_delete_file(ctx
, path
);
1418 if (++cache
->files
>= cache
->loader_files
) {
1419 ngx_http_file_cache_loader_sleep(cache
);
1424 elapsed
= ngx_abs((ngx_msec_int_t
) (ngx_current_msec
- cache
->last
));
1426 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, ngx_cycle
->log
, 0,
1427 "http file cache loader time elapsed: %M", elapsed
);
1429 if (elapsed
>= cache
->loader_threshold
) {
1430 ngx_http_file_cache_loader_sleep(cache
);
1434 return (ngx_quit
|| ngx_terminate
) ? NGX_ABORT
: NGX_OK
;
1439 ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t
*cache
)
1441 ngx_msleep(cache
->loader_sleep
);
1445 cache
->last
= ngx_current_msec
;
1451 ngx_http_file_cache_add_file(ngx_tree_ctx_t
*ctx
, ngx_str_t
*name
)
1457 ngx_http_file_cache_t
*cache
;
1459 if (name
->len
< 2 * NGX_HTTP_CACHE_KEY_LEN
) {
1463 if (ctx
->size
< (off_t
) sizeof(ngx_http_file_cache_header_t
)) {
1464 ngx_log_error(NGX_LOG_CRIT
, ctx
->log
, 0,
1465 "cache file \"%s\" is too small", name
->data
);
1469 ngx_memzero(&c
, sizeof(ngx_http_cache_t
));
1472 c
.length
= ctx
->size
;
1473 c
.fs_size
= (ctx
->fs_size
+ cache
->bsize
- 1) / cache
->bsize
;
1475 p
= &name
->data
[name
->len
- 2 * NGX_HTTP_CACHE_KEY_LEN
];
1477 for (i
= 0; i
< NGX_HTTP_CACHE_KEY_LEN
; i
++) {
1478 n
= ngx_hextoi(p
, 2);
1480 if (n
== NGX_ERROR
) {
1486 c
.key
[i
] = (u_char
) n
;
1489 return ngx_http_file_cache_add(cache
, &c
);
1494 ngx_http_file_cache_add(ngx_http_file_cache_t
*cache
, ngx_http_cache_t
*c
)
1496 ngx_http_file_cache_node_t
*fcn
;
1498 ngx_shmtx_lock(&cache
->shpool
->mutex
);
1500 fcn
= ngx_http_file_cache_lookup(cache
, c
->key
);
1504 fcn
= ngx_slab_alloc_locked(cache
->shpool
,
1505 sizeof(ngx_http_file_cache_node_t
));
1507 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1511 ngx_memcpy((u_char
*) &fcn
->node
.key
, c
->key
, sizeof(ngx_rbtree_key_t
));
1513 ngx_memcpy(fcn
->key
, &c
->key
[sizeof(ngx_rbtree_key_t
)],
1514 NGX_HTTP_CACHE_KEY_LEN
- sizeof(ngx_rbtree_key_t
));
1516 ngx_rbtree_insert(&cache
->sh
->rbtree
, &fcn
->node
);
1520 fcn
->valid_msec
= 0;
1527 fcn
->body_start
= 0;
1528 fcn
->fs_size
= c
->fs_size
;
1530 cache
->sh
->size
+= c
->fs_size
;
1533 ngx_queue_remove(&fcn
->queue
);
1536 fcn
->expire
= ngx_time() + cache
->inactive
;
1538 ngx_queue_insert_head(&cache
->sh
->queue
, &fcn
->queue
);
1540 ngx_shmtx_unlock(&cache
->shpool
->mutex
);
1547 ngx_http_file_cache_delete_file(ngx_tree_ctx_t
*ctx
, ngx_str_t
*path
)
1549 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, ctx
->log
, 0,
1550 "http file cache delete: \"%s\"", path
->data
);
1552 if (ngx_delete_file(path
->data
) == NGX_FILE_ERROR
) {
1553 ngx_log_error(NGX_LOG_CRIT
, ctx
->log
, ngx_errno
,
1554 ngx_delete_file_n
" \"%s\" failed", path
->data
);
1562 ngx_http_file_cache_valid(ngx_array_t
*cache_valid
, ngx_uint_t status
)
1565 ngx_http_cache_valid_t
*valid
;
1567 if (cache_valid
== NULL
) {
1571 valid
= cache_valid
->elts
;
1572 for (i
= 0; i
< cache_valid
->nelts
; i
++) {
1574 if (valid
[i
].status
== 0) {
1575 return valid
[i
].valid
;
1578 if (valid
[i
].status
== status
) {
1579 return valid
[i
].valid
;
1588 ngx_http_file_cache_set_slot(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
1594 ngx_str_t s
, name
, *value
;
1595 ngx_int_t loader_files
;
1596 ngx_msec_t loader_sleep
, loader_threshold
;
1598 ngx_http_file_cache_t
*cache
;
1600 cache
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_file_cache_t
));
1601 if (cache
== NULL
) {
1602 return NGX_CONF_ERROR
;
1605 cache
->path
= ngx_pcalloc(cf
->pool
, sizeof(ngx_path_t
));
1606 if (cache
->path
== NULL
) {
1607 return NGX_CONF_ERROR
;
1613 loader_threshold
= 200;
1617 max_size
= NGX_MAX_OFF_T_VALUE
;
1619 value
= cf
->args
->elts
;
1621 cache
->path
->name
= value
[1];
1623 if (cache
->path
->name
.data
[cache
->path
->name
.len
- 1] == '/') {
1624 cache
->path
->name
.len
--;
1627 if (ngx_conf_full_name(cf
->cycle
, &cache
->path
->name
, 0) != NGX_OK
) {
1628 return NGX_CONF_ERROR
;
1631 for (i
= 2; i
< cf
->args
->nelts
; i
++) {
1633 if (ngx_strncmp(value
[i
].data
, "levels=", 7) == 0) {
1635 p
= value
[i
].data
+ 7;
1636 last
= value
[i
].data
+ value
[i
].len
;
1638 for (n
= 0; n
< 3 && p
< last
; n
++) {
1640 if (*p
> '0' && *p
< '3') {
1642 cache
->path
->level
[n
] = *p
++ - '0';
1643 cache
->path
->len
+= cache
->path
->level
[n
] + 1;
1649 if (*p
++ == ':' && n
< 2 && p
!= last
) {
1653 goto invalid_levels
;
1656 goto invalid_levels
;
1659 if (cache
->path
->len
< 10 + 3) {
1665 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1666 "invalid \"levels\" \"%V\"", &value
[i
]);
1667 return NGX_CONF_ERROR
;
1670 if (ngx_strncmp(value
[i
].data
, "keys_zone=", 10) == 0) {
1672 name
.data
= value
[i
].data
+ 10;
1674 p
= (u_char
*) ngx_strchr(name
.data
, ':');
1677 name
.len
= p
- name
.data
;
1681 s
.len
= value
[i
].data
+ value
[i
].len
- p
;
1684 size
= ngx_parse_size(&s
);
1690 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1691 "invalid keys zone size \"%V\"", &value
[i
]);
1692 return NGX_CONF_ERROR
;
1695 if (ngx_strncmp(value
[i
].data
, "inactive=", 9) == 0) {
1697 s
.len
= value
[i
].len
- 9;
1698 s
.data
= value
[i
].data
+ 9;
1700 inactive
= ngx_parse_time(&s
, 1);
1701 if (inactive
== (time_t) NGX_ERROR
) {
1702 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1703 "invalid inactive value \"%V\"", &value
[i
]);
1704 return NGX_CONF_ERROR
;
1710 if (ngx_strncmp(value
[i
].data
, "max_size=", 9) == 0) {
1712 s
.len
= value
[i
].len
- 9;
1713 s
.data
= value
[i
].data
+ 9;
1715 max_size
= ngx_parse_offset(&s
);
1717 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1718 "invalid max_size value \"%V\"", &value
[i
]);
1719 return NGX_CONF_ERROR
;
1725 if (ngx_strncmp(value
[i
].data
, "loader_files=", 13) == 0) {
1727 loader_files
= ngx_atoi(value
[i
].data
+ 13, value
[i
].len
- 13);
1728 if (loader_files
== NGX_ERROR
) {
1729 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1730 "invalid loader_files value \"%V\"", &value
[i
]);
1731 return NGX_CONF_ERROR
;
1737 if (ngx_strncmp(value
[i
].data
, "loader_sleep=", 13) == 0) {
1739 s
.len
= value
[i
].len
- 13;
1740 s
.data
= value
[i
].data
+ 13;
1742 loader_sleep
= ngx_parse_time(&s
, 0);
1743 if (loader_sleep
== (ngx_msec_t
) NGX_ERROR
) {
1744 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1745 "invalid loader_sleep value \"%V\"", &value
[i
]);
1746 return NGX_CONF_ERROR
;
1752 if (ngx_strncmp(value
[i
].data
, "loader_threshold=", 17) == 0) {
1754 s
.len
= value
[i
].len
- 17;
1755 s
.data
= value
[i
].data
+ 17;
1757 loader_threshold
= ngx_parse_time(&s
, 0);
1758 if (loader_threshold
== (ngx_msec_t
) NGX_ERROR
) {
1759 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1760 "invalid loader_threshold value \"%V\"", &value
[i
]);
1761 return NGX_CONF_ERROR
;
1767 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1768 "invalid parameter \"%V\"", &value
[i
]);
1769 return NGX_CONF_ERROR
;
1772 if (name
.len
== 0 || size
== 0) {
1773 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1774 "\"%V\" must have \"keys_zone\" parameter",
1776 return NGX_CONF_ERROR
;
1779 cache
->path
->manager
= ngx_http_file_cache_manager
;
1780 cache
->path
->loader
= ngx_http_file_cache_loader
;
1781 cache
->path
->data
= cache
;
1782 cache
->path
->conf_file
= cf
->conf_file
->file
.name
.data
;
1783 cache
->path
->line
= cf
->conf_file
->line
;
1784 cache
->loader_files
= loader_files
;
1785 cache
->loader_sleep
= loader_sleep
;
1786 cache
->loader_threshold
= loader_threshold
;
1788 if (ngx_add_path(cf
, &cache
->path
) != NGX_OK
) {
1789 return NGX_CONF_ERROR
;
1792 cache
->shm_zone
= ngx_shared_memory_add(cf
, &name
, size
, cmd
->post
);
1793 if (cache
->shm_zone
== NULL
) {
1794 return NGX_CONF_ERROR
;
1797 if (cache
->shm_zone
->data
) {
1798 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1799 "duplicate zone \"%V\"", &name
);
1800 return NGX_CONF_ERROR
;
1804 cache
->shm_zone
->init
= ngx_http_file_cache_init
;
1805 cache
->shm_zone
->data
= cache
;
1807 cache
->inactive
= inactive
;
1808 cache
->max_size
= max_size
;
1815 ngx_http_file_cache_valid_set_slot(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
1822 ngx_uint_t i
, n
, status
;
1824 ngx_http_cache_valid_t
*v
;
1825 static ngx_uint_t statuses
[] = { 200, 301, 302 };
1827 a
= (ngx_array_t
**) (p
+ cmd
->offset
);
1829 if (*a
== NGX_CONF_UNSET_PTR
) {
1830 *a
= ngx_array_create(cf
->pool
, 1, sizeof(ngx_http_cache_valid_t
));
1832 return NGX_CONF_ERROR
;
1836 value
= cf
->args
->elts
;
1837 n
= cf
->args
->nelts
- 1;
1839 valid
= ngx_parse_time(&value
[n
], 1);
1840 if (valid
== (time_t) NGX_ERROR
) {
1841 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1842 "invalid time value \"%V\"", &value
[n
]);
1843 return NGX_CONF_ERROR
;
1848 for (i
= 0; i
< 3; i
++) {
1849 v
= ngx_array_push(*a
);
1851 return NGX_CONF_ERROR
;
1854 v
->status
= statuses
[i
];
1861 for (i
= 1; i
< n
; i
++) {
1863 if (ngx_strcmp(value
[i
].data
, "any") == 0) {
1869 status
= ngx_atoi(value
[i
].data
, value
[i
].len
);
1871 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
1872 "invalid status \"%V\"", &value
[i
]);
1873 return NGX_CONF_ERROR
;
1877 v
= ngx_array_push(*a
);
1879 return NGX_CONF_ERROR
;