ngx_ext_rename_file: remove the target file if ngx_copy_file() fails
Maxim Dounin
mdounin at mdounin.ru
Mon Aug 3 09:52:05 UTC 2015
Hello!
On Thu, Jul 09, 2015 at 02:10:48PM +0100, Mindaugas Rasiukevicius wrote:
> Hi,
>
> Some background: nginx 1.9.2, used as a cache, can get into the state
> when it stops evicting the objects and eventually stops caching without
> being able to recover. This happens when the disk is full. Consider the
> following nginx.conf fragment:
>
> proxy_cache_path /cache/nginx levels=1:2
> keys_zone=c3:4096m max_size=8500g
> inactive=30d use_temp_path=on;
> proxy_temp_path /cache/nginx-tmp 1 2;
>
> The disk is filled because the workers have been fetching the data from
> the backend faster than the cache manager is able to evict:
>
> $ df -h | grep cache
> /dev/sdb1 8.7T 8.7T 16M 100% /cache
> tmpfs 2.0G 0 2.0G 0% /cache/nginx-tmp
>
> Since /cache and /cache/nginx-tmp are separate mount points, nginx has to
> perform copy instead of rename. The copy functions fails due to ENOSPC,
> but the ngx_ext_rename_file() does not clean up the failed target. At this
> point, based on ngx_http_file_cache_sh_t::size, the cache manager believes
> that the 8.5 TB threshold has not been crossed and nginx fails to recover.
>
> Please find the patch attached.
>
> --
> Mindaugas
> # HG changeset patch
> # User Mindaugas Rasiukevicius <rmind at netbsd.org>
> # Date 1436387157 -3600
> # Wed Jul 08 21:25:57 2015 +0100
> # Node ID 9c978e51bf0e9bd6fc0fc04f4016a3c5701e9912
> # Parent dcae651b2a0cbd3de2f1fd5cf5b8c72627db94fd
> ngx_ext_rename_file: remove the target file if ngx_copy_file() fails.
>
> diff -r dcae651b2a0c -r 9c978e51bf0e src/core/ngx_file.c
> --- a/src/core/ngx_file.c Tue Jul 07 16:38:49 2015 +0300
> +++ b/src/core/ngx_file.c Wed Jul 08 21:25:57 2015 +0100
> @@ -727,12 +727,11 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
> ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
> ngx_rename_file_n " \"%s\" to \"%s\" failed",
> name, to->data);
> + }
>
> - if (ngx_delete_file(name) == NGX_FILE_ERROR) {
> - ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
> - ngx_delete_file_n " \"%s\" failed", name);
> -
> - }
> + if (ngx_delete_file(name) == NGX_FILE_ERROR) {
> + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
> + ngx_delete_file_n " \"%s\" failed", name);
> }
>
> ngx_free(name);
By calling ngx_delete_file() at this point, you do this for all
errors returned by ngx_copy_file(), including cases when it wasn't
able to open a destination file for some reason. This will result
in additional confusing errors, and looks like a wrong approach.
If you want nginx to remove a destination file, this should be
done in ngx_copy_file().
--
Maxim Dounin
http://nginx.org/
More information about the nginx-devel
mailing list