JavaRush /Курси /ChatGPT Apps /Стійкість системи: тайм-аути, circuit breaker-и, bulkhead...

Стійкість системи: тайм-аути, circuit breaker-и, bulkhead-и, захист від штормів вебхуків

ChatGPT Apps
Рівень 16 , Лекція 2
Відкрита

1. Навіщо взагалі думати про «стійкість» у ChatGPT App

У звичайному вебзастосунку користувач принаймні бачить URL, індикатор завантаження (спінер) браузера та може оновити сторінку. У ChatGPT користувач бачить один екран: чат і ваш застосунок. Якщо щось гальмує, він не розуміє, хто винен, — OpenAI, ваш Gateway, платіжний сервіс чи якийсь інший мікросервіс аналітики. Для нього все це — «ChatGPT + ваш застосунок».

Коли tool-call зависає на 3060 секунд, модель чекає, чекає… і в найкращому разі вибачається за затримку. У гіршому — галюцинує відповідь замість даних від вашого бекенду. Тому стійкість — це не лише про SRE і доступність, а й про якість відповіді, тон поведінки моделі та метрики в Store.

В екосистемі ChatGPT App є кілька незалежних контурів:

  • ChatGPT ↔ MCP Gateway.
  • Gateway ↔ ваші бекенд-/REST-сервіси (Gift REST API, Commerce REST API, Analytics Service тощо).
  • Ваші сервіси ↔ зовнішні API (LLM, платежі, каталоги).
  • Вхідні вебхуки (ACP, Stripe, будь-які інтеграції) ↔ ваші обробники.

Проблема в тому, що збій в одному місці може спричинити каскад. Gateway чесно чекає на завислий сервіс, воркери заповнюються, зʼєднання вичерпуються, клієнти починають робити повторні спроби — і за кілька хвилин у вас класичний «системний біль»: усе горить і тоне одночасно. Саме від цього захищають чотири патерни (шаблони), про які ми сьогодні говоримо:

  • Timeouts — ми ніколи не чекаємо вічно.
  • Circuit breaker — ми не бʼємося в зачинені двері.
  • Bulkheads — ми будуємо «відсіки» й не даємо потонути всьому кораблю.
  • Захист від штормів вебхуків — ми визнаємо, що вебхуки приходять із дублікатами, сплесками та повторними спробами, і готуємося до цього.

2. Timeouts: ми не чекаємо вічно

Що таке тайм-аут (timeout) і чому без нього все погано

Timeout — це максимальний час, який ваш код готовий чекати на відповідь від залежності: бази даних, MCP-сервера, зовнішнього HTTP API, моделі. Якщо відповідь не прийшла за заданий час, ми вважаємо виклик невдалим, звільняємо ресурси й повертаємо зрозумілу помилку або fallback.

Без тайм-аутів запити можуть:

  • нескінченно висіти в очікуванні,
  • займати зʼєднання та пул потоків,
  • блокувати наступні запити,
  • спричиняти каскадні відмови.

Патерн простий: «краще передбачувана відмова через 35 секунд, ніж незрозуміла тиша 5 хвилин».

Варто памʼятати, що тайм-аути є на кількох рівнях:

  • на рівні проксі/балансувальника (Cloudflare, Nginx),
  • на рівні MCP Gateway (HTTP-клієнти до мікросервісів),
  • у самих сервісах (виклики до БД, зовнішніх API, LLM).

Для ChatGPT загалом варто прагнути, щоб повний час tool-call був у діапазоні 510 секунд для звичайних операцій і максимум 2030 секунд для особливо важких. Усе, що довше, — майже гарантовано поганий користувацький досвід.

Простий fetchWithTimeout у TypeScript

Почнемо з практики. У GiftGenius MCP Gateway є допоміжний HTTP-клієнт, який звертається до сервісу підбору подарунків, до commerce-сервісу та до аналітики. Обгорнемо стандартний fetch у функцію з тайм-аутом:

// src/gateway/httpClient.ts
export async function fetchWithTimeout(
  url: string,
  opts: RequestInit & { timeoutMs?: number } = {}
) {
  const { timeoutMs = 5000, ...rest } = opts;
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

  try {
    return await fetch(url, { ...rest, signal: controller.signal });
  } finally {
    clearTimeout(timeoutId);
  }
}

Тепер у коді Gateway ми не робимо «голий» fetch, а використовуємо лише цю допоміжну функцію:

// src/gateway/giftClient.ts
import { fetchWithTimeout } from "./httpClient";

export async function callGiftService(path: string) {
  const res = await fetchWithTimeout(
    process.env.GIFT_SERVICE_URL + path,
    { timeoutMs: 4000 }
  );

  if (!res.ok) {
    throw new Error(`gift_service_${res.status}`);
  }
  return res.json();
}

Такий підхід гарантує: навіть якщо gift-сервіс зависне, за 4 секунди ми обірвемо зʼєднання й зможемо віддати MCP-помилку в ChatGPT, а не тримати зʼєднання до останнього.

Де саме ставити тайм-аути в GiftGenius

У нашому прикладі GiftGenius:

  • На рівні Gateway: тайм-аути на виклики Gift REST API, Commerce REST API, Analytics Service / REST API.
  • Усередині цих сервісів: тайм-аути на виклики до БД, ACP/платіжних сервісів, зовнішніх рекомендаційних API.
  • На вході в Gateway: загальний тайм-аут запиту від ChatGPT, щоб tool-call не перетворювався на «вічний спінер».

Важливо, щоб час очікування на верхньому рівні був трохи більший, ніж на внутрішніх. Наприклад, якщо Gateway чекає бекенд 5 секунд, а бекенд чекає БД 3 секунди, у нас залишається запас на обробку й серіалізацію результату.

Як пояснювати тайм-аути моделі ChatGPT

Для ChatGPT важливо повертати семантичні помилки, а не мовчки рвати зʼєднання. Замість абстрактного 500 краще повернути структуровану MCP-помилку, яку модель зможе озвучити користувачеві: «Сервіс підбору подарунків зараз перевантажений, спробуйте ще раз трохи пізніше» — і так далі.

Це означає, що в Gateway під час тайм-ауту треба:

  1. Спіймати AbortError або наш timeout_….
  2. Сформувати MCP-відповідь зі змістовним кодом і коротким описом.
  3. Дати моделі можливість вирішити, як пояснити це людині.

Тайм-аути розвʼязують проблему завислих запитів. Але якщо залежність почала масово падати, вони не врятують від лавини однакових невдалих спроб. Тут потрібен наступний рівень захисту — circuit breaker.

3. Circuit breaker: «автомат» проти сервісів, які «вмирають»

Інтуїція: чому одного тайм-ауту замало

Ми вже навчилися обмежувати час очікування окремих викликів за допомогою тайм-аутів. Тайм-аут захищає один конкретний виклик. Але якщо залежність остаточно «померла» (наприклад, commerce-сервіс падає через OOM (Out Of Memory) на кожному запиті), ми все одно продовжимо до неї звертатися: щоразу чекатимемо 35 секунд, ловитимемо помилку, навантажуватимемо мережу й CPU — і знову чекатимемо.

Circuit breaker (автомат) додає «памʼять»: він відстежує помилки й тайм-аути та, коли їх стає надто багато, перестає взагалі надсилати запити до цього сервісу. Натомість він одразу повертає швидку відмову або fallback. А через певний час обережно пробує знову в режимі half-open.

Класичні стани автомата:

  • Closed — усе нормально, запити проходять.
  • Open — сервіс вважається «мертвим», запити не йдуть, одразу повертається помилка.
  • Half-open — пробуємо обмежену кількість запитів; якщо вони успішні — повертаємося до closed, якщо знову впали — знову до open.

Проста схема circuit breaker

Невелика діаграма:

stateDiagram-v2
    [*] --> Closed
    Closed --> Open: занадто багато помилок
    Open --> HalfOpen: минув cooldown
    HalfOpen --> Closed: кілька успіхів поспіль
    HalfOpen --> Open: знову помилки
    Open --> Open: швидка відмова

Міні-реалізація circuit breaker у TypeScript

У реальних проєктах зазвичай використовують готові бібліотеки (для Node.js є, наприклад, opossum або легкі саморобні рішення). Але щоб зрозуміти механіку, достатньо компактного класу.

Дуже спрощений приклад breaker навколо виклику commerce-модуля:

// src/gateway/circuitBreaker.ts
type State = "closed" | "open" | "half-open";

export class CircuitBreaker {
    private state: State = "closed";
    private failureCount = 0;
    private nextAttemptAt = 0;

    constructor(
        private readonly failureThreshold = 5,
        private readonly cooldownMs = 30_000
    ) {}

    async call<T>(fn: () => Promise<T>): Promise<T> {
        const now = Date.now();

        if (this.state === "open") {
            if (now < this.nextAttemptAt) {
                throw new Error("circuit_open");
            }
            this.state = "half-open";
        }

        try {
            const result = await fn();
            this.onSuccess();
            return result;
        } catch (err) {
            this.onFailure();
            throw err;
        }
    }

    private onSuccess() {
        this.failureCount = 0;
        this.state = "closed";
    }

    private onFailure() {
        this.failureCount++;
        if (this.failureCount >= this.failureThreshold) {
            this.state = "open";
            this.nextAttemptAt = Date.now() + this.cooldownMs;
        }
    }
}

І використання в клієнті commerce-сервісу:

// src/gateway/commerceClient.ts
const commerceBreaker = new CircuitBreaker(3, 20_000);

export async function callCommerce(path: string) {
    return commerceBreaker.call(async () => {
        const res = await fetchWithTimeout(
            process.env.COMMERCE_URL + path,
            { timeoutMs: 3000 }
        );
        if (!res.ok) throw new Error(`commerce_${res.status}`);
        return res.json();
    });
}

Тут, коли commerce починає масово відповідати помилками або не встигає до тайм-ауту, після кількох невдач breaker переходить у open. У цьому стані протягом cooldownMs ми взагалі не намагаємося звертатися до сервісу й одразу повертаємо помилку circuit_open.

Що має бачити ChatGPT, коли breaker «відключив» сервіс

З точки зору ChatGPT краще, якщо ви:

  • Швидко відповідаєте MCP-помилкою «commerce_unavailable» або «gift_service_overloaded».
  • Додаєте зрозумілий опис: «Сервіс оплати тимчасово недоступний, давайте спробуємо пізніше».
  • Не ховаєте помилку за нескінченними повторними спробами.

Це якраз той випадок, коли «швидка, чесна відмова» краща за довге підвисання. Особливо під час checkout (оформлення замовлення): користувач швидше переживе зрозуміле повідомлення, ніж 40 секунд дивитиметься на спінер і отримає «щось пішло не так».

Тайм-аути й breaker захищають нас від «поганих» або недоступних залежностей. Але вони не розвʼязують проблему, коли один тип навантаження зʼїдає всі ресурси й починає душити інші частини системи. Для цього потрібен ще один шар — bulkheads.

4. Bulkheads: ізоляція «відсіків», щоб один не потопив увесь корабель

Аналогія з кораблем

Патерн bulkhead названий на честь перебірок у кораблі: якщо в одному відсіку пробоїна, вода не розтечеться по всьому кораблю. В архітектурі це означає: розділити ресурси між різними напрямами роботи, щоб один перевантажений сервіс не «зʼїв» усе — CPU, зʼєднання, пули — і не зламав критичні шляхи.

У мікросервісах це зазвичай роблять через окремі:

  • пули HTTP-зʼєднань,
  • пули потоків/воркерів,
  • черги/топіки,
  • навіть окремі БД-кластери для критично важливих операцій.

Ідея проста: якщо сервіс рекомендацій подарунків починає працювати повільніше й підгальмовувати, він вичерпає лише свої ресурси, але не зламає checkout і авторизацію.

Bulkheads у світі Node.js і MCP Gateway

У Node.js немає потоків у класичному сенсі (є event loop і воркери), але ми можемо обмежувати кількість паралельних завдань для кожного напряму.

Приклад: у Gateway є три зовнішні залежності:

  • Gift-сервіс (підбір подарунків, важкі LLM-виклики).
  • Commerce-сервіс (checkout, ACP).
  • Analytics-сервіс (логування подій).

Ми можемо запровадити прості ліміти на одночасні запити до кожного з них.

Наприклад, невеликий «семафор» для обмеження паралельності:

// src/gateway/bulkhead.ts
export class Bulkhead {
    private active = 0;
    private queue: (() => void)[] = [];

    constructor(private readonly maxConcurrent: number) {}

    async run<T>(fn: () => Promise<T>): Promise<T> {
        if (this.active >= this.maxConcurrent) {
            await new Promise<void>((resolve) => this.queue.push(resolve));
        }
        this.active++;

        try {
            return await fn();
        } finally {
            this.active--;
            const next = this.queue.shift();
            if (next) next();
        }
    }
}

І використання для сервісів:

// src/gateway/clients.ts
import { Bulkhead } from "./bulkhead";

const giftBulkhead = new Bulkhead(10);      // до 10 паралельних
const commerceBulkhead = new Bulkhead(3);   // чекаут суттєво обмежений
const analyticsBulkhead = new Bulkhead(50); // можна багато

export async function callGiftWithBulkhead(fn: () => Promise<any>) {
    return giftBulkhead.run(fn);
}

export async function callCommerceWithBulkhead(fn: () => Promise<any>) {
    return commerceBulkhead.run(fn);
}

Таким чином, навіть якщо GPT масово попросить «зроби мені 30 складних підборів подарунків», вони виконуватимуться максимум по 10 одночасно. А checkout зможе продовжувати працювати, використовуючи свій окремий ліміт.

GiftGenius: які «відсіки» ми хочемо

У GiftGenius розумно зробити окремі «відсіки» для:

  • підбору подарунків (LLM-важкі, менш критичні — їх можна сповільнювати),
  • checkout/ACP (дуже критично, тож треба захищати максимально),
  • аналітики/логів (важливо, але затримки можна певний час терпіти).

У більш просунутій архітектурі ви ще й розгортаєте їх як різні кластери з окремими ресурсами. Але в межах цієї лекції важлива саме ідея: не дозволяти другорядним фічам «зʼїсти» весь кисень.

Усі три ці патерни — тайм-аути, circuit breaker і bulkheads — стосуються того, як ви ходите назовні, до своїх залежностей. Але є ще один клас загроз стійкості: вхідні потоки подій, які можуть «завалити» вас навіть за ідеально налаштованих вихідних викликів. Найтиповіший приклад — шторми вебхуків.

5. Шторми вебхуків: коли світ надсилає вам події частіше, ніж ви готові

Як поводяться вебхуки в реальності

Четверте джерело проблем зі стійкістю — вхідні події: вебхуки від ACP, Stripe та інших систем. Саме вони можуть влаштувати справжній «шторм», навіть якщо у вас уже налаштовані тайм-аути, circuit breaker-и та bulkhead-и.

Вебхуки — це не HTTP-запит «на вимогу», а push-події від зовнішніх систем (Stripe, ACP, зовнішні магазини тощо). Вони мають кілька неприємних властивостей:

  • Доставка щонайменше один раз (at-least-once) — отже, дублікати неминучі.
  • Порядок доставки не гарантується.
  • У разі помилок вони люблять робити повторні спроби: спочатку через секунду, потім через 10 секунд, потім через хвилину… доки ви не відповісте 2xx.
  • У піку (наприклад, на розпродажі) вони приходять пачками, створюючи «шторм».

Якщо ваш обробник неідемпотентний і працює надто довго, він стає вузьким місцем: уся черга забивається, а повторні спроби лише підсилюють шторм. У результаті ви можете «покласти» базу, чергу, пул воркерів — а за ними, ланцюжком, і решту системи.

Базові принципи захисту від штормів

Є кілька ідей, які помітно підвищують шанси «пережити» шторм:

По-перше, queue-first, process-later. В ідеалі вхідний вебхук не має синхронно виконувати важку роботу. Замість цього він якнайшвидше перевіряє підпис/формат, кладе завдання в чергу й відповідає 200 OK. Обробка йде асинхронно у воркері. Якщо вам потрібне «швидке підтвердження» для ChatGPT, ви можете тримати окремий контур сповіщень.

По-друге, ідемпотентність обробника. Повторний вебхук за тією самою операцією не має «створити замовлення ще раз» або «списати гроші двічі». Зазвичай це розвʼязують зберіганням ключа ідемпотентності або eventId і перевіркою, чи обробляли ми вже цю подію.

По-третє, rate limiting і circuit breaker на стороні приймача. Навіть якщо відправник «штормить», ви можете:

  • обмежити RPS за IP/підпискою/кінцевою точкою (endpoint),
  • тимчасово віддавати 429 або 503, щоб уповільнити повторні спроби,
  • використовувати breaker, щоб не «лити» потік у зламаний downstream (наприклад, у БД замовлень).

Приклад Next.js-обробника вебхука в GiftGenius

Припустімо, що в нас є ACP/платіжний сервіс, який надсилає вебхук про статус замовлення в POST /api/commerce/webhook. Ми хочемо:

  • швидко прийняти подію й покласти її в чергу,
  • не обробляти її синхронно,
  • не зламатися через дублікати.

Спрощений приклад (без перевірки підпису й реальної черги — це буде в модулях про безпеку та черги):

// app/api/commerce/webhook/route.ts
import { NextRequest, NextResponse } from "next/server";

// Тут у нас могла б бути Redis/черга, поки імітуємо масив
const inMemoryQueue: any[] = [];
const processedEvents = new Set<string>(); // ідемпотентність (для демо)

export async function POST(req: NextRequest) {
    const event = await req.json();

    const eventId = event.id as string;
    if (processedEvents.has(eventId)) {
        return NextResponse.json({ ok: true, duplicate: true });
    }

    // В реальності тут буде перевірка підпису і схеми

    inMemoryQueue.push(event); // кладемо в чергу для фонової обробки
    // Фоновий воркер пізніше обробить і позначить ID як оброблений
    return NextResponse.json({ ok: true });
}

Поки це псевдореалізація, але важливі два моменти:

  1. Синхронна частина має бути максимально легкою.
  2. Ми закладаємо ідемпотентність довкола event.id.

У реальному житті ви будете:

  • використовувати зовнішню чергу (SQS, RabbitMQ, Kafka),
  • зберігати оброблені події в БД,
  • перевіряти підпис вебхука й версію payload-у,
  • можливо, застосовувати окремий Bulkhead/Breaker довкола обробника.

Як це виглядає в контексті GiftGenius

Для GiftGenius, інтегрованого з ACP/Stripe через вебхуки, захист від штормів особливо важливий у пікові сезони (Новий рік, Чорна пʼятниця). У ці періоди подій багато:

  • створення intent-ів,
  • підтвердження платежів,
  • скасування,
  • повернення.

Якщо ваш обробник почне «розростатися» (наприклад, через запити до зовнішнього API), ви ризикуєте тим, що:

  • ACP почне робити повторні спроби,
  • події прийдуть пачками,
  • БД замовлень і пул воркерів будуть забиті.

Патерн «queue first» + ідемпотентність + rate limiting на вході якраз і слугує страховкою від таких сценаріїв.

6. Як ці патерни працюють разом

Тепер зберемо всі ці патерни в один сценарій і подивімося, як вони працюють у реальному ланцюжку «Підбери подарунок і одразу оформи замовлення».

Розглянемо ланцюжок «ChatGPT → Gateway → Gift Service → Commerce → вебхуки» на прикладі сценарію:

Користувач у чаті каже: «Підбери подарунок і одразу оформи замовлення».

  1. Модель вирішує викликати ваш tool suggest_and_checkout.
  2. Gateway викликає gift-сервіс через fetchWithTimeout і bulkhead gift-сервісу.
  3. Якщо gift-сервіс завис, спрацьовує тайм-аут; breaker довкола нього після певної кількості помилок перейде в open, і наступні запити одразу отримуватимуть MCP-помилку «gift_service_unavailable».
  4. Якщо gift-сервіс відповідає, Gateway викликає commerce-сервіс (знову з тайм-аутом і окремим bulkhead).
  5. Будь-які проблеми з commerce викликають окремий circuit breaker, налаштований суворіше, ніж у gift (бо checkout критичний).
  6. Успішне замовлення призводить до вебхука від ACP у ваш /api/commerce/webhook, який кладе подію в чергу й швидко відповідає; фонові воркери обробляють оплату, а повторні вебхуки з тим самим eventId ігноруються як дублікати.

У підсумку:

  • Сервіс підбору, що зависає, не «кладе» checkout.
  • Commerce, що зависає, не перетворює всі tool-calls на хвилинний спінер — ChatGPT швидко отримує змістовну помилку.
  • Шторми вебхуків не ламають ваш основний HTTP-контур.
  • Ви контролюєте точки деградації: краще тимчасово вимкнути персоналізовані рекомендації, ніж «звалити» оплату.

7. Невеликий практичний чекліст для вашого App (у розповідній формі)

Якщо узагальнити, у типовому ChatGPT App з MCP/Gateway має сенс послідовно пройтися такими питаннями.

Спочатку перевірте, чи є тайм-аути на всіх зовнішніх викликах. Увесь код fetch, запити до БД і до LLM мають використовувати обгортку на кшталт fetchWithTimeout з прийнятними значеннями. Важливо, щоб не було місць, де запит може висіти нескінченно.

Далі визначте найкрихкіші залежності. Як правило, це платіжні сервіси, ACP, великі зовнішні API й іноді ваша ж БД замовлень. Навколо них варто додати circuit breaker, щоб захиститися від лавини повторних запитів у вже «мертвий» сервіс. Водночас одразу вирішіть, як ChatGPT поводитиметься, коли breaker у стані open.

Після цього подивіться на ресурси як на «відсіки». Чи все у вас іде через один connection pool і один пул воркерів? Чи критичні операції (вхід, checkout) мають власні обмеження паралельності, незалежні від сервісу рекомендацій та аналітики? Якщо ні — додайте найпростішу реалізацію bulkhead-ів, хоча б як грубий ліміт паралельних завдань.

Нарешті проведіть аудит усіх вхідних вебхуків. Перевірте, чи є в них idempotency key або eventId, чи не намагаєтеся ви виконувати важку роботу синхронно в HTTP-обробнику, і чи вмієте пережити хвилю повторних спроб, якщо ваш downstream тимчасово «впаде». Якщо ні — перенесіть логіку в чергу й фонові воркери.

Така послідовність кроків дає дуже відчутне зростання стійкості навіть без надскладної інфраструктури.

8. Типові помилки під час роботи з timeouts, circuit breakers, bulkheads і штормами вебхуків

Помилка № 1: відсутність тайм-аутів «десь унизу».
Розробники часто ставлять тайм-аут лише на Gateway або лише на фронтенд, забуваючи, що всередині бекенду є ще БД, зовнішні API і LLM. У підсумку зовнішній запит начебто має тайм-аут 5 секунд, але всередині один виклик до БД або платіжного сервісу може висіти хвилинами. Це блокує пул зʼєднань і спричиняє каскадні відмови.

Помилка № 2: гігантські тайм-аути «про всяк випадок».
Іноді ставлять тайм-аут 60120 секунд: «нехай уже дотягне». У контексті ChatGPT це майже завжди погано. Користувач іде, модель починає галюцинувати, а ваші ресурси весь цей час заблоковані. Набагато краще — чесна відмова через 510 секунд із зрозумілим описом.

Помилка № 3: circuit breaker без продуманого UX.
Іноді breaker додають «для формальності», але коли він спрацьовує, користувачеві або моделі повертається незрозумілий 500, «ECONNREFUSED» або «axios error». У підсумку GPT не може адекватно пояснити, що відбувається, і починає вигадувати. Варто одразу продумати формулювання помилок, які будуть зрозумілі і людям, і моделі.

Помилка № 4: змішування ресурсів без підходу bulkhead.
Класичний сценарій: один сервіс рекомендацій (або аналітики) починає гальмувати, «зʼїдає» весь пул зʼєднань до БД або пул потоків — і слідом за ним «падають» checkout і вхід. Усе тому, що ресурси не розділені. Відсутність бодай якогось bulkhead-підходу призводить до того, що другорядна фіча може «покласти» весь продакшн.

Помилка № 5: обробка вебхуків як звичайних запитів.
Новачки часто пишуть вебхук-обробник так само, як звичайний контролер: довга бізнес-логіка, запити до сторонніх API, відсутність ідемпотентності. В умовах повторних спроб і дублікатів це призводить до подвійної обробки подій, дивних станів замовлень і падінь під навантаженням під час шторму.

Помилка № 6: ігнорування ідемпотентності в commerce-сценаріях.
Особливо небезпечно, коли вебхук оплати може створити замовлення ще раз або повторно змінити його стан. Без перевірки idempotency key і зберігання статусу обробки події ви рано чи пізно отримаєте подвійне списання або дивні дублікати замовлень.

Помилка № 7: спроба полагодити все setTimeout-ами і «магічними затримками».
Іноді хочуть обійти race condition і проблеми шторму через «почекати 100 мс — і буде нормально». На практиці це робить поведінку ще нестабільнішою й жодним чином не захищає від реальних збоїв. Правильний шлях — явні тайм-аути, circuit breaker, черги та ідемпотентність, а не «шаманство» із затримками.

Помилка № 8: відсутність пріоритизації критичних шляхів.
Коли checkout і вхід живуть у тих самих лімітах, що й аналітика або рекомендаційна логіка, будь-яке перевантаження може однаково «покласти» і критичні, і другорядні частини. У стійкому дизайні checkout і автентифікація — найкритичніші: для них потрібні окремі ресурси, окремі ліміти, окремі алерти й SLO.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ