Gymterview
senior

Как работает кластеризация в Jira Data Center и как это влияет на плагины?

Jira Data Center — кластерное решение, где несколько нод Jira работают за балансировщиком нагрузки, разделяя общую БД и файловую систему, что предъявляет специфические требования к плагинам.

Архитектура DC кластера

Пример
                    ┌──────────────────┐
                    │  Load Balancer   │
                    └───────┬──────────┘
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
         ┌────────┐    ┌────────┐    ┌────────┐
         │ Node 1 │    │ Node 2 │    │ Node 3 │
         │ Jira   │    │ Jira   │    │ Jira   │
         │+Plugin │    │+Plugin │    │+Plugin │
         └───┬────┘    └───┬────┘    └───┬────┘
             │   Hazelcast │             │
             └──────┬──────┘─────────────┘
                    │
              ┌─────┴─────┐
              │ Shared DB  │     ┌─────────────────┐
              │ (Postgres) │     │ Shared FS (NFS)  │
              └────────────┘     └─────────────────┘

Влияние на плагины

Пример
// НЕПРАВИЛЬНО — состояние в памяти ноды
@Named
public class BadService {
    // Этот кэш локальный — данные рассинхронизируются между нодами
    private final Map<String, Config> localCache = new ConcurrentHashMap<>();
}

// ПРАВИЛЬНО — данные в общей БД или распределённом кэше
@Named
public class GoodService {
    private final ActiveObjects ao;
    private final CacheManager cacheManager;

    @Inject
    public GoodService(@ComponentImport ActiveObjects ao,
                       @ComponentImport CacheManager cacheManager) {
        this.ao = ao;
        this.cacheManager = cacheManager;
    }
}

Распределённые блокировки (ClusterLockService)

Пример
@Named
public class ScheduledTaskService {

    private final ClusterLockService clusterLockService;

    @Inject
    public ScheduledTaskService(
            @ComponentImport ClusterLockService clusterLockService) {
        this.clusterLockService = clusterLockService;
    }

    public void runExclusiveTask() {
        ClusterLock lock = clusterLockService.getLockForName("my-plugin-daily-task");
        if (lock.tryLock()) {
            try {
                performDailyCleanup();
            } finally {
                lock.unlock();
            }
        } else {
            log.debug("Задача уже выполняется на другой ноде");
        }
    }
}

Чек-лист кластерной совместимости

Требование Проверка
Нет in-memory state Все данные в БД или distributed cache
Нет локальных файлов Используется shared filesystem или БД
Нет java.util.Timer Используется SAL PluginScheduler или ClusterLockService
Нет статических кэшей Используется CacheManager
Scheduled tasks идемпотентны Безопасно выполнить на любой ноде
Plugin settings Через SAL PluginSettingsFactory (хранятся в БД)

Частые ошибки

  • Локальный ConcurrentHashMap как кэш — рассинхронизация между нодами
  • java.util.Timer или ScheduledExecutorService — задача выполняется на каждой ноде одновременно
  • Запись во временные локальные файлы — другая нода их не видит
  • Не тестировать на кластере из 2+ нод перед релизом

Как используется в 2026

  • Кластерная совместимость — обязательное требование для публикации на Atlassian Marketplace
  • Типичные кластеры: 2-4 ноды для средних компаний, 8+ нод для крупных enterprise
  • Atlassian предоставляет DC Performance Toolkit для нагрузочного тестирования плагинов в кластере
  • Zero-downtime upgrades предъявляют дополнительные требования к обратной совместимости плагинов

На собеседовании: это senior-вопрос, показывающий понимание распределённых систем. Ключевое: stateless-дизайн, CacheManager вместо ConcurrentHashMap, ClusterLockService для эксклюзивных задач. Маркер atlassian-data-center-compatible=true обязателен. Hazelcast — движок распределённого кэша и messaging в Jira DC.