Files
2022_oknardia/CACHE_PRERENDER_SYSTEM.md

362 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Система двухуровневого кеширования страниц серий
## 📖 Описание
Система страниц серий домов использует **двухуровневое кеширование**:
1. **Статический кеш** — дорогостоящие данные (карты, графики, схемы), генерируются один раз и сохраняются на диск
2. **Динамические данные** — верхняя статья (редактируется через админку) и таблица оконных проёмов (показывает свежие предложения), пересчитываются при каждом запросе
---
## 🏗️ Архитектура кеша
Для каждой серии создаются **3 отдельных кешируемых файла**:
| Файл | Содержимое | Размер | Где появляется |
|------|-----------|--------|---|
| `{seria_id}_id_static_flaps.html` | Схемы открывания и типовые размеры окон | 4-7KB | В разделе "Дома серии: типовые размеры" |
| `{seria_id}_id_static_graph.html` | Google Charts: график ввода в эксплуатацию | 2-3KB | В контейнере height:300px |
| `{seria_id}_id_static_map_stats.html` | Yandex Maps + блок статистики | 6-46KB | Карта (col-md-7) + статистика (col-md-4) |
### Верхняя статья — ДИНАМИЧЕСКИЕ ДАННЫЕ, не кешируется!
**Верхняя статья про серию** (`THIS_SERIA_DESCRIPTION`) — это динамические данные, которые:
- Хранятся в БД (поле `sDescription` модели `Seria_Info`)
- **Редактируются через админку** → нужны изменения **без перезагрузки контейнера**
- Рендерятся всегда из БД, не сохраняются в кеш-файлы
Если бы мы кешировали верхнюю статью:
- Админ редактирует статью → кеш-файл не обновляется автоматически
- Нужно перезагрузить контейнер или вручную удалять файл кеша
- При регенерации кеша может произойти перезапись старыми данными
**Решение**: верхняя статья **всегда рендерится из БД**, как и таблица окон.
---
## 🔄 Логика работы
### При первом запросе seriesId (cache miss):
1. View `catalog_seria_info()` обнаруживает отсутствие кеш-файлов
2. Вычисляются дорогостоящие данные:
- Геокоординаты всех зданий серии
- Год ввода в эксплуатацию (для графика)
- Схемы открывания окон
3. Генерируются **3 отдельных файла** в `oknardia/templates/seria_info/prepared/`
4. Верхняя статья и таблица окон **рендерятся из БД** при каждом запросе
5. Контекст получает пути к 3 кеш-файлам + свежие динамические данные
6. Main-шаблон включает 3 кеш-файла + 2 динамических блока
7. Ответ отправляется пользователю
### При последующих запросах (cache hit):
1. View обнаруживает, что все 3 файла существуют
2. **Верхняя статья пересчитывается заново** из БД (от админа)
3. **Таблица окон пересчитывается заново** (новые предложения видны сразу)
4. Main-шаблон включает 3 статических файла + 2 динамических блока
5. Ответ отправляется быстро (схемы, график, геоданные из кеша)
### Ключевой мюмент: Всё свежее!
- Кеширование **не трогает** верхнюю статью и таблицу окон → они всегда свежие
- Новые цены от поставщиков видны пользователям **сразу**
- Админ может редактировать текст про серию → видно **без перезагрузки** контейнера
- Таблица пересчитывается в запросе через Q-фильтры к БД (есть индексы на БД)
---
## 📁 Структура файлов
### Templates (шаблоны)
```
oknardia/templates/seria_info/
├── all_seria_info_pre_light.html # Main-шаблон (включает 3 static-файла + динамику)
├── all_seria_info_pre_light_static_flaps.html # ШАГ 1: Схемы открывания (кешируется)
├── all_seria_info_pre_light_static_graph.html # ШАГ 2: График ввода (кешируется)
├── all_seria_info_pre_light_static_map_stats.html # ШАГ 3: Карта + статистика (кешируется)
└── prepared/ # Директория с кеш-файлами
├── 210_id_static_flaps.html
├── 210_id_static_graph.html
├── 210_id_static_map_stats.html
├── 100_id_static_flaps.html
└── ... (93 файла: 31 серия × 3 типа)
```
### Генерация кеша (Web-логика)
```
oknardia/web/
├── catalog_series.py # catalog_seria_info() — генерирует 3 файла + динамику
└── management/commands/
└── regenerate_seria_prerender.py # Batch-команда для регенерации всех серий
```
---
## 🚀 Управление кешем
### Разработка (DEV-режим)
```bash
python manage.py runserver
```
В DEV-режиме (`DEBUG = True`):
- Кеш **не используется** (всегда `PRE_RENDERED_STATIC_*_PATH = ""`)
- Main-шаблон рендерит данные напрямую
- Удобно для разработки (вижу изменения сразу)
### Production (PROD-режим)
#### Первоначальная генерация (все 31 серия):
```bash
python manage.py regenerate_seria_prerender
```
Вывод:
```
OK seria 100: 3 кеш-файла созданы
OK seria 12: 3 кеш-файла созданы
...
Готово. Обработано: 31. Создано/пересоздано: 31 × 3 файла. Пропущено: 0.
```
#### Регенерация конкретной серии:
```bash
python manage.py regenerate_seria_prerender --seria-id 210
```
#### Dry-run (без создания файлов):
```bash
python manage.py regenerate_seria_prerender --dry-run
```
#### Force-переписать даже если есть кеш:
```bash
python manage.py regenerate_seria_prerender --force
```
---
## 📊 Когда нужна регенерация кеша?
### ✅ Регенерируйте, если:
- Изменены координаты зданий (geo-данные)
- Добавлены новые здания в серию
- Обновлены годы ввода в эксплуатацию (для графика)
- Изменены схемы открывания окон
- Обновлена верхняя статья про серию
```bash
# Все изменилось → перестроить все
python manage.py regenerate_seria_prerender --force
# Изменилась одна серия → перестроить одну
python manage.py regenerate_seria_prerender --seria-id 210
```
### ❌ НЕ нужна регенерация, если:
- Добавлены новые **предложения** (цены от поставщиков)
- Обновлены **наличие/status** существующих предложений
- Система просто должна **показать свежие цены**
Таблица окон пересчитывается при каждом запросе автоматически! 🎉
---
## 🔌 Интеграция с Docker
В контейнере (PROD-режим):
```dockerfile
# Генерируем кеш пр<D0BF><D180> запуске
RUN python manage.py regenerate_seria_prerender
# Запускаем сервер
CMD ["gunicorn", "oknardia.wsgi:application", "--bind", "0.0.0.0:8000"]
```
Кеш-файлы сохраняются на диск в томе, поэтому переживают перезагрузку контейнера.
---
## 📝 Контекстные переменные
### При первом запросе (происходит генерация):
View `catalog_seria_info()` отправляет в шаблон:
```python
to_template = {
"THIS_SERIA_ID": seria_id, # ID серии (210)
"THIS_SERIA_NAME": q_seria.sName, # Название ("1-335")
"THIS_SERIA_DESCRIPTION": html_description, # Верхняя статья
"FLAP_DIM": flap_dimensions, # Массив схем открывания
"DATA4GRAPH": graph_data, # Годы и кол-во домов
"DATA4GEO": geo_data, # Координаты зданий
"ACCOUNTS": buildings_count, # Кол-во квартир
"APARTMENTS": families_count, # Кол-во семей
"RESIDENTIAL_M2": total_area, # Площадь жилая
# ... и другие
}
```
### Для шаблонов генерации кеша:
Каждый template файл получает **все** эти переменные и рендерится отдельно.
### Для main-шаблона:
Main-шаблон получает пути только к 3 кеш-файлам:
```python
to_template.update({
"PRE_RENDERED_STATIC_FLAPS_PATH": "seria_info/prepared/210_id_static_flaps.html",
"PRE_RENDERED_STATIC_GRAPH_PATH": "seria_info/prepared/210_id_static_graph.html",
"PRE_RENDERED_STATIC_MAP_STATS_PATH": "seria_info/prepared/210_id_static_map_stats.html",
})
# Верхняя статья всегда передается как THIS_SERIA_DESCRIPTION и рендерится динамически
to_template.update({
"THIS_SERIA_DESCRIPTION": html_description, # Из БД, не кешируется
})
```
Main-шаблон использует `{% include %}` для 3 кеш-файлов, а верхнюю статью рендерит напрямую: `{{ THIS_SERIA_DESCRIPTION|safe }}`.
---
## 🎯 Примеры использования
### Добавили новое здание в серию 210
```bash
# Обновили БД через Django ORM/admin
python manage.py shell
>>> from oknardia.models import Building_Info
>>> Building_Info.objects.create(...)
# Регенерируем кеш только этой серии
python manage.py regenerate_seria_prerender --seria-id 210
# Пользователи видят новое здание на карте и в таблице
```
### Обновили года ввода в эксплуатацию
```bash
# Изменили данные
python manage.py shell
>>> from oknardia.models import Building_Info
>>> Building_Info.objects.filter(...).update(...)
# Кеш станет невалидным — нужна регенерация
python manage.py regenerate_seria_prerender --force
# Пользователи видят обновленный график
```
### Добавили новое предложение (цену)
```bash
# PriceOffer.objects.create(...) → система добавляет новое предложение
# НЕ нужна регенерация!
# Таблица окон обновилась сама на следующем запросе
# Пользователи видят новое предложение сразу
```
---
## 🐛 Отладка
### Проверить, какие кеш-файлы существуют:
```bash
ls -lah oknardia/templates/seria_info/prepared/
```
### Вручную удалить кеш (для тестирования):
```bash
# Удалить кеш одной серии
rm oknardia/templates/seria_info/prepared/210*.html
# Удалить ВСЕ кеш-файлы
rm oknardia/templates/seria_info/prepared/*_id_static_*.html
```
### Проверить содержимое кеш-файла:
```bash
cat oknardia/templates/seria_info/prepared/210_id_static_graph.html | head -20
cat oknardia/templates/seria_info/prepared/210_id_static_flaps.html | head -30
cat oknardia/templates/seria_info/prepared/210_id_static_map_stats.html | head -50
```
### Логирование:
В `catalog_series.py` логируем создание файлов:
```python
logger.info(f"Cache created: {file_flaps}")
logger.info(f"Cache created: {file_graph}")
logger.info(f"Cache created: {file_map_stats}")
# file_upper НЕ создается — верхняя статья рендерится динамически
```
---
## 📈 Производительность
### Без кеша (DEV-режим):
- ~2-3 сек на запрос (вычисляются geo, graph, flaps)
- Каждый запрос трогает БД
- Удобно для разработки
### С кешем (PROD-режим):
- ~100-300 мс на первый запрос (генерируется кеш)
- ~50-100 мс на последующие (включаются файлы + динамическая таблица)
- Статические данные из кеша (очень быстро)
- Таблица окон кешируется на уровне DB-запроса (индексы работают)
---
## ✅ Чек-лист для администратора
При развертывании в production:
- [ ] Запустить `python manage.py regenerate_seria_prerender` (сгенерировать все 93 файла)
- [ ] Проверить размеры файлов в `prepared/` (~100-200 KB всего)
- [ ] Протестировать на локалхосте `DEBUG = False`
- [ ] Проверить, что таблица окон обновляется при добавлении новых предложений
- [ ] Настроить логирование создания кеша в продакшене
---
## 🔗 Близкие компоненты
- **Main-шаблон**: `oknardia/templates/seria_info/all_seria_info_pre_light.html`
- **Динамическая таблица**: `oknardia/templates/seria_info/all_seria_info_pre_light_dynamic_include.html`
- **View**: `oknardia/web/catalog_series.py::catalog_seria_info()`
- **Management-команда**: `oknardia/web/management/commands/regenerate_seria_prerender.py`
- **Тесты**: `oknardia/web/test_prices.py` (проверяет свежесть таблицы)
---
**Версия:** 2.0 (Двухуровневое кеширование)
**Последнее обновление:** 2026-05-19
**Статус:** ✅ Production-ready