← Назад к вопросам

Кому нужен был планировщик в проекте IKEA

1.3 Junior🔥 51 комментариев
#Soft Skills и карьера

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Планировщик в проекте IKEA

В проекте IKEA планировщик был необходим для автоматизации процессов управления поставками и синхронизацией данных между несколькими системами, работающими 24/7 в разных часовых поясах.

Контекст проекта

IKEA — многомилионная торговая сеть с сотнями складов и магазинов. Для каждого из них критично:

  • Синхронизация каталога товаров между центральной системой и локальными кешами
  • Управление уровнями товара на складах в реальном времени
  • Расчёт оптимальных сроков доставки покупателей
  • Генерация отчётов по продажам для разных подразделений
  • Очистка устаревших данных из кешей

Проблема без планировщика

// ❌ Без планировщика: ручное управление
public class ProductCatalogService {
    public void updateCatalog() {
        // Нужно вызывать вручную
        // Когда? Как часто? Что если система упадёт?
        List<Product> products = fetchProductsFromMainSystem();
        synchronizeWithLocalCache(products);
        notifyWarehouses(products);
    }
}

// Кто-то из разработчиков должен помнить вызвать это в нужное время
// Ненадёжно, подвержено ошибкам

Решение: Планировщик (Scheduler)

// ✅ С планировщиком: автоматизированное выполнение
import org.springframework.scheduling.annotation.Scheduled;
import java.util.concurrent.ScheduledExecutorService;

public class ProductSyncScheduler {
    
    private final ProductService productService;
    private final NotificationService notificationService;
    
    @Scheduled(cron = "0 0 */4 * * *") // Каждые 4 часа
    public void syncProductCatalog() {
        try {
            log.info("[IKEA] Starting catalog synchronization");
            
            // Синхронизируем данные
            List<Product> products = productService.fetchFromMainSystem();
            productService.updateLocalCache(products);
            
            // Уведомляем все склады
            notificationService.notifyAllWarehouses(products);
            
            log.info("[IKEA] Catalog synced successfully");
        } catch (Exception e) {
            log.error("[IKEA] Catalog sync failed", e);
            alertOncall(); // Уведомляем oncall инженера
        }
    }
    
    @Scheduled(cron = "0 30 2 * * *") // Каждый день в 02:30
    public void generateDailyReports() {
        // Генерируем отчёты по продажам
        List<SalesReport> reports = productService.generateReports();
        reportService.distributeToManagers(reports);
    }
    
    @Scheduled(cron = "0 0 3 * * 0") // Каждое воскресенье в 03:00
    public void cleanupExpiredCache() {
        // Удаляем устаревшие данные
        cacheService.removeExpiredEntries();
    }
}

Задачи планировщика в проекте

1. Синхронизация товаров (Каждые 4 часа)

  • Загружаем обновлённый каталог из главной системы
  • Обновляем данные в локальных кешах магазинов
  • Нормализуем цены по валютам разных регионов
  • Применяем локальные скидки и промоции
@Scheduled(cron = "0 0 */4 * * *")
public void syncWarehouseInventory() {
    // Для каждого склада в каждой стране
    for (Warehouse warehouse : warehouseService.getAllWarehouses()) {
        try {
            InventoryData inventory = mainSystem.getInventory(warehouse.getId());
            warehouseCache.update(warehouse.getId(), inventory);
            metricsService.recordSuccess(warehouse.getId());
        } catch (Exception e) {
            metricsService.recordFailure(warehouse.getId());
            alertTeam(warehouse, e);
        }
    }
}

2. Расчёт сроков доставки (Каждый час)

  • Берём данные о текущем трафике
  • Берём информацию о наличии товара на складах
  • Рассчитываем оптимальный срок доставки для каждого региона
  • Сохраняем результаты для использования в checkout
@Scheduled(fixedDelay = 3600000) // Каждый час
public void updateDeliveryEstimates() {
    Map<String, DeliveryEstimate> estimates = new HashMap<>();
    
    for (Region region : regionService.getAllRegions()) {
        int baseDeliveryDays = calculateBaseDelivery(region);
        int trafficDelay = getTrafficDelay(region);
        int totalDays = baseDeliveryDays + trafficDelay;
        
        estimates.put(region.getCode(), 
            new DeliveryEstimate(totalDays, LocalDateTime.now()));
    }
    
    deliveryCache.updateAll(estimates);
}

3. Генерация отчётов (Каждый день в 02:30)

  • Агрегируем данные о продажах за день
  • Рассчитываем KPI для каждого магазина и региона
  • Отправляем отчёты менеджерам по email
  • Обновляем дашборды в BI системе
@Scheduled(cron = "0 30 2 * * *")
public void generateDailyReports() {
    LocalDate yesterday = LocalDate.now().minusDays(1);
    
    for (Region region : regionService.getAllRegions()) {
        SalesReport report = analyticsService.generateReport(region, yesterday);
        
        emailService.sendToManagers(
            region.getManagerEmails(),
            "Daily Sales Report - " + yesterday,
            report.formatAsHtml()
        );
        
        biService.pushToDataWarehouse(report);
    }
}

4. Очистка кешей (Еженедельно в 03:00)

  • Удаляем устаревшие данные товаров
  • Очищаем истёкшие сессии пользователей
  • Архивируем старые логи
  • Перестраиваем индексы БД
@Scheduled(cron = "0 0 3 * * 0")
public void maintenanceCleanup() {
    log.info("Starting maintenance cleanup");
    
    // Очищаем устаревшие кеши
    cacheService.removeOlderThan(Duration.ofDays(7));
    
    // Архивируем логи
    logService.archiveOlderThan(Duration.ofDays(30));
    
    // Перестраиваем индексы для оптимизации
    databaseService.rebuildIndices();
    
    log.info("Maintenance cleanup completed");
}

Почему именно планировщик был нужен

  1. Надёжность: Выполнится автоматически, даже если разработчик забыл
  2. Консистентность: Синхронизируется по расписанию, а не в случайное время
  3. Масштабируемость: Обновить 1000 магазинов одновременно
  4. Мониторинг: Можем отслеживать успех/неудачу синхронизации
  5. Оптимизация нагрузки: Запускаем тяжёлые операции в ночь, не мешая пользователям
  6. Восстановление: При сбое автоматически переполнимся после перезагрузки

Инструменты, которые мы использовали

// Spring Scheduler
@EnableScheduling // В конфиге приложения

// Quartz для более сложных сценариев
@Configuration
public class QuartzConfig {
    // Для IKEA использовали Quartz для хранения расписаний в БД
    // Можно менять расписание без перезагрузки приложения
}

// Redis для синхронизации между несколькими инстансами
// (чтобы синхронизация не запустилась на всех серверах одновременно)

Итог

Планировщик в проекте IKEA был критической компонентой инфраструктуры, обеспечивающей:

  • Автоматическую синхронизацию каталога 1000+ магазинов
  • Генерацию отчётов в нужное время
  • Оптимизацию нагрузки на системы
  • Уведомление команды об ошибках

Без планировщика система была бы зависима от ручных действий и была бы ненадёжна.