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.