Назад | Перейти на главную страницу

Это состояние гонки nginx / WebDAV и как его обойти?

У меня есть сервер nginx 1.10.3, работающий на Debian Stretch. Один из обслуживаемых сайтов - это общий ресурс WebDAV, который читается и записывается настольным приложением. Приложение выполняет следующие шаги, когда файл с именем myfile сохраняется на сервере WebDAV:

  1. DELETE /myfile.tmp
  2. PUT /myfile.tmp, тело содержит новые данные файла
  3. DELETE /myfile
  4. MOVE /myfile.tmp, Destination: http://webdav.example.com/myfile
  5. GET /myfile

Клиентское приложение сравнивает ответ с шага 5 с данными, отправленными на шаге 2, и, если данные файла не совпадают, возникает ошибка. Эти шаги выполняются очень быстро в нашей конкретной сети (сервер и клиент географически близки, подключены к одному коммутатору Ethernet) - мое тестирование с помощью tcpdump показывает, что весь диалог завершается в течение 45 мс.

Проблема в том, что данные, возвращенные на шаге 5, не немедленно соответствует тому, что клиент отправил на шаге 2. Возвращаемые данные являются предыдущей версией myfile, перед DELETE/MOVE шаги заменили его. Если бы я вернулся и повторил шаг 5 вручную через мгновение, данные файла были бы новой версией, как и ожидалось.

Я знаю, что клиент ожидает получения каждого ответа, прежде чем отправить следующий запрос. Мое лучшее предположение состоит в том, что разные запросы попадают в разные рабочие / потоки nginx, или, может быть, есть какая-то недействительность кеша или сброс, который происходит недостаточно быстро.

Как я могу исправить это поведение, не изменяя клиентское приложение или искусственно не замедляя запросы?

Полная конфигурация nginx.conf и сайта:

pid /run/nginx.pid;
user www-data;
worker_processes auto;
worker_rlimit_nofile 20000;

events {
    multi_accept on;
    use epoll;
    worker_connections 4000;
}

http {
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    sendfile on;
    server_tokens off;
    tcp_nodelay on;
    tcp_nopush on;
    keepalive_requests 100000;
    keepalive_timeout 65;
    client_body_timeout 10;
    send_timeout 10;
    reset_timedout_connection on;
    types_hash_max_size 2048;

    open_file_cache max=200000 inactive=20s;
    open_file_cache_errors on;
    open_file_cache_min_uses 2;
    open_file_cache_valid 30s;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    gzip on;
    gzip_buffers 16 8k;
    gzip_comp_level 6;
    gzip_disable msie6;
    gzip_http_version 1.1;
    gzip_min_length 10240;
    gzip_proxied any;
    gzip_vary on;
    gzip_types
        application/atom+xml
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rss+xml
        application/vnd.geo+json
        application/vnd.ms-fontobject
        application/x-font-ttf
        application/x-javascript
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xml
        application/xml+rss
        font/opentype
        image/bmp
        image/svg+xml
        image/x-icon
        text/cache-manifest
        text/css
        text/javascript
        text/plain
        text/vcard
        text/vnd.rim.location.xloc
        text/vtt
        text/x-component
        text/x-cross-domain-policy
        text/xml;

    server {
        listen 80;
        listen [::]:80;
        server_name webdav.example.com;

        root /var/www/webdav.example.com;

        autoindex on;
        autoindex_exact_size off;
        autoindex_localtime on;
        dav_access user:rw group:r all:r;
        dav_methods DELETE MOVE PUT;
        create_full_put_path on;
    }
}

РЕДАКТИРОВАТЬ: Я обнаружил интересное наблюдение. Если я перезагружу nginx (sudo service nginx reload), первая попытка сохранить файл будет успешной. Но если я попытаюсь сохранить его в следующий раз, произойдет та же ошибка.

Оказывается, это был материал open_file_cache. В документы звучит так, будто он кэширует только метаданные файла, как кеш статистики, но в этом случае он фактически делал некоторые ответы устаревшими.

open_file_cache off; внутри server { ... } block был все, что нужно, и теперь он работает хорошо.