Модуль 11

TTL та автоматичне видалення ключів

Ось готовий урок, створений спеціально для тебе в стилі Девіда Малана (CS50). Уяви, що ми зараз в аудиторії Sanders Theatre, я ходжу сценою, активно жестикулюю, а на екрані за мною — величезний код. Поїхали! 🚀


🎓 CS50: TTL (Time To Live) та мистецтво забувати

Привіт, друзі! 👋

1. 🔥 Вступ: Проблема вічного сміття

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

Проходить тиждень. Нова вечірка. Нові речі. Старі все ще лежать. Минає місяць. У вашій квартирі вже неможливо пройти, бо вона завалена чиїмись забутими парасольками та пакетами.

Риторичне запитання: Що станеться з вашим сервером (вашою квартирою), якщо ви будете зберігати дані (речі) вічно, навіть коли вони вже нікому не потрібні? Правильно: у вас закінчиться пам’ять (RAM), і сервер впаде. Boom. 💥

А тепер перенесімося в реальний світ розробки: * Вам прийшов код для входу через SMS. Він потрібен лише 5 хвилин. Чи треба зберігати його рік? * Ви закешували курси валют. Чи будуть вони актуальні завтра? * Користувач додав товар у кошик, але не купив і пішов. Тримати цей кошик вічно?

Без механізму автоматичного видалення ми перетворимо нашу базу даних на звалище. Нам потрібен спосіб сказати даним: "Ти живеш рівно 10 хвилин, а потім — зникаєш без сліду".

Саме тут на сцену виходить TTL.


2. 🧠 Теоретична база: Як це працює?

Що таке TTL? Це абревіатура від Time To Live (Час життя). Це лічильник, таймер зворотного відліку, який ми прикріплюємо до конкретного ключа (даних).

Уявіть це як наліпку з терміном придатності на пакеті молока 🥛. Як тільки дата настає — молоко (дані) вважається зіпсованим і викидається.

Що відбувається "під капотом"? ⚙️

(На прикладі найпопулярнішого інструменту для цього — Redis)

Коли ви кажете базі даних: "Збережи цей ключ на 10 секунд", вона не запускає окремий таймер для кожного ключа (це було б занадто дорого для процесора). Натомість використовується дві стратегії:

  1. Ліниве видалення (Lazy Expiration): Уявіть бібліотекаря. Ви просите книгу. Бібліотекар бере її з полиці, дивиться на дату повернення. Якщо дата минула — він каже: "Ой, ця книга списана", викидає її прямо при вас і каже, що книги немає. Тобто: дані видаляються в момент, коли хтось намагається їх прочитати.

  2. Активне видалення (Active Expiration): Уявіть прибиральника, який раз на секунду випадковим чином перевіряє 20 книг на полицях. Якщо знаходить прострочені — викидає. Тобто: база даних у фоновому режимі потроху підчищає сміття.

👉 Що треба запам'ятати залізно: TTL гарантує, що ви не отримаєте старі дані. Або ви отримаєте дані, або null (нічого), якщо час вийшов.


3. 🧪 Приклади: Від магії до реальності

Давайте подивимось на код. Ми будемо використовувати синтаксис Redis, бо він найзрозуміліший, але логіка однакова всюди.

Приклад 1: Місія нездійсненна 🕵️‍♂️

Уявіть, що ми надсилаємо секретне повідомлення.

> SET secret_code "007"
OK
> EXPIRE secret_code 5
(integer) 1

Ми створили ключ secret_code і сказали йому жити 5 секунд.

Питання до вас: Що я побачу, якщо зроблю запит GET secret_code через 2 секунди? Відповідь: "007"

Питання до вас: А що я побачу через 6 секунд? Відповідь: (nil) — тобто нічого. Пустота.


Приклад 2: SMS-код для входу 📱

Реальний кейс. Користувач хоче залогінитися. Ми генеруємо код 123456. Він дійсний тільки 60 секунд.

Замість двох команд (SET потім EXPIRE), ми робимо це однією атомарною (неподільною) командою SETEX:

> SETEX sms:user:380991234567 60 "123456"
OK

(Читається як: Set with Expiration -> ключ -> час -> значення)

Тепер, якщо користувач введе код через 61 секунду, наша програма спробує знайти ключ, отримає nil і скаже: "Вибачте, код недійсний, замовте новий". Без жодних if (created_at + 60 < now) у вашому коді! База зробила все за вас.


Приклад 3: Кешування важкого запиту 🐢➡️🐇

У вас є дашборд, який рахує складну аналітику продажів за рік. Це займає 5 секунд. Користувач оновлює сторінку 10 разів поспіль. Сервер "лягає".

Рішення: Ми рахуємо один раз, записуємо результат у кеш з TTL = 60 секунд.

  1. Приходить запит.
  2. Перевіряємо кеш. Є дані? Віддаємо миттєво (0.01 сек).
  3. Немає даних? Рахуємо (5 сек), записуємо в кеш, ставимо TTL, віддаємо.

Наступні 60 секунд всі користувачі отримують результат миттєво. Дані можуть бути трішки застарілими (на хвилину), але для аналітики це ок, а сервер — щасливий.


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

Час "забруднити руки" кодом! Уявіть, що ви працюєте в консолі (або просто запишіть рішення на папері).

Завдання 1: "Швидкопсувний товар" 🍞 Створіть ключ bread зі значенням fresh, який "зіпсується" (зникне) через 3 секунди. Перевірте, скільки часу йому лишилось жити командою TTL bread.

Завдання 2: "Блокування хакера" 🛡️ Користувач з IP 192.168.1.1 намагається підібрати пароль. Ви вирішили заблокувати його на 1 годину (3600 секунд). Напишіть команду, яка створює ключ block:192.168.1.1 зі значенням banned і правильним часом життя.

Завдання 3: "Помилка новачка" ⚠️ Ви виконали такий код: 1. SET session_id "xyz-123" 2. EXPIRE session_id 300 (чекаємо 5 хвилин) 3. Ой, треба оновити сесію! 4. SET session_id "new-data-456"

Питання: Чи збережеться таймер видалення (TTL) після 4-го кроку? Ану подумайте... (Підказка: Більшість баз даних скидають TTL при перезаписі значення).

Завдання 4: Міні-кейс "Купон на знижку" 🏷️ Ви розробляєте інтернет-магазин. Ви хочете дати користувачу знижку 10%, яка діє тільки до кінця сьогоднішнього дня. Як ви розрахуєте TTL в секундах, якщо зараз 14:00? (Просто опишіть логіку розрахунку).


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

Як відрізнити новачка від профі в роботі з TTL?

❌ Помилка новачка: Новачок думає, що дані в кеші/Redis — надійні. Він пише код так: "Візьми дані з кешу. Крапка." Коли TTL спливає, програма падає з помилкою NullPointerException або undefined.

✅ Мислення архітектора: Досвідчений розробник думає за патерном Cache-Aside: 1. Спробуй взяти з кешу. 2. ЯКЩО там пусто (TTL вийшов) —> піди в основну базу даних, візьми оригінал. 3. Поклади в кеш знову. 4. Віддай дані.

💡 Порада з практики: Ніколи не покладайтеся на те, що TTL спрацює мілісекунда в мілісекунду. Це не ядерний годинник. Це механізм очищення. Якщо ваша бізнес-логіка залежить від точності 0.001с — TTL не для цього.

Ще одна порада: Використовуйте Jitter (випадковий розкид). Якщо ви ставите TTL рівно 60 хвилин для 1 мільйона ключів одночасно, то через годину вони всі одночасно зникнуть, і 1 мільйон запитів полетить у вашу базу даних. База впаде ("Cache Stampede"). Рішення: Ставте TTL: 60 хв + випадкові 1-5 хвилин.


6. 🧩 Підсумок

Отже, друзі, що ми сьогодні зрозуміли?

  1. Пам'ять не гумова. Зберігати все вічно — дорого і небезпечно.
  2. TTL (Time To Live) — це наш автоматичний прибиральник.
  3. Ми використовуємо це для сесій, кешування, кодів безпеки та тимчасових блокувань.
  4. Дані з TTL — це "тимчасові" дані. Завжди майте план "Б", якщо ключа вже немає.

Тепер ви вмієте керувати часом життя інформації! Ви не просто зберігаєте дані, ви керуєте їхньою долею.

🔍 Тизер наступного уроку: Ми навчилися видаляти дані за часом. Але що робити, якщо пам'ять забилася повністю, а час TTL ще не вийшов? Кого викинути першим: найстарішого? Чи того, ким користуються найрідше? Наступного разу ми поговоримо про Стратегії витіснення (Eviction Policies): LRU проти LFU. Це буде справжня битва алгоритмів!

А на сьогодні це все. Це був CS50! 🏛️