Mercurial > hg > nginx-quic
comparison src/http/ngx_http_file_cache.c @ 4385:70ba81827472
Cache locks initial implementation.
New directives: proxy_cache_lock on/off, proxy_cache_lock_timeout. With
proxy_cache_lock set to on, only one request will be allowed to go to
upstream for a particular cache item. Others will wait for a response
to appear in cache (or cache lock released) up to proxy_cache_lock_timeout.
Waiting requests will recheck if they have cached response ready (or are
allowed to run) every 500ms.
Note: we intentionally don't intercept NGX_DECLINED possibly returned by
ngx_http_file_cache_read(). This needs more work (possibly safe, but needs
further investigation). Anyway, it's exceptional situation.
Note: probably there should be a way to disable caching of responses
if there is already one request fetching resource to cache (without waiting
at all). Two possible ways include another cache lock option ("no_cache")
or using proxy_no_cache with some supplied variable.
Note: probably there should be a way to lock updating requests as well. For
now "proxy_cache_use_stale updating" is available.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 26 Dec 2011 11:15:23 +0000 |
parents | 4533d7684e14 |
children | e8181eeddaf8 |
comparison
equal
deleted
inserted
replaced
4384:a8b6d5dee539 | 4385:70ba81827472 |
---|---|
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_http.h> | 9 #include <ngx_http.h> |
10 #include <ngx_md5.h> | 10 #include <ngx_md5.h> |
11 | 11 |
12 | 12 |
13 static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r, | |
14 ngx_http_cache_t *c); | |
15 static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev); | |
13 static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r, | 16 static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r, |
14 ngx_http_cache_t *c); | 17 ngx_http_cache_t *c); |
15 static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r, | 18 static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r, |
16 ngx_http_cache_t *c); | 19 ngx_http_cache_t *c); |
17 #if (NGX_HAVE_FILE_AIO) | 20 #if (NGX_HAVE_FILE_AIO) |
179 cln = ngx_pool_cleanup_add(r->pool, 0); | 182 cln = ngx_pool_cleanup_add(r->pool, 0); |
180 if (cln == NULL) { | 183 if (cln == NULL) { |
181 return NGX_ERROR; | 184 return NGX_ERROR; |
182 } | 185 } |
183 | 186 |
187 cln->handler = ngx_http_file_cache_cleanup; | |
188 cln->data = c; | |
189 | |
184 if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) { | 190 if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) { |
185 return NGX_ERROR; | 191 return NGX_ERROR; |
186 } | 192 } |
187 | |
188 cln->handler = ngx_http_file_cache_cleanup; | |
189 cln->data = c; | |
190 | 193 |
191 if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { | 194 if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { |
192 return NGX_ERROR; | 195 return NGX_ERROR; |
193 } | 196 } |
194 | 197 |
242 ngx_http_file_cache_t *cache; | 245 ngx_http_file_cache_t *cache; |
243 ngx_http_core_loc_conf_t *clcf; | 246 ngx_http_core_loc_conf_t *clcf; |
244 | 247 |
245 c = r->cache; | 248 c = r->cache; |
246 | 249 |
250 if (c->waiting) { | |
251 return NGX_AGAIN; | |
252 } | |
253 | |
247 if (c->buf) { | 254 if (c->buf) { |
248 return ngx_http_file_cache_read(r, c); | 255 return ngx_http_file_cache_read(r, c); |
249 } | 256 } |
250 | 257 |
251 cache = c->file_cache; | 258 cache = c->file_cache; |
252 | 259 |
253 cln = ngx_pool_cleanup_add(r->pool, 0); | 260 if (c->node == NULL) { |
254 if (cln == NULL) { | 261 cln = ngx_pool_cleanup_add(r->pool, 0); |
255 return NGX_ERROR; | 262 if (cln == NULL) { |
263 return NGX_ERROR; | |
264 } | |
265 | |
266 cln->handler = ngx_http_file_cache_cleanup; | |
267 cln->data = c; | |
256 } | 268 } |
257 | 269 |
258 rc = ngx_http_file_cache_exists(cache, c); | 270 rc = ngx_http_file_cache_exists(cache, c); |
259 | 271 |
260 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 272 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
261 "http file cache exists: %i e:%d", rc, c->exists); | 273 "http file cache exists: %i e:%d", rc, c->exists); |
262 | 274 |
263 if (rc == NGX_ERROR) { | 275 if (rc == NGX_ERROR) { |
264 return rc; | 276 return rc; |
265 } | 277 } |
266 | |
267 cln->handler = ngx_http_file_cache_cleanup; | |
268 cln->data = c; | |
269 | 278 |
270 if (rc == NGX_AGAIN) { | 279 if (rc == NGX_AGAIN) { |
271 return NGX_HTTP_CACHE_SCARCE; | 280 return NGX_HTTP_CACHE_SCARCE; |
272 } | 281 } |
273 | 282 |
304 if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { | 313 if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { |
305 return NGX_ERROR; | 314 return NGX_ERROR; |
306 } | 315 } |
307 | 316 |
308 if (!test) { | 317 if (!test) { |
309 return NGX_DECLINED; | 318 goto done; |
310 } | 319 } |
311 | 320 |
312 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | 321 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
313 | 322 |
314 ngx_memzero(&of, sizeof(ngx_open_file_info_t)); | 323 ngx_memzero(&of, sizeof(ngx_open_file_info_t)); |
328 case 0: | 337 case 0: |
329 return NGX_ERROR; | 338 return NGX_ERROR; |
330 | 339 |
331 case NGX_ENOENT: | 340 case NGX_ENOENT: |
332 case NGX_ENOTDIR: | 341 case NGX_ENOTDIR: |
333 return rv; | 342 goto done; |
334 | 343 |
335 default: | 344 default: |
336 ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, | 345 ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, |
337 ngx_open_file_n " \"%s\" failed", c->file.name.data); | 346 ngx_open_file_n " \"%s\" failed", c->file.name.data); |
338 return NGX_ERROR; | 347 return NGX_ERROR; |
352 if (c->buf == NULL) { | 361 if (c->buf == NULL) { |
353 return NGX_ERROR; | 362 return NGX_ERROR; |
354 } | 363 } |
355 | 364 |
356 return ngx_http_file_cache_read(r, c); | 365 return ngx_http_file_cache_read(r, c); |
366 | |
367 done: | |
368 | |
369 if (rv == NGX_DECLINED) { | |
370 return ngx_http_file_cache_lock(r, c); | |
371 } | |
372 | |
373 return rv; | |
374 } | |
375 | |
376 | |
377 static ngx_int_t | |
378 ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c) | |
379 { | |
380 ngx_msec_t now, timer; | |
381 ngx_http_file_cache_t *cache; | |
382 | |
383 if (!c->lock) { | |
384 return NGX_DECLINED; | |
385 } | |
386 | |
387 cache = c->file_cache; | |
388 | |
389 ngx_shmtx_lock(&cache->shpool->mutex); | |
390 | |
391 if (!c->node->updating) { | |
392 c->node->updating = 1; | |
393 c->updating = 1; | |
394 } | |
395 | |
396 ngx_shmtx_unlock(&cache->shpool->mutex); | |
397 | |
398 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
399 "http file cache lock u:%d wt:%M", | |
400 c->updating, c->wait_time); | |
401 | |
402 if (c->updating) { | |
403 return NGX_DECLINED; | |
404 } | |
405 | |
406 c->waiting = 1; | |
407 | |
408 now = ngx_current_msec; | |
409 | |
410 if (c->wait_time == 0) { | |
411 c->wait_time = now + c->lock_timeout; | |
412 | |
413 c->wait_event.handler = ngx_http_file_cache_lock_wait_handler; | |
414 c->wait_event.data = r; | |
415 c->wait_event.log = r->connection->log; | |
416 } | |
417 | |
418 timer = c->wait_time - now; | |
419 | |
420 ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer); | |
421 | |
422 r->main->blocked++; | |
423 | |
424 return NGX_AGAIN; | |
425 } | |
426 | |
427 | |
428 static void | |
429 ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev) | |
430 { | |
431 ngx_uint_t wait; | |
432 ngx_msec_t timer; | |
433 ngx_http_cache_t *c; | |
434 ngx_http_request_t *r; | |
435 ngx_http_file_cache_t *cache; | |
436 | |
437 r = ev->data; | |
438 c = r->cache; | |
439 | |
440 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, | |
441 "http file cache wait handler wt:%M cur:%M", | |
442 c->wait_time, ngx_current_msec); | |
443 | |
444 timer = c->wait_time - ngx_current_msec; | |
445 | |
446 if ((ngx_msec_int_t) timer <= 0) { | |
447 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, | |
448 "http file cache lock timeout"); | |
449 c->lock = 0; | |
450 goto wakeup; | |
451 } | |
452 | |
453 cache = c->file_cache; | |
454 wait = 0; | |
455 | |
456 ngx_shmtx_lock(&cache->shpool->mutex); | |
457 | |
458 if (c->node->updating) { | |
459 wait = 1; | |
460 } | |
461 | |
462 ngx_shmtx_unlock(&cache->shpool->mutex); | |
463 | |
464 if (wait) { | |
465 ngx_add_timer(ev, (timer > 500) ? 500 : timer); | |
466 return; | |
467 } | |
468 | |
469 wakeup: | |
470 | |
471 c->waiting = 0; | |
472 r->main->blocked--; | |
473 r->connection->write->handler(r->connection->write); | |
357 } | 474 } |
358 | 475 |
359 | 476 |
360 static ngx_int_t | 477 static ngx_int_t |
361 ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c) | 478 ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c) |
516 ngx_int_t rc; | 633 ngx_int_t rc; |
517 ngx_http_file_cache_node_t *fcn; | 634 ngx_http_file_cache_node_t *fcn; |
518 | 635 |
519 ngx_shmtx_lock(&cache->shpool->mutex); | 636 ngx_shmtx_lock(&cache->shpool->mutex); |
520 | 637 |
521 fcn = ngx_http_file_cache_lookup(cache, c->key); | 638 fcn = c->node; |
639 | |
640 if (fcn == NULL) { | |
641 fcn = ngx_http_file_cache_lookup(cache, c->key); | |
642 } | |
522 | 643 |
523 if (fcn) { | 644 if (fcn) { |
524 ngx_queue_remove(&fcn->queue); | 645 ngx_queue_remove(&fcn->queue); |
525 | 646 |
526 fcn->uses++; | 647 if (c->node == NULL) { |
527 fcn->count++; | 648 fcn->uses++; |
649 fcn->count++; | |
650 } | |
528 | 651 |
529 if (fcn->error) { | 652 if (fcn->error) { |
530 | 653 |
531 if (fcn->valid_sec < ngx_time()) { | 654 if (fcn->valid_sec < ngx_time()) { |
532 goto renew; | 655 goto renew; |
619 u_char *p; | 742 u_char *p; |
620 ngx_http_cache_t *c; | 743 ngx_http_cache_t *c; |
621 | 744 |
622 c = r->cache; | 745 c = r->cache; |
623 | 746 |
747 if (c->file.name.len) { | |
748 return NGX_OK; | |
749 } | |
750 | |
624 c->file.name.len = path->name.len + 1 + path->len | 751 c->file.name.len = path->name.len + 1 + path->len |
625 + 2 * NGX_HTTP_CACHE_KEY_LEN; | 752 + 2 * NGX_HTTP_CACHE_KEY_LEN; |
626 | 753 |
627 c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1); | 754 c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1); |
628 if (c->file.name.data == NULL) { | 755 if (c->file.name.data == NULL) { |
954 ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno, | 1081 ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno, |
955 ngx_delete_file_n " \"%s\" failed", | 1082 ngx_delete_file_n " \"%s\" failed", |
956 tf->file.name.data); | 1083 tf->file.name.data); |
957 } | 1084 } |
958 } | 1085 } |
1086 } | |
1087 | |
1088 if (c->wait_event.timer_set) { | |
1089 ngx_del_timer(&c->wait_event); | |
959 } | 1090 } |
960 } | 1091 } |
961 | 1092 |
962 | 1093 |
963 static void | 1094 static void |