# Кэширование и пагинация
## Реализованные функции
### 1. Кэширование на уровне API (Redis)
**Файлы:**
- `lib/cache/redis.ts` - базовые функции кэширования
- `lib/api/cached-api.ts` - API с интегрированным кэшированием
**Функции:**
- ✅ `getCachedAircraft()` - кэширование списка ВС (TTL: 1 час)
- ✅ `getCachedRisks()` - кэширование рисков (TTL: 30 минут)
- ✅ `getCachedAudits()` - кэширование аудитов (TTL: 30 минут)
- ✅ `getCachedOrganizations()` - кэширование организаций (TTL: 2 часа)
- ✅ `getCachedStats()` - кэширование статистики (TTL: 5 минут)
- ✅ `invalidateCache()` - инвалидация кэша при изменении данных
**Использование в API routes:**
```typescript
import { getCachedAircraft } from '@/lib/api/cached-api';
export async function GET() {
const aircraft = await getCachedAircraft();
return NextResponse.json(aircraft, {
headers: {
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
},
});
}
```
**TTL для разных типов данных:**
- Aircraft: 1 час
- Risks: 30 минут
- Audits: 30 минут
- Organizations: 2 часа
- Documents: 1 час
- Regulations: 24 часа
- Stats: 5 минут
### 2. Next.js ISR (Incremental Static Regeneration)
**Реализовано:**
- ✅ Экспорт `revalidate` в страницах для ISR
- ✅ Автоматическое пересоздание страниц по расписанию
**Пример:**
```typescript
// app/regulations/page.tsx
export const revalidate = 3600; // Пересоздавать каждый час
```
**Преимущества:**
- Статические страницы загружаются мгновенно
- Автоматическое обновление без пересборки всего сайта
- Меньше нагрузка на сервер
### 3. SWR для кэширования на клиенте
**Файлы:**
- `lib/swr-config.ts` - конфигурация SWR
- `hooks/useSWRData.ts` - хуки для использования SWR
**Хуки:**
- ✅ `useAircraftData()` - данные о ВС с автоматическим обновлением
- ✅ `useRisksData()` - данные о рисках
- ✅ `useAuditsData()` - данные об аудитах
- ✅ `useStatsData()` - статистика
- ✅ `useOrganizationsData()` - данные об организациях
**Использование:**
```tsx
import { useAircraftData } from '@/hooks/useSWRData';
function AircraftList() {
const { data, error, isLoading, mutate } = useAircraftData({
page: 1,
limit: 50,
paginate: true,
});
if (isLoading) return
Загрузка...
;
if (error) return
Ошибка: {error.message}
;
return (
{data?.data.map(aircraft => (
{aircraft.registrationNumber}
))}
);
}
```
**Настройки SWR:**
- `revalidateOnFocus: false` - не обновлять при фокусе
- `revalidateOnReconnect: true` - обновлять при восстановлении соединения
- `refreshInterval` - автоматическое обновление (настраивается для каждого хука)
- `dedupingInterval: 2000` - дедупликация запросов
- `errorRetryCount: 3` - количество попыток при ошибке
### 4. Пагинация
#### Server-side пагинация
**Реализовано:**
- ✅ `paginatedQuery()` в `lib/database/query-optimizer.ts`
- ✅ Поддержка пагинации в API routes
- ✅ Оптимизированные запросы с LIMIT/OFFSET
**Использование:**
```typescript
// API route
const result = await paginatedQuery(
'SELECT * FROM aircraft WHERE status = $1',
page,
limit,
['Активен'],
'created_at DESC'
);
// Возвращает:
// {
// data: [...],
// total: 1000,
// page: 1,
// pageSize: 50,
// totalPages: 20
// }
```
**Запрос с пагинацией:**
```
GET /api/aircraft?page=1&limit=50&paginate=true
```
#### Client-side пагинация
**Компоненты:**
- ✅ `Pagination` - компонент пагинации
- ✅ `usePagination` - хук для пагинации данных
- ✅ `useInfiniteScroll` - хук для бесконечной прокрутки
**Использование:**
```tsx
import Pagination from '@/components/Pagination';
import { usePagination } from '@/hooks/usePagination';
function DataList({ data }) {
const { data: paginatedData, pagination } = usePagination(data, {
page: 1,
limit: 20,
total: data.length,
});
return (
<>
{paginatedData.map(item =>
{item.name}
)}
>
);
}
```
## Интеграция
### API Routes с кэшированием
Все API routes используют кэширование через Redis:
- `/api/aircraft` - кэширование списка ВС
- `/api/risks` - кэширование рисков с фильтрацией
- `/api/audits` - кэширование аудитов с фильтрацией
- `/api/stats` - кэширование статистики
- `/api/organizations` - кэширование организаций
### Инвалидация кэша
При изменении данных необходимо инвалидировать кэш:
```typescript
import { invalidateCache } from '@/lib/api/cached-api';
// После создания/обновления/удаления
await invalidateCache('aircraft', aircraftId);
```
## Настройка
### Redis
Добавьте в `.env.local`:
```env
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_ENABLED=true
```
### TTL кэша
Настройка TTL в `lib/api/cached-api.ts`:
```typescript
const CACHE_TTL = {
aircraft: 3600, // 1 час
risks: 1800, // 30 минут
// ...
};
```
### SWR настройки
Настройка в `lib/swr-config.ts`:
```typescript
export const swrConfig: SWRConfiguration = {
refreshInterval: 0, // Отключить автоматическое обновление
dedupingInterval: 2000, // Дедупликация
errorRetryCount: 3, // Попытки при ошибке
};
```
## Производительность
### Преимущества кэширования
1. **Снижение нагрузки на БД** - запросы к Redis быстрее чем к PostgreSQL
2. **Быстрая отдача данных** - кэшированные данные возвращаются мгновенно
3. **Масштабируемость** - Redis может обслуживать множество запросов параллельно
### Преимущества SWR
1. **Автоматическое обновление** - данные обновляются в фоне
2. **Дедупликация** - одинаковые запросы выполняются один раз
3. **Кэширование на клиенте** - данные доступны сразу при повторном визите
4. **Оптимистичные обновления** - можно обновлять UI до получения ответа
### Преимущества пагинации
1. **Меньше данных** - загружаются только нужные записи
2. **Быстрее загрузка** - меньше данных = быстрее ответ
3. **Меньше памяти** - не нужно хранить все данные в памяти
## Мониторинг
### Метрики кэша
- Hit rate (процент попаданий в кэш)
- Miss rate (процент промахов)
- TTL для разных типов данных
- Размер кэша
### Метрики SWR
- Количество запросов
- Время ответа
- Количество ошибок
- Частота обновлений
## Рекомендации
1. **Используйте server-side пагинацию** для больших списков (>100 записей)
2. **Настройте TTL** в зависимости от частоты обновления данных
3. **Инвалидируйте кэш** при изменении данных
4. **Мониторьте hit rate** кэша для оптимизации TTL
5. **Используйте SWR** для данных, которые часто обновляются
6. **Применяйте ISR** для статических страниц с редкими обновлениями