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