English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

гість
1 / ?
назад до уроків

Форма 1: Відновлення стану. Форма 2: Марнотратний звіт.

Зміряне серце б’ється за годинником. Не за потребою. Не за зміною. За таймером.

Дві форми, одна коренева причина: заплановане завдання замість правильного дизайну.

Форма 1: Відновлення стану

Перехід стану не завершується атомарно. Замість виправлення переходу, фонове завдання запускається із затримкою та узгоджує. Користувачі бачать пошкоджений стан протягом вікна узгодження.

Приклад GitHub (2026-04-08): Запит на злиття (pull request) мав upstream-репозиторій, який став приватним. GitHub спробував виконати перехід стану: закрити PR, оновити статус гілки, очистити статус злиття. Перехід не завершився атомарно. Статус PR одночасно показував «branch-forced-closed» та «Merge status cannot be loaded». Фонова задача Sidekiq запустилася за кілька хвилин і завершила узгодження. Спостерігачі бачили пошкоджений стан протягом усього проміжку часу.

The Metered Heart: задача Sidekiq виконувалася за розкладом. Вона запустилася не тому, що GitHub виявив пошкоджений стан, а тому, що спрацював таймер. Користувач, який спостерігав за PR у реальному часі, бачив суперечливий PR до наступного виконання задачі.

Form 2: Wasteful Report

Звіт або агрегація переобчислюється з нуля через фіксований інтервал. Без перевірки кешу. Без захисного механізму ідемпотентності. Без інкрементного оновлення. Кожне виконання: повне сканування.

Приклади: нічне cron-завдання, яке переобчислює загальну суму покупок кожного користувача, скануючи всі замовлення з самого початку. Щоденне аналітичне завдання, яке регенерує дашборд із необроблених логів подій. Щотижневий підсумковий лист, який запитує кожен рядок у таблиці активності.

Кожне завдання запускається незалежно від того, чи змінилися дані з моменту останнього виконання. Кожне сканує всю історію, навіть коли нові дані містяться лише за останні 24 години. Кожне замінює інкрементний дизайн запланованим повторенням.

The Shared Root

A Metered Heart не може сказати правду про власний стан. Він знає лише годинник. Form 1: задача відновлення стану запускається о T+5 хвилин незалежно від того, чи стан пошкоджено о T+0. Form 2: задача звіту запускається о 2 годині ночі незалежно від того, чи змінилися дані з учорашнього дня.

Годинник не несе інформації про те, що потрібно робити. Цю інформацію несе подія: «перехід стану щойно завершився невдало», «надійшли нові замовлення». Metered Heart відкидає цю інформацію та замінює її розкладом.

Витік капіталу

Metered Heart виснажує живий капітал: інженери чергують на інциденти з порушеним станом. Підриває соціальну довіру: користувачі бачать неузгоджені дані та повідомляють про дефекти, які зникають самі. Підсилює інші MOAD: робота з відновлення стану, яка сканує всі записи, щоб знайти порушений стан, часто містить MOAD-0001 (сканування O(N²)). Звітна робота, яка переобчислює холодні дані, може спровокувати MOAD-0005 (штампед кешу). MOAD-0009 посилює інші дефекти.

Спільний корінь

Form 1 та Form 2 виглядають по-різному на поверхні: одна відновлює стан, інша переобчислює дані. Їх об’єднує спільна коренева причина.

Form 1 і Form 2 мають спільну кореневу причину. Опишіть її одним реченням. Потім наведіть приклад кожної форми з програмного забезпечення, яким ви користувалися.

Fire on Change, Not on Clock

Event-driven дизайн спрацьовує, коли щось змінюється. Зміна стану — це подія. Подія є тригером.

Форма 1: атомарний перехід замінює ремонтне завдання.

Якщо перехід стану може залишити систему в пошкодженому проміжному стані, дефект міститься в самому переході, а не в відсутності ремонтного завдання. Виправте перехід, щоб він завершувався атомарно (або транзакційно). Коли перехід завершується атомарно, пошкоджений стан ніколи не виникає. Ремонтному завданню нічого виправляти.

# DEFECT: non-atomic transition leaves broken state
def close_pr_on_repo_private(pr_id):
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'   # крок 1: частковий стан
pr.save()                             # видно користувачам ЗАРАЗ
# ... інші кроки можуть завершитися невдало ...
pr.merge_status = 'not_applicable'
pr.save()                             # крок 2: тепер узгоджено
# Sidekiq job узгоджує, якщо крок 2 не вдається
# ВИПРАВЛЕННЯ: атомарний перехід; немає проміжного стану
def close_pr_on_repo_private(pr_id):
with db.transaction():
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'
pr.merge_status = 'not_applicable'
pr.save()   # обидва поля зберігаються атомарно; ніколи не напівзаписані

Форма 2: поступкове оновлення замінює повний перерахунок.

Звіт, який перераховується з нуля, спрацьовує тому, що старі дані + нові дані = новий результат. Але старий результат + дельта = той самий новий результат, обчислений поступково. Подія: надійшли нові дані. Тригер: оновити агрегат лише для нових даних.

# ДЕФЕКТ: повний перерахунок за розкладом
def nightly_totals_job():
for user in all_users():
total = sum(o.amount for o in user.orders)  # сканувати весь час
user.total_purchases = total
user.save()

# ВИПРАВЛЕННЯ: інкрементне оновлення на основі подій
def on_order_placed(order):
order.user.total_purchases += order.amount   # лише дельта
order.user.save()

Інкрементне оновлення спрацьовує, коли надходить замовлення, а не о 2 годині ночі. Воно оновлює лише відповідного користувача. Воно зчитує лише нове замовлення, а не всі замовлення за весь час. Нічний джоб зникає.

Чому Форма 1 виявляє зламаний перехід

Лічильник серця Form 1 показує, що перехід стану залишився незавершеним. Джоб відновлення існує, бо інженер помітив зламаний стан і додав механізм узгодження замість виправлення переходу. Джоб відновлення: лата над зламаним архітектурним рішенням.

MOAD-0009 як підсилювач

MOAD-0009 підсилює інші MOAD. Джоб відновлення стану, який сканує всі записи, щоб знайти зламаний стан: MOAD-0001 (сканування O(N) або O(N²) за кожне виконання джобу). Джоб звіту, який переобчислює все з нуля: MOAD-0005 (штампед кешу, коли джоб запускається і звертається до теплого upstream). MOAD-0009 не просто шкодить сам по собі; він доставляє інші MOAD за розкладом.

Діагностика та редизайн

Команда запускає нічний cron-джоб о 2 годині ночі. Джоб сканує всі замовлення всіх користувачів і переобчислює загальну суму покупок кожного користувача з нуля. Джоб триває 4 години. До 6 години ранку дашборд показує свіжі суми. Між 2 і 6 годинами ранку дашборд показує суми за вчора.

Яка форма MOAD-0009? Яка подія має запускати перерахунок? Яка проміжна структура даних робить оновлення інкрементним?