Модуль 12

Result backend та зберігання результатів

Ось урок на тему "Result backend та зберігання результатів", написаний у стилі CS50: енергійно, з аналогіями та фокусом на розуміння суті.


🎓 CS50: Result Backend — Де ми загубили нашу відповідь?

Привіт, друзі! Радий бачити вас знову.

Сьогодні ми торкнемося теми, яка є критично важливою, коли ваші програми починають робити більше однієї справи одночасно. Ми вже знаємо про черги задач (Task Queues) і про те, як відправити важку роботу "на фон" (background), щоб не змушувати користувача чекати.

Але тут виникає одне дуже цікаве питання.


1. 🔥 Вступ: проблема та мотивація

Уявіть ситуацію. Ви — клієнт у хімчистці. Ви приносите свій улюблений костюм (це наша Задача або Task) і віддаєте його працівнику (це наш Брокер повідомлень). Працівник кидає костюм у величезний кошик, звідки його колись заберуть майстри прання (Воркери).

Ви йдете додому. Воркери працюють: перуть, сушать, прасують. Костюм готовий! Він чистий і свіжий.

Але стривайте... Де зараз костюм? Він у майстра в руках? Він лежить на підлозі? Чи майстер має бігати містом і шукати вас, щоб віддати його?

Якщо ми не домовилися про місце видачі, робота зроблена, але результат (чистий костюм) втрачено або він недоступний вам.

У програмуванні це виглядає так: 1. Ви відправили важку задачу: calculate_report.delay(). 2. Воркер її виконав. Отримав число 42. 3. Воркер каже: "Я закінчив!" і... число 42 зникає в нікуди, бо воркер просто переходить до наступної задачі.

Питання до вас: Як нам отримати це число 42 назад у нашу основну програму, якщо воркер і основна програма — це абсолютно різні процеси, які можуть бути навіть на різних серверах?

Нам потрібне "місце видачі". Нам потрібен Result Backend.


2. 🧠 Теоретична база (Що там під капотом?)

Давайте розкладемо це на атоми.

Що таке Result Backend?

Це спеціальне сховище (база даних, кеш, файл), яке слугує "дошкою оголошень" або "полицею видачі".

  1. Broker (Брокер): Відповідає за доставку задачі від вас до воркера.
  2. Worker (Воркер): Виконує роботу.
  3. Result Backend: Місце, куди воркер кладе результат (або помилку, якщо щось пішло не так).

Як це працює (Step-by-step):

  1. Клієнт: Відправляє задачу. Система видає унікальний Task ID (наприклад, uuid-1234). Це як номерок у гардеробі.
  2. Воркер: Бере задачу, пітніє, працює. Отримує результат.
  3. Збереження: Воркер бере цей результат і записує його в Backend під ключем uuid-1234.
  4. uuid-1234: "Ready: Result is 42"
  5. Клієнт: Коли йому цікаво, він стукає в Backend: "Ей, що там із uuid-1234?"
  6. Backend: "Ще робиться" АБО "Ось твій результат".

💡 Що треба запам’ятати залізно:

  • Result Backend — це не про чергу. Це про зберігання статусу та відповіді.
  • AsyncResult. Коли ви відправляєте задачу, ви миттєво отримуєте об’єкт AsyncResult. Це не сам результат, це "обіцянка" (promise) результату, за допомогою якої ви можете опитувати Backend.

Що використовують як Backend?

  • Redis / Memcached: Найпопулярніші. Чому? Бо вони працюють у пам'яті (RAM). Це дуже швидко. Результати часто потрібні ненадовго, тому зберігати їх у вічній базі даних не завжди є сенс.
  • Бази даних (PostgreSQL, MySQL): Можна, але повільніше. Підходить, якщо історія результатів важлива надовго.

3. 🧪 Приклади (від простого до реального)

Уявімо, що ми пишемо на Python (використовуючи Celery), бо це стандарт індустрії для таких задач. Але логіка однакова для будь-якої мови.

Приклад 1: Проста арифметика

Ви хочете додати два числа на іншому сервері (звучить дивно, але для прикладу — ідеально).

# tasks.py (Це код воркера)
@app.task
def add(x, y):
    return x + y  # Воркер повертає це значення
# main.py (Це ви, клієнт)
result = add.delay(4, 4) 
# В цей момент задача пішла в чергу.
# Змінна result — це НЕ 8. Це "квитанція" (AsyncResult).

Питання: Що буде, якщо я одразу напишу print(result)? Відповідь: Ви побачите щось типу <AsyncResult: a1b2c3d4...>, але не число 8.

Щоб отримати 8, нам треба звернутися до бекенду:

print(result.status) # Питаємо бекенд: "PENDING" (ще чекаємо)
# ... проходить секунда ...
print(result.get())  # Питаємо бекенд: 8! (Дістали з Redis)

Приклад 2: Генерація звіту (Реальний кейс)

У вас є кнопка "Завантажити звіт за рік". Це займає 30 секунд.

  1. Фронтенд робить запит: POST /generate-report.
  2. Бекенд запускає задачу і повертає фронтенду Task ID.
  3. Фронтенд показує спіннер ("Крутилку") і кожні 2 секунди питає сервер:
  4. "Задача id-123 готова?" -> "Ні, статус PROCESSING"
  5. "Задача id-123 готова?" -> "Ні, статус PROCESSING"
  6. "Задача id-123 готова?" -> "Так! Статус SUCCESS. Ось посилання на файл: /media/report.pdf"

Ось тут Result Backend зберігав цей статус і фінальне посилання.


4. 🛠 Практична частина

Час забруднити руки кодом (або псевдокодом).

Завдання 1: Налаштування Уявіть, що ви конфігуруєте Celery. Ви вказали Брокер (RabbitMQ). Що станеться, якщо ви не вкажете Result Backend, але спробуєте викликати result.get()? (Спробуйте здогадатися. Правильно: буде помилка або результат ніколи не прийде, бо воркеру нікуди його класти).

Завдання 2: "Асинхронний чекун" Напишіть функцію на псевдокоді, яка: 1. Відправляє задачу heavy_computation. 2. Не блокує програму (не використовує wait). 3. Кожну секунду перевіряє статус. 4. Якщо статус SUCCESS — друкує результат. Якщо FAILURE — друкує помилку.

Завдання 3: TTL (Time To Live) Ви використовуєте Redis як Result Backend. У вас мільйон задач на день. Проблема: Пам'ять Redis переповнилася через тиждень. Чому? Рішення: Що треба налаштувати, щоб результати зникали самі через 1 годину після виконання? (Шукаємо налаштування result expires).

Завдання 4: Міні-кейс "Прогрес-бар" Ми хочемо бачити смужку завантаження (0% -> 100%). Як Result Backend може допомогти тут? Підказка: Чи можемо ми оновлювати стан задачі в процесі виконання, записуючи туди не тільки фінал, а й проміжні дані (meta-data)?


5. 💡 Мислення як у розробника

Ось де новачки часто помиляються, а сеньйори хитають головою.

🚫 Помилка 1: Передача великих даних через Backend

Ви згенерували PDF на 100 МБ. Погано: Повернути бінарний код файлу через return (і записати 100 МБ у Redis). Добре: Зберегти файл на диск (S3, локальне сховище), а в Result Backend повернути лише шлях (string): "https://s3.../file.pdf". Чому? Result Backend має бути швидким і легким. Не забивайте його сміттям.

🚫 Помилка 2: "Забування" результатів

Якщо ви не забираєте результати (не робите .get() або .forget()) і не налаштували автовидалення (expiry), ваша база даних розпухне від старих результатів 5-річної давнини. Завжди налаштовуйте expiry time.

🚫 Помилка 3: Використання Result Backend як основної БД

Не треба зберігати там бізнес-дані. Це тимчасове сховище для результатів задач. Як тільки ви отримали результат і записали його в свою основну базу — забувайте про запис у Result Backend.


6. 🧩 Підсумок

Отже, що ми маємо сьогодні в сухому залишку?

  1. Broker передає задачі, але не зберігає відповіді.
  2. Result Backend — це "камера схову", де воркер залишає результат для клієнта.
  3. Ми отримуємо Task ID, за яким, як за номерком, забираємо результат пізніше.
  4. Ми не кладемо туди "слонів" (великі файли), ми кладемо туди "адреси слонів".

Тепер ви вмієте: не просто відправляти задачі в космос, а й отримувати зворотний зв'язок. Ви замкнули коло обміну даними!

Що далі? Добре, ми отримали результат. А що, якщо задача впала з помилкою? А якщо світло вимкнули, поки воркер працював? На наступному уроці ми поговоримо про Retry Policies (політики повторів) та Idempotency (ідемпотентність). Буде гаряче!

Це був CS50. Побачимось! 👋