From a4798c4a36c53a0594d48602c39836b52a54f45c Mon Sep 17 00:00:00 2001 From: Vladislav Kopnin Date: Tue, 6 Oct 2026 15:01:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=20=D0=BA=D1=8D=D1=88=D1=83:=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D0=B2=D0=B0=D0=BB=D1=83?= =?UTF-8?q?=20=D1=81=D0=BD=D0=BE=D0=B2=D0=B0=20=D1=81=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20modified,=20=D0=B2=D1=81=D0=B5=D0=B3=D0=B4?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=B0=D1=87=D0=B0=D0=B5=D1=82=20=D1=82=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Как и просили: фоновое обновление по интервалу сначала смотрит на дату изменения файла и, если таблица не менялась, не скачивает её. Всегда перечитывать таблицу — только принудительное обновление из админки (force_reload, кнопка «Обновить сейчас»). Тесты: по интервалу — без скачивания при неизменном modified; кнопка — скачивает независимо от modified (test_force_refresh_ignores_ttl_and_modified). --- README.md | 18 ++++++++++-------- src/backend/schedule/cache.py | 21 +++++++++++++++------ tests/test_api.py | 10 +++++----- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 929bb9c..b33929a 100644 --- a/README.md +++ b/README.md @@ -101,16 +101,18 @@ nginx (https://schedule.our-place.ru) → 127.0.0.1:8090 ошибка, число групп); - **вставить новую ссылку**: она проверяется скачиванием и разбором таблицы до сохранения — нерабочая не применится, данные на сайте не пострадают; -- **принудительно обновить расписание** — вне интервала, не дожидаясь - фонового обновления (интервал и так перечитывает таблицу целиком); +- **принудительно обновить расписание** — вне интервала: кнопка перечитывает + таблицу всегда, не глядя на дату изменения файла; - **задать интервал обновления** в минутах (`1…1440`). -Каждое обновление (и по кнопке, и по интервалу) **всегда перечитывает таблицу**, -а в панели и в логе видно, изменился ли файл на Диске: `sha256`, время -последнего изменения содержимого и отметка «изменился / без изменений». -Если вы поправили таблицу, а после обновления написано «без изменений» — значит -публичная ссылка Яндекса отдаёт прежнюю версию файла (бывает с задержкой), -стоит повторить обновление через минуту. +Фоновое обновление по интервалу работает как раньше: сначала сверяется +`modified` файла, и если таблица не менялась — скачивания не происходит. +Принудительное обновление (кнопка) скачивает файл **всегда**, поэтому его и +стоит нажимать сразу после правок в таблице. В панели и в логе при этом видно, +изменилось ли содержимое файла: `sha256`, время последнего изменения и отметка +«изменился / без изменений». Если вы поправили таблицу, а после обновления +написано «без изменений» — значит публичная ссылка Яндекса отдаёт прежнюю +версию файла (бывает с задержкой), стоит повторить обновление через минуту. Как это устроено: diff --git a/src/backend/schedule/cache.py b/src/backend/schedule/cache.py index 6c8c801..aa76469 100644 --- a/src/backend/schedule/cache.py +++ b/src/backend/schedule/cache.py @@ -2,9 +2,9 @@ - однопоточная перезагрузка (lock): параллельные запросы во время загрузки ждут один download вместо N скачиваний; -- интервал обновления: по его истечении таблица перечитывается целиком - (поле modified у публичной ссылки может «застыть», поэтому на него не - опираемся — оно только показывается в админке); +- интервал обновления: по его истечении сверяем modified таблицы — если файл + не менялся, скачивание не повторяется; принудительное обновление из админки + (force_reload) перечитывает таблицу всегда; - stale-while-error: если таблица перестала скачиваться, отдаём последнее удачное состояние и повторяем попытку не чаще, чем раз в retry_after секунд (вместо скачивания на каждый запрос); @@ -218,10 +218,19 @@ class ScheduleCache: if not self._link: self._fail(ScheduleError(NO_LINK_ERROR), now) return - # Обновление интервала всегда перечитывает таблицу: поле modified у - # публичной ссылки может «застыть» (кэш на стороне Диска), и тогда - # проверка «таблица не менялась» держала бы старые данные бесконечно. + # Обновление по интервалу: сначала сверяем modified — если файл не + # менялся, скачивание не повторяем. Принудительное обновление (кнопка + # в админке) идёт через force_reload и качает файл всегда. modified = fetch_table_modified(self._link) + if ( + self._data is not None + and modified is not None + and modified == self._modified + ): + # таблица не менялась — источник доступен, данные актуальны + self._loaded_at = now + self._error = None + return try: self._load(now, modified) except ScheduleError as exc: diff --git a/tests/test_api.py b/tests/test_api.py index dc81d68..ba6af99 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -170,16 +170,16 @@ def test_cache_no_repeated_download(client, cache): assert cache.calls["download"] == 1 -def test_interval_refresh_always_downloads(cache): - """По интервалу таблица перечитывается всегда. +def test_modified_unchanged_no_download(cache): + """По интервалу сверяем modified: если таблица не менялась — не скачиваем. - Поле modified у публичной ссылки может «застыть», поэтому на него не - опираемся: иначе стенд показывал бы старые данные бесконечно. + Принудительное обновление из админки при этом качает файл всегда + (см. test_force_refresh_ignores_ttl_and_modified). """ cache._ttl = 0 cache.get() cache.get() - assert cache.calls["download"] == 2 # оба обновления по интервалу скачали таблицу + assert cache.calls["download"] == 1 assert cache.calls["modified"] >= 2