changeset 525:43cc6f0b77ce NGINX_0_8_9

nginx 0.8.9 *) Feature: now the start cache loader runs in a separate process; this should improve large caches handling. *) Feature: now temporarily files and permanent storage area may reside at different file systems.
author Igor Sysoev <http://sysoev.ru>
date Mon, 17 Aug 2009 00:00:00 +0400
parents 16d97d9e72b7
children 607556aed0a1
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_file.c src/core/ngx_file.h src/http/modules/ngx_http_dav_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_cache.h src/http/ngx_http_file_cache.c src/http/ngx_http_upstream.c src/os/unix/ngx_errno.h src/os/unix/ngx_process.c src/os/unix/ngx_process.h src/os/unix/ngx_process_cycle.c src/os/unix/ngx_process_cycle.h
diffstat 16 files changed, 447 insertions(+), 262 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,13 @@
 
+Changes with nginx 0.8.9                                         17 Aug 2009
+
+    *) Feature: now the start cache loader runs in a separate process; this 
+       should improve large caches handling.
+
+    *) Feature: now temporarily files and permanent storage area may reside 
+       at different file systems.
+
+
 Changes with nginx 0.8.8                                         10 Aug 2009
 
     *) Bugfix: in handling FastCGI headers split in records.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,13 @@
 
+Изменения в nginx 0.8.9                                           17.08.2009
+
+    *) Добавление: теперь стартовый загрузчик кэша работает в отдельном 
+       процесс; это должно улучшить обработку больших кэшей.
+
+    *) Добавление: теперь временные файлы и постоянное место хранения могут 
+       располагаться на разных файловых системах.
+
+
 Изменения в nginx 0.8.8                                           10.08.2009
 
     *) Исправление: в обработке заголовков ответа, разделённых в 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8008
-#define NGINX_VERSION      "0.8.8"
+#define nginx_version         8009
+#define NGINX_VERSION      "0.8.9"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -99,13 +99,7 @@ ngx_create_temp_file(ngx_file_t *file, n
             continue;
         }
 
-        if ((path->level[0] == 0)
-            || (err != NGX_ENOENT
-#if (NGX_WIN32)
-                && err != NGX_ENOTDIR
-#endif
-            ))
-        {
+        if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {
             ngx_log_error(NGX_LOG_CRIT, file->log, err,
                           ngx_open_tempfile_n " \"%s\" failed",
                           file->name.data);
@@ -264,7 +258,8 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n
     }
 
     path->len = 0;
-    path->manager = (ngx_path_manager_pt) cmd->post;
+    path->manager = NULL;
+    path->loader = NULL;
     path->conf_file = cf->conf_file->file.name.data;
     path->line = cf->conf_file->line;
 
@@ -325,6 +320,7 @@ ngx_conf_merge_path_value(ngx_conf_t *cf
                    + init->level[2] + (init->level[2] ? 1 : 0);
 
     (*path)->manager = NULL;
+    (*path)->loader = NULL;
     (*path)->conf_file = NULL;
 
     if (ngx_add_path(cf, path) != NGX_OK) {
@@ -528,7 +524,9 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng
 ngx_int_t
 ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
 {
-    ngx_err_t  err;
+    u_char           *name;
+    ngx_err_t         err;
+    ngx_copy_file_t   cf;
 
 #if !(NGX_WIN32)
 
@@ -558,14 +556,8 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
 
     err = ngx_errno;
 
-    if (err
-#if (NGX_WIN32)
-            == ERROR_PATH_NOT_FOUND
-#else
-            == NGX_ENOENT
-#endif
-       )
-    {
+    if (err == NGX_ENOPATH) {
+
         if (!ext->create_path) {
             goto failed;
         }
@@ -605,6 +597,44 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
 
 #endif
 
+    if (err == NGX_EXDEV) {
+
+        cf.size = -1;
+        cf.buf_size = 0;
+        cf.access = ext->access;
+        cf.time = ext->time;
+        cf.log = ext->log;
+
+        name = ngx_alloc(to->len + 1 + 10, ext->log);
+        if (name == NULL) {
+            return NGX_ERROR;
+        }
+
+        (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len - 1, to->data,
+                           (uint32_t) ngx_next_temp_number(0));
+
+        if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {
+
+            if (ngx_rename_file(name, to->data) == NGX_FILE_ERROR) {
+                ngx_free(name);
+                goto failed;
+            }
+
+            ngx_free(name);
+
+            if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+                              ngx_delete_file_n " \"%s\" failed", src->data);
+
+                return NGX_ERROR;
+            }
+
+            return NGX_OK;
+        }
+
+        ngx_free(name);
+    }
+
 failed:
 
     if (ext->delete_file) {
@@ -614,15 +644,141 @@ failed:
         }
     }
 
-    if (err && ext->log_rename_error) {
+    if (err) {
         ngx_log_error(NGX_LOG_CRIT, ext->log, err,
                       ngx_rename_file_n " \"%s\" to \"%s\" failed",
                       src->data, to->data);
     }
 
-    ext->rename_error = err;
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)
+{
+    char             *buf;
+    off_t             size;
+    size_t            len;
+    ssize_t           n;
+    ngx_fd_t          fd, nfd;
+    ngx_int_t         rc;
+    ngx_file_info_t   fi;
+
+    rc = NGX_ERROR;
+    buf = NULL;
+    nfd = NGX_INVALID_FILE;
+
+    fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+    if (fd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", from);
+        goto failed;
+    }
+
+    if (cf->size != -1) {
+        size = cf->size;
+
+    } else {
+        if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_fd_info_n " \"%s\" failed", from);
+
+            goto failed;
+        }
+
+        size = ngx_file_size(&fi);
+    }
+
+    len = cf->buf_size ? cf->buf_size : 65536;
+
+    if ((off_t) len > size) {
+        len = (size_t) size;
+    }
+
+    buf = ngx_alloc(len, cf->log);
+    if (buf == NULL) {
+        goto failed;
+    }
+
+    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
+                        cf->access);
+
+    if (nfd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", to);
+        goto failed;
+    }
+
+    while (size > 0) {
 
-    return NGX_ERROR;
+        if ((off_t) len > size) {
+            len = (size_t) size;
+        }
+
+        n = ngx_read_fd(fd, buf, len);
+
+        if (n == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_read_fd_n " \"%s\" failed", from);
+            goto failed;
+        }
+
+        if ((size_t) n != len) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_read_fd_n " has read only %z of %uz from %s",
+                          n, size, from);
+            goto failed;
+        }
+
+        n = ngx_write_fd(nfd, buf, len);
+
+        if (n == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_write_fd_n " \"%s\" failed", to);
+            goto failed;
+        }
+
+        if ((size_t) n != len) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_write_fd_n " has written only %z of %uz to %s",
+                          n, size, to);
+            goto failed;
+        }
+
+        size -= n;
+    }
+
+    if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                      ngx_set_file_time_n " \"%s\" failed", to);
+        goto failed;
+    }
+
+    rc = NGX_OK;
+
+failed:
+
+    if (nfd != NGX_INVALID_FILE) {
+        if (ngx_close_file(nfd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", to);
+        }
+    }
+
+    if (fd != NGX_INVALID_FILE) {
+        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", from);
+        }
+    }
+
+    if (buf) {
+        ngx_free(buf);
+    }
+
+    return rc;
 }
 
 
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -30,6 +30,7 @@ struct ngx_file_s {
 
 
 typedef time_t (*ngx_path_manager_pt) (void *data);
+typedef void (*ngx_path_loader_pt) (void *data);
 
 
 typedef struct {
@@ -38,6 +39,7 @@ typedef struct {
     size_t                     level[3];
 
     ngx_path_manager_pt        manager;
+    ngx_path_loader_pt         loader;
     void                      *data;
 
     u_char                    *conf_file;
@@ -71,16 +73,25 @@ typedef struct {
     ngx_uint_t                 path_access;
     time_t                     time;
     ngx_fd_t                   fd;
-    ngx_err_t                  rename_error;
 
     unsigned                   create_path:1;
     unsigned                   delete_file:1;
-    unsigned                   log_rename_error:1;
 
     ngx_log_t                 *log;
 } ngx_ext_rename_file_t;
 
 
+typedef struct {
+    off_t                      size;
+    size_t                     buf_size;
+
+    ngx_uint_t                 access;
+    time_t                     time;
+
+    ngx_log_t                 *log;
+} ngx_copy_file_t;
+
+
 typedef struct ngx_tree_ctx_s  ngx_tree_ctx_t;
 
 typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev);
@@ -115,6 +126,7 @@ ngx_int_t ngx_add_path(ngx_conf_t *cf, n
 ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user);
 ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,
     ngx_ext_rename_file_t *ext);
+ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf);
 ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);
 
 void ngx_init_temp_number(void);
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -53,8 +53,6 @@ static ngx_int_t ngx_http_dav_copy_dir_t
     ngx_str_t *path);
 static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
     ngx_str_t *path);
-static ngx_int_t ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, u_char *from,
-    u_char *to);
 
 static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
 static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
@@ -249,7 +247,6 @@ ngx_http_dav_put_handler(ngx_http_reques
     ext.time = -1;
     ext.create_path = dlcf->create_full_put_path;
     ext.delete_file = 1;
-    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     if (r->headers_in.date) {
@@ -520,6 +517,7 @@ ngx_http_dav_copy_move_handler(ngx_http_
     ngx_uint_t                overwrite, slash, dir;
     ngx_str_t                 path, uri;
     ngx_tree_ctx_t            tree;
+    ngx_copy_file_t           cf;
     ngx_file_info_t           fi;
     ngx_table_elt_t          *dest, *over;
     ngx_ext_rename_file_t     ext;
@@ -791,43 +789,24 @@ overwrite_done:
             ext.time = -1;
             ext.create_path = 1;
             ext.delete_file = 0;
-            ext.log_rename_error = 0;
             ext.log = r->connection->log;
 
             if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
                 return NGX_HTTP_NO_CONTENT;
             }
 
-            if (ext.rename_error != NGX_EXDEV) {
-
-                if (ext.rename_error) {
-                    ngx_log_error(NGX_LOG_CRIT, r->connection->log,
-                                  ext.rename_error,
-                                  ngx_rename_file_n " \"%s\" to \"%s\" failed",
-                                  path.data, copy.path.data);
-                }
-
-                return NGX_HTTP_INTERNAL_SERVER_ERROR;
-            }
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
         dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
 
-        tree.size = ngx_file_size(&fi);
-        tree.mtime = ngx_file_mtime(&fi);
-        tree.access = dlcf->access;
-        tree.log = r->connection->log;
+        cf.size = ngx_file_size(&fi);
+        cf.buf_size = 0;
+        cf.access = dlcf->access;
+        cf.time = ngx_file_mtime(&fi);
+        cf.log = r->connection->log;
 
-        if (ngx_http_dav_copy_file(&tree, path.data, copy.path.data) == NGX_OK)
-        {
-            if (r->method == NGX_HTTP_MOVE) {
-                rc = ngx_http_dav_delete_path(r, &path, 0);
-
-                if (rc != NGX_OK) {
-                    return rc;
-                }
-            }
-
+        if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
             return NGX_HTTP_NO_CONTENT;
         }
     }
@@ -941,6 +920,7 @@ ngx_http_dav_copy_tree_file(ngx_tree_ctx
 {
     u_char                   *p, *file;
     size_t                    len;
+    ngx_copy_file_t           cf;
     ngx_http_dav_copy_ctx_t  *copy;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
@@ -961,7 +941,13 @@ ngx_http_dav_copy_tree_file(ngx_tree_ctx
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                    "http copy file to: \"%s\"", file);
 
-    (void) ngx_http_dav_copy_file(ctx, path->data, file);
+    cf.size = ctx->size;
+    cf.buf_size = 0;
+    cf.access = ctx->access;
+    cf.time = ctx->mtime;
+    cf.log = ctx->log;
+
+    (void) ngx_copy_file(path->data, file, &cf);
 
     ngx_free(file);
 
@@ -970,75 +956,6 @@ ngx_http_dav_copy_tree_file(ngx_tree_ctx
 
 
 static ngx_int_t
-ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, u_char *from, u_char *to)
-{
-    off_t       size;
-    ssize_t     n;
-    ngx_fd_t    fd, cfd;
-    ngx_int_t   rc;
-    u_char      buf[NGX_HTTP_DAV_COPY_BLOCK];
-
-    fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
-
-    if (fd == NGX_INVALID_FILE) {
-        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n,
-                                  from);
-        return NGX_ERROR;
-    }
-
-    cfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
-                        ctx->access);
-
-    rc = NGX_ERROR;
-
-    if (cfd == NGX_INVALID_FILE) {
-        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, to);
-        goto failed;
-    }
-
-    for (size = ctx->size; size > 0; size -= n) {
-
-        n = ngx_read_fd(fd, buf, NGX_HTTP_DAV_COPY_BLOCK);
-
-        if (n == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                          ngx_read_fd_n " \"%s\" failed", from);
-            goto failed;
-        }
-
-        if (ngx_write_fd(cfd, buf, n) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                          ngx_write_fd_n " \"%s\" failed", to);
-            goto failed;
-        }
-    }
-
-    if (ngx_set_file_time(to, cfd, ctx->mtime) != NGX_OK) {
-        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_set_file_time_n " \"%s\" failed", to);
-        goto failed;
-    }
-
-    if (ngx_close_file(cfd) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_close_file_n " \"%s\" failed", to);
-        goto failed;
-    }
-
-    rc = NGX_OK;
-
-failed:
-
-    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_close_file_n " \"%s\" failed", from);
-    }
-
-    return rc;
-}
-
-
-static ngx_int_t
 ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
 {
     ngx_table_elt_t  *depth;
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -1065,9 +1065,10 @@ ngx_http_fastcgi_reinit_request(ngx_http
 static ngx_int_t
 ngx_http_fastcgi_process_header(ngx_http_request_t *r)
 {
-    u_char                         *p, *start, *last, *part_start, *part_end;
+    u_char                         *p, *msg, *start, *last,
+                                   *part_start, *part_end;
     size_t                          size;
-    ngx_str_t                      *status_line, line, *pattern;
+    ngx_str_t                      *status_line, *pattern;
     ngx_int_t                       rc, status;
     ngx_buf_t                       buf;
     ngx_uint_t                      i;
@@ -1151,40 +1152,39 @@ ngx_http_fastcgi_process_header(ngx_http
         if (f->type == NGX_HTTP_FASTCGI_STDERR) {
 
             if (f->length) {
-                line.data = u->buffer.pos;
+                msg = u->buffer.pos;
 
                 if (u->buffer.pos + f->length <= u->buffer.last) {
-                    line.len = f->length;
                     u->buffer.pos += f->length;
                     f->length = 0;
                     f->state = ngx_http_fastcgi_st_padding;
 
                 } else {
-                    line.len = u->buffer.last - u->buffer.pos;
                     f->length -= u->buffer.last - u->buffer.pos;
                     u->buffer.pos = u->buffer.last;
                 }
 
-                while (line.data[line.len - 1] == LF
-                       || line.data[line.len - 1] == CR
-                       || line.data[line.len - 1] == '.'
-                       || line.data[line.len - 1] == ' ')
-                {
-                    line.len--;
+                for (p = u->buffer.pos - 1; msg < p; p--) {
+                    if (*p != LF && *p != CR && *p != '.' && *p != ' ') {
+                        break;
+                    }
                 }
 
+                p++;
+
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                              "FastCGI sent in stderr: \"%V\"", &line);
+                              "FastCGI sent in stderr: \"%*s\"", p - msg, msg);
 
                 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
                 if (flcf->catch_stderr) {
                     pattern = flcf->catch_stderr->elts;
 
-                    line.data[line.len - 1] = '\0';
-
                     for (i = 0; i < flcf->catch_stderr->nelts; i++) {
-                        if (ngx_strstr(line.data, pattern[i].data)) {
+                        if (ngx_strnstr(msg, (char *) pattern[i].data,
+                                        p - msg)
+                            != NULL)
+                        {
                             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
                         }
                     }
@@ -1452,9 +1452,9 @@ ngx_http_fastcgi_process_header(ngx_http
 static ngx_int_t
 ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
 {
+    u_char                  *m, *msg;
     ngx_int_t                rc;
     ngx_buf_t               *b, **prev;
-    ngx_str_t                line;
     ngx_chain_t             *cl;
     ngx_http_request_t      *r;
     ngx_http_fastcgi_ctx_t  *f;
@@ -1538,30 +1538,27 @@ ngx_http_fastcgi_input_filter(ngx_event_
                     break;
                 }
 
-                line.data = f->pos;
+                msg = f->pos;
 
                 if (f->pos + f->length <= f->last) {
-                    line.len = f->length;
                     f->pos += f->length;
                     f->length = 0;
                     f->state = ngx_http_fastcgi_st_padding;
 
                 } else {
-                    line.len = f->last - f->pos;
                     f->length -= f->last - f->pos;
                     f->pos = f->last;
                 }
 
-                while (line.data[line.len - 1] == LF
-                       || line.data[line.len - 1] == CR
-                       || line.data[line.len - 1] == '.'
-                       || line.data[line.len - 1] == ' ')
-                {
-                    line.len--;
+                for (m = f->pos - 1; msg < m; m--) {
+                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
+                        break;
+                    }
                 }
 
                 ngx_log_error(NGX_LOG_ERR, p->log, 0,
-                              "FastCGI sent in stderr: \"%V\"", &line);
+                              "FastCGI sent in stderr: \"%*s\"",
+                              m + 1 - msg, msg);
 
                 if (f->pos == f->last) {
                     break;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.8.8';
+our $VERSION = '0.8.9';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -97,6 +97,7 @@ typedef struct {
     ngx_rbtree_node_t                sentinel;
     ngx_queue_t                      queue;
     ngx_atomic_t                     cold;
+    ngx_atomic_t                     loading;
     off_t                            size;
 } ngx_http_file_cache_sh_t;
 
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -53,6 +53,7 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
     ngx_http_file_cache_t  *ocache = data;
 
     size_t                  len;
+    ngx_uint_t              n;
     ngx_http_file_cache_t  *cache;
 
     cache = shm_zone->data;
@@ -68,6 +69,15 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
             return NGX_ERROR;
         }
 
+        for (n = 0; n < 3; n++) {
+            if (cache->path->level[n] != ocache->path->level[n]) {
+                ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+                              "cache \"%V\" had previously different levels",
+                              &shm_zone->shm.name);
+                return NGX_ERROR;
+            }
+        }
+
         cache->sh = ocache->sh;
 
         cache->shpool = ocache->shpool;
@@ -75,6 +85,10 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
 
         cache->max_size /= cache->bsize;
 
+        if (!cache->sh->cold || cache->sh->loading) {
+            cache->path->loader = NULL;
+        }
+
         return NGX_OK;
     }
 
@@ -100,6 +114,7 @@ ngx_http_file_cache_init(ngx_shm_zone_t 
     ngx_queue_init(&cache->sh->queue);
 
     cache->sh->cold = 1;
+    cache->sh->loading = 0;
     cache->sh->size = 0;
 
     cache->bsize = ngx_fs_bsize(cache->path->name.data);
@@ -635,7 +650,6 @@ ngx_http_file_cache_update(ngx_http_requ
     ext.time = -1;
     ext.create_path = 1;
     ext.delete_file = 1;
-    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
@@ -1026,39 +1040,8 @@ ngx_http_file_cache_manager(void *data)
 {
     ngx_http_file_cache_t  *cache = data;
 
-    off_t           size;
-    time_t          next;
-    ngx_tree_ctx_t  tree;
-
-    if (cache->sh->cold) {
-
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
-                       "http file cache manager update");
-
-        tree.init_handler = NULL;
-        tree.file_handler = ngx_http_file_cache_manage_file;
-        tree.pre_tree_handler = ngx_http_file_cache_noop;
-        tree.post_tree_handler = ngx_http_file_cache_noop;
-        tree.spec_handler = ngx_http_file_cache_delete_file;
-        tree.data = cache;
-        tree.alloc = 0;
-        tree.log = ngx_cycle->log;
-
-        cache->last = ngx_current_msec;
-        cache->files = 0;
-
-        if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
-            return 10;
-        }
-
-        cache->sh->cold = 0;
-
-        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
-                      "http file cache: %V %.3fM, bsize: %uz",
-                      &cache->path->name,
-                      ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
-                      cache->bsize);
-    }
+    off_t   size;
+    time_t  next;
 
     next = ngx_http_file_cache_expire(cache);
 
@@ -1088,6 +1071,52 @@ ngx_http_file_cache_manager(void *data)
 }
 
 
+static void
+ngx_http_file_cache_loader(void *data)
+{
+    ngx_http_file_cache_t  *cache = data;
+
+    ngx_tree_ctx_t  tree;
+
+    if (!cache->sh->cold || cache->sh->loading) {
+        return;
+    }
+
+    if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+                   "http file cache loader");
+
+    tree.init_handler = NULL;
+    tree.file_handler = ngx_http_file_cache_manage_file;
+    tree.pre_tree_handler = ngx_http_file_cache_noop;
+    tree.post_tree_handler = ngx_http_file_cache_noop;
+    tree.spec_handler = ngx_http_file_cache_delete_file;
+    tree.data = cache;
+    tree.alloc = 0;
+    tree.log = ngx_cycle->log;
+
+    cache->last = ngx_current_msec;
+    cache->files = 0;
+
+    if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
+        cache->sh->loading = 0;
+        return;
+    }
+
+    cache->sh->cold = 0;
+    cache->sh->loading = 0;
+
+    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                  "http file cache: %V %.3fM, bsize: %uz",
+                  &cache->path->name,
+                  ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
+                  cache->bsize);
+}
+
+
 static ngx_int_t
 ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
 {
@@ -1468,6 +1497,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
     }
 
     cache->path->manager = ngx_http_file_cache_manager;
+    cache->path->loader = ngx_http_file_cache_loader;
     cache->path->data = cache;
 
     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -2635,7 +2635,6 @@ ngx_http_upstream_store(ngx_http_request
     ext.time = -1;
     ext.create_path = 1;
     ext.delete_file = 1;
-    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     if (u->headers_in.last_modified) {
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -16,6 +16,7 @@ typedef int               ngx_err_t;
 
 #define NGX_EPERM         EPERM
 #define NGX_ENOENT        ENOENT
+#define NGX_ENOPATH       ENOENT
 #define NGX_ESRCH         ESRCH
 #define NGX_EINTR         EINTR
 #define NGX_ECHILD        ECHILD
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -214,21 +214,33 @@ ngx_spawn_process(ngx_cycle_t *cycle, ng
 
     switch (respawn) {
 
+    case NGX_PROCESS_NORESPAWN:
+        ngx_processes[s].respawn = 0;
+        ngx_processes[s].just_spawn = 0;
+        ngx_processes[s].detached = 0;
+        break;
+
+    case NGX_PROCESS_JUST_SPAWN:
+        ngx_processes[s].respawn = 0;
+        ngx_processes[s].just_spawn = 1;
+        ngx_processes[s].detached = 0;
+        break;
+
     case NGX_PROCESS_RESPAWN:
         ngx_processes[s].respawn = 1;
-        ngx_processes[s].just_respawn = 0;
+        ngx_processes[s].just_spawn = 0;
         ngx_processes[s].detached = 0;
         break;
 
     case NGX_PROCESS_JUST_RESPAWN:
         ngx_processes[s].respawn = 1;
-        ngx_processes[s].just_respawn = 1;
+        ngx_processes[s].just_spawn = 1;
         ngx_processes[s].detached = 0;
         break;
 
     case NGX_PROCESS_DETACHED:
         ngx_processes[s].respawn = 0;
-        ngx_processes[s].just_respawn = 0;
+        ngx_processes[s].just_spawn = 0;
         ngx_processes[s].detached = 1;
         break;
     }
--- a/src/os/unix/ngx_process.h
+++ b/src/os/unix/ngx_process.h
@@ -27,7 +27,7 @@ typedef struct {
     char               *name;
 
     unsigned            respawn:1;
-    unsigned            just_respawn:1;
+    unsigned            just_spawn:1;
     unsigned            detached:1;
     unsigned            exiting:1;
     unsigned            exited:1;
@@ -45,9 +45,10 @@ typedef struct {
 #define NGX_MAX_PROCESSES         1024
 
 #define NGX_PROCESS_NORESPAWN     -1
-#define NGX_PROCESS_RESPAWN       -2
-#define NGX_PROCESS_JUST_RESPAWN  -3
-#define NGX_PROCESS_DETACHED      -4
+#define NGX_PROCESS_JUST_SPAWN    -2
+#define NGX_PROCESS_RESPAWN       -3
+#define NGX_PROCESS_JUST_RESPAWN  -4
+#define NGX_PROCESS_DETACHED      -5
 
 
 #define ngx_getpid   getpid
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -12,7 +12,9 @@
 
 static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,
     ngx_int_t type);
-static void ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type);
+static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle,
+    ngx_uint_t respawn);
+static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch);
 static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo);
 static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle);
 static void ngx_master_process_exit(ngx_cycle_t *cycle);
@@ -26,6 +28,7 @@ static ngx_thread_value_t ngx_worker_thr
 #endif
 static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);
 static void ngx_cache_manager_process_handler(ngx_event_t *ev);
+static void ngx_cache_loader_process_handler(ngx_event_t *ev);
 
 
 ngx_uint_t    ngx_process;
@@ -61,6 +64,15 @@ u_long         cpu_affinity;
 static u_char  master_process[] = "master process";
 
 
+static ngx_cache_manager_ctx_t  ngx_cache_manager_ctx = {
+    ngx_cache_manager_process_handler, "cache manager process", 0
+};
+
+static ngx_cache_manager_ctx_t  ngx_cache_loader_ctx = {
+    ngx_cache_loader_process_handler, "cache loader process", 60000
+};
+
+
 static ngx_cycle_t      ngx_exit_cycle;
 static ngx_log_t        ngx_exit_log;
 static ngx_open_file_t  ngx_exit_log_file;
@@ -122,7 +134,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
 
     ngx_start_worker_processes(cycle, ccf->worker_processes,
                                NGX_PROCESS_RESPAWN);
-    ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
+    ngx_start_cache_manager_processes(cycle, 0);
 
     ngx_new_binary = 0;
     delay = 0;
@@ -203,7 +215,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             if (ngx_new_binary) {
                 ngx_start_worker_processes(cycle, ccf->worker_processes,
                                            NGX_PROCESS_RESPAWN);
-                ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
+                ngx_start_cache_manager_processes(cycle, 0);
                 ngx_noaccepting = 0;
 
                 continue;
@@ -222,7 +234,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
                                                    ngx_core_module);
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_JUST_RESPAWN);
-            ngx_start_cache_manager_process(cycle, NGX_PROCESS_JUST_RESPAWN);
+            ngx_start_cache_manager_processes(cycle, 1);
             live = 1;
             ngx_signal_worker_processes(cycle,
                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
@@ -232,7 +244,7 @@ ngx_master_process_cycle(ngx_cycle_t *cy
             ngx_restart = 0;
             ngx_start_worker_processes(cycle, ccf->worker_processes,
                                        NGX_PROCESS_RESPAWN);
-            ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
+            ngx_start_cache_manager_processes(cycle, 0);
             live = 1;
         }
 
@@ -317,7 +329,7 @@ ngx_single_process_cycle(ngx_cycle_t *cy
 static void
 ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
 {
-    ngx_int_t      i, s;
+    ngx_int_t      i;
     ngx_channel_t  ch;
 
     ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
@@ -335,58 +347,70 @@ ngx_start_worker_processes(ngx_cycle_t *
         ch.slot = ngx_process_slot;
         ch.fd = ngx_processes[ngx_process_slot].channel[0];
 
-        for (s = 0; s < ngx_last_process; s++) {
-
-            if (s == ngx_process_slot
-                || ngx_processes[s].pid == -1
-                || ngx_processes[s].channel[0] == -1)
-            {
-                continue;
-            }
-
-            ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
-                          "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
-                          ch.slot, ch.pid, ch.fd,
-                          s, ngx_processes[s].pid,
-                          ngx_processes[s].channel[0]);
-
-            /* TODO: NGX_AGAIN */
-
-            ngx_write_channel(ngx_processes[s].channel[0],
-                              &ch, sizeof(ngx_channel_t), cycle->log);
-        }
+        ngx_pass_open_channel(cycle, &ch);
     }
 }
 
 
 static void
-ngx_start_cache_manager_process(ngx_cycle_t *cycle, ngx_int_t type)
+ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn)
 {
-    ngx_int_t        i;
-    ngx_uint_t       n;
+    ngx_uint_t       i, manager, loader;
     ngx_path_t     **path;
     ngx_channel_t    ch;
 
+    manager = 0;
+    loader = 0;
+
     path = ngx_cycle->pathes.elts;
-    for (n = 0; n < ngx_cycle->pathes.nelts; n++) {
-        if (path[n]->manager) {
-            goto start;
+    for (i = 0; i < ngx_cycle->pathes.nelts; i++) {
+
+        if (path[i]->manager) {
+            manager = 1;
+        }
+
+        if (path[i]->loader) {
+            loader = 1;
         }
     }
 
-    return;
+    if (manager == 0) {
+        return;
+    }
 
-start:
+    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
+                      &ngx_cache_manager_ctx, "cache manager process",
+                      respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);
 
     ch.command = NGX_CMD_OPEN_CHANNEL;
-
-    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, NULL,
-                      "cache manager process", type);
-
     ch.pid = ngx_processes[ngx_process_slot].pid;
     ch.slot = ngx_process_slot;
     ch.fd = ngx_processes[ngx_process_slot].channel[0];
 
+    ngx_pass_open_channel(cycle, &ch);
+
+    if (loader == 0) {
+        return;
+    }
+
+    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
+                      &ngx_cache_loader_ctx, "cache loader process",
+                      respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);
+
+    ch.command = NGX_CMD_OPEN_CHANNEL;
+    ch.pid = ngx_processes[ngx_process_slot].pid;
+    ch.slot = ngx_process_slot;
+    ch.fd = ngx_processes[ngx_process_slot].channel[0];
+
+    ngx_pass_open_channel(cycle, &ch);
+}
+
+
+static void
+ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)
+{
+    ngx_int_t  i;
+
     for (i = 0; i < ngx_last_process; i++) {
 
         if (i == ngx_process_slot
@@ -398,14 +422,14 @@ start:
 
         ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                       "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
-                      ch.slot, ch.pid, ch.fd,
+                      ch->slot, ch->pid, ch->fd,
                       i, ngx_processes[i].pid,
                       ngx_processes[i].channel[0]);
 
         /* TODO: NGX_AGAIN */
 
         ngx_write_channel(ngx_processes[i].channel[0],
-                          &ch, sizeof(ngx_channel_t), cycle->log);
+                          ch, sizeof(ngx_channel_t), cycle->log);
     }
 }
 
@@ -456,14 +480,14 @@ ngx_signal_worker_processes(ngx_cycle_t 
                        ngx_processes[i].exited,
                        ngx_processes[i].detached,
                        ngx_processes[i].respawn,
-                       ngx_processes[i].just_respawn);
+                       ngx_processes[i].just_spawn);
 
         if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {
             continue;
         }
 
-        if (ngx_processes[i].just_respawn) {
-            ngx_processes[i].just_respawn = 0;
+        if (ngx_processes[i].just_spawn) {
+            ngx_processes[i].just_spawn = 0;
             continue;
         }
 
@@ -533,7 +557,7 @@ ngx_reap_children(ngx_cycle_t *cycle)
                        ngx_processes[i].exited,
                        ngx_processes[i].detached,
                        ngx_processes[i].respawn,
-                       ngx_processes[i].just_respawn);
+                       ngx_processes[i].just_spawn);
 
         if (ngx_processes[i].pid == -1) {
             continue;
@@ -590,26 +614,7 @@ ngx_reap_children(ngx_cycle_t *cycle)
                 ch.slot = ngx_process_slot;
                 ch.fd = ngx_processes[ngx_process_slot].channel[0];
 
-                for (n = 0; n < ngx_last_process; n++) {
-
-                    if (n == ngx_process_slot
-                        || ngx_processes[n].pid == -1
-                        || ngx_processes[n].channel[0] == -1)
-                    {
-                        continue;
-                    }
-
-                    ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
-                          "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
-                          ch.slot, ch.pid, ch.fd,
-                          n, ngx_processes[n].pid,
-                          ngx_processes[n].channel[0]);
-
-                    /* TODO: NGX_AGAIN */
-
-                    ngx_write_channel(ngx_processes[n].channel[0],
-                                      &ch, sizeof(ngx_channel_t), cycle->log);
-                }
+                ngx_pass_open_channel(cycle, &ch);
 
                 live = 1;
 
@@ -1012,13 +1017,14 @@ ngx_worker_process_exit(ngx_cycle_t *cyc
                 && !c[i].read->resolver)
             {
                 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
-                              "open socket #%d left in connection %ui%s",
-                              c[i].fd, i, ngx_debug_quit ? ", aborting" : "");
-                ngx_debug_point();
+                              "open socket #%d left in connection %ui",
+                              c[i].fd, i);
+                ngx_debug_quit = 1;
             }
         }
 
         if (ngx_debug_quit) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "aborting");
             ngx_debug_point();
         }
     }
@@ -1265,6 +1271,8 @@ ngx_worker_thread_cycle(void *data)
 static void
 ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
 {
+    ngx_cache_manager_ctx_t *ctx = data;
+
     void         *ident[4];
     ngx_event_t   ev;
 
@@ -1275,16 +1283,16 @@ ngx_cache_manager_process_cycle(ngx_cycl
     ngx_close_listening_sockets(cycle);
 
     ngx_memzero(&ev, sizeof(ngx_event_t));
-    ev.handler = ngx_cache_manager_process_handler;
+    ev.handler = ctx->handler;
     ev.data = ident;
     ev.log = cycle->log;
     ident[3] = (void *) -1;
 
     ngx_use_accept_mutex = 0;
 
-    ngx_setproctitle("cache manager process");
+    ngx_setproctitle(ctx->name);
 
-    ngx_add_timer(&ev, 0);
+    ngx_add_timer(&ev, ctx->delay);
 
     for ( ;; ) {
 
@@ -1331,3 +1339,29 @@ ngx_cache_manager_process_handler(ngx_ev
 
     ngx_add_timer(ev, next * 1000);
 }
+
+
+static void
+ngx_cache_loader_process_handler(ngx_event_t *ev)
+{
+    ngx_uint_t     i;
+    ngx_path_t   **path;
+    ngx_cycle_t   *cycle;
+
+    cycle = (ngx_cycle_t *) ngx_cycle;
+
+    path = cycle->pathes.elts;
+    for (i = 0; i < cycle->pathes.nelts; i++) {
+
+        if (ngx_terminate || ngx_quit) {
+            break;
+        }
+
+        if (path[i]->loader) {
+            path[i]->loader(path[i]->data);
+            ngx_time_update(0, 0);
+        }
+    }
+
+    exit(0);
+}
--- a/src/os/unix/ngx_process_cycle.h
+++ b/src/os/unix/ngx_process_cycle.h
@@ -25,6 +25,13 @@
 #define NGX_PROCESS_SIGNALLER  3
 
 
+typedef struct {
+    ngx_event_handler_pt       handler;
+    char                      *name;
+    ngx_msec_t                 delay;
+} ngx_cache_manager_ctx_t;
+
+
 void ngx_master_process_cycle(ngx_cycle_t *cycle);
 void ngx_single_process_cycle(ngx_cycle_t *cycle);