Привіт! Мене звати Андрій Годяцький і я працюю Senior Software Engineer PHP в українській продуктовій IT-компанії Appflame. У цій статті я хочу розповісти про два кейси реіндексу/ремапінгу або, як заведено в SQL, міграції:
Стаття буде цікава тим, хто працює з ElasticSearch та базами користувачів під великим навантаженням, де дуже важлива актуальність інформації та її постійне оновлення. А також тим, у кого є необхідність мати стабільну робочу базу без просадок/даунтаймів/зупинок, щоб можна було зробити бажану структуру індексу в процесі роботи без жодних відображень на клієнтській частині.
В цій статті я розповідатиму про два кейси реіндексу в нашому додатку для знайомств Hily. Він налічує близько 25 мільйонів користувачів у світі й працює на ринках Tier 1
В певний момент виникла продуктова необхідність змінити структуру даних однієї секції та певних типів даних в ній. Для вирішення цих завдань нам потрібно було зробити реіндекс.
Під час запуску першого реіндексу нам потрібно було зупинити всі наші воркери, які постачали дані в ElasticSearch, оновлювали їх та записували туди нову інформацію. Через це ми зіштовхнулися з такими проблемами:
В результаті такої операції, ми отримали просадку близько 20% практично по всіх продуктових метриках, виросли дублікати дій (івент, який повторювався декілька разів через те, що тимчасово не оновлювалися дані в ElasticSearch), з’явився ризик втратити деяких користувачів — користувач зайшов, не побачив релевантних юзерів, не отримав бажаної активності, вийшов і видалив додаток.
Була ще одна проблема: ми не знали, скільки часу нам потрібно буде, щоб зробити реіндекс на проді. На проді у нас велика база — близько 25 мільйонів користувачів, а на стейджі
Якщо на стейджі ми запустили реіндекс і він відбувся за п’ять хвилин, то на проді цей процес зайняв близько двох годин!
Ми не могли просто взяти й стопнути його, тому що б тоді у нас вийшли одні індекси биті з уже переробленою структурою, а інші — зі старою структурою. У нас була б каша, яку потрібно було б «розхльобувати» повторним запуском операції реіндексу.
Коли завершився процес реіндексу, всі воркери знову почали працювати й дані почали оновлюватися. Але ці дві години залишилися з просадкою. З часом всі процеси відновилися й протягом декількох годин ми наздогнали всі метрики по користувачах.
Після того, як у нас відбулася просадка, постало питання: як нам у майбутньому робити реіндекс без просадок?
Ми знайшли для себе таке рішення: створити копію індексу з новою структурою
Я написав скрипт, який займався всім цим процесом: створив новий індекс та команди для ElasticSearch, який мав переливати дані зі старого індексу в новий, паралельно змінюючи/оновлюючи їх структуру відповідно до продуктових потреб.
Таким чином ми отримували дані зі старого індексу в новому та з новою структурою. Але що робити з даними, які прилітали у старий індекс, але вже були переміщені операцією реіндексу? На розв’язання такої задачі прийшло рішення дублювання операцій CUD у новий індекс. Така схема дозволила нам працювати без зупинки воркерів на CUD й дозволила зберегти максимальну актуальність даних у процесі реіндексу.
Коли ElasticSearch закінчив процес перелиття даних зі старого індексу в новий, то ми отримали абсолютно валідні дані в обох версіях й нічого не втратили. В цей момент ми виконали команду переключення alias. Система почала працювати з новим індексом і всі запити CRUD
Якщо раніше під час першого реіндексу операції «оновити/вставити/видалити» були призупинені та взагалі не відбулися (відбувалося тільки отримання даних), то у процесі з новим індексом всі операції виконувались під час процесу реіндексу.
Для нових та існуючих користувачів все працювало так як потрібно. Якщо під час реіндексу існуючий користувач отримав активність, змінював свої дані — алгоритм отримував всю інформацію й враховував її.
Таким чином під час другого реіндексу ми змогли зберегти актуальність даних та релевантність видачі користувачів.
Та ми вирішили піти далі й подумали: як ми можемо оптимізувати швидкість процесу реіндексу?
В ElasticSearch є можливість конфігурувати (Slicing), скільки процесів реіндексу він запустить для того, щоб переливати дані зі старого індексу в новий. Ми нарізали більше процесів, завдяки чому швидкість збільшилася й перелиття даних відбулося не за дві години, а за годину.
В ElasticSearch є таке поняття як версії документа. Наприклад, якщо користувач зареєструвався в нашому додатку, створюється документ з версією 1. Цей документ містить всю інформацію, яку ввів користувач (ім’я, вік, вподобання тощо). Якщо цей же користувач оновив або додав інформацію, створюється документ з версією 2, 3 і т.д.
В момент переливання (реіндексу) ми могли зіштовхнутися з конфліктом версій — це коли ElasticSearch намагається вставити документ старішої версії (наприклад, 4), а в базі вже існує нова версія (5). Це може відбутися тоді, коли ми дублюємо всі процеси в новий індекс.
Оскільки конфлікт версій обриває процес реіндексу, викидаючи помилку, то нам варто уникати таких випадків, так як процес потрібно буде починати знову.
Для вирішення цього питання ми переключили опцію conflicts у значення proceed, що дає змогу продовжити процес без переривання, ігноруючи цей конфлікт. Для нас ігнорування ніяким чином не відобразилось на актуальності даних, так як новіший документ мав всю необхідну актуальну інформацію.
Коли ми перелили всі дані зі старого індексу в новий й повністю переключилися в роботі на нього, старий індекс залишився неактивним.
Ми перевірили, щоб продуктові метрики були в нормі, щоб не було жодних просадок після перелиття і жодних проблем із новим індексом, після чого видалили старий індекс.
Перемикатись назад на старий індекс і відповідно зберігати його є сенс тільки тоді, коли операція реіндексу не відбулася, і цей процес обірвався з якоїсь технічної причини.
До початку і в процесі другого реіндексу ми слідкували за:
Наша бекенд-команда слідкувала за цими моніторами в момент реіндексу, щоб у випадку виникнення проблеми вимкнути процес реіндексу, зрозуміти, що це за проблема, та швидко пофіксити її. Монітори дуже допомагають нам жити й тримати наш додаток у нормальному робочому режимі.
Наші девопси підняли окремий сервер, де вони розгорнули ElasticSearch з усією базою. Це було повністю налаштоване тестове середовище схоже на наш бекенд на проді, той, на якому в нас зараз працює додаток.
Я написав скрипти, які навантажували ElasticSearch, імітуючи операції CRUD подібні проду на наше тестове середовище. Таким чином ми створили навантаження схоже із продакшен-середовищем.
В цей час під цим навантаженням я запустив процес реіндексу. Він виконався, і ми змогли побачити, що все окей. На цьому етапі ми ще спіймали пару нюансів і помилок, які пофіксили. Це тестове середовище дало нам гарне розуміння, що ми можемо їхати на продакшен, що у нас все добре пройшло.
Я не можу сказати, що це звичний для нас підхід, ми такі операції робимо доволі рідко. Це не дешеве задоволення — потрібно на певний час орендувати великі, потужні сервери й розгорнути та налаштувати там середовище. Але саме завдяки такому тестуванню можна зрозуміти, яку оптимізацію отримаєш на продакшені, зловити пару неявних багів і переконатись, що можна їхати на прод, адже система працює чітко навіть під навантаженням.
Тут ключовий момент під навантаженням. Річ у тім, що на своїх локальних комп’ютерах і навіть на звичайних тестових середовищах ми не можемо створити таке сильне навантаження — вони не мають стільки потужності, щоб витримувати такі важкі умови. До цього я не чув і не бачив, щоб у компаніях створювали окреме середовище для тестування під навантаженням. Це був класний досвід.
Ми запускали скрипт у час, коли у нас найменше завантаження на наших серверах: для нас це 12 година дня за київським часом. Коли відбувався другий реіндекс було дуже класно бачити в порівнянні з першим разом, що всі бізнесові метрики йдуть рівненько, що наші користувачі використовують додаток, не помічаючи, що в нас щось відбувається, що для них все виглядає так само як і раніше.
Ну і процес відбувся вдвічі швидше: ми зробили реіндекс за годину, при цьому ніяких просадок метрик не отримали. Все було тихо, класно, чисто.
Зараз, порівнюючи перший і другий реіндекс, я міг би поділитися декількома порадами.
Якщо перед вами постала складна задача, це ще не означає що цей виклик потрібно прийняти. Можливо, є інші, простіші рішення, а можливо цей складний шлях взагалі надуманий, непотрібний і не зробивши цей крок ви геть нічого не втратите й навпаки заощадите купу ресурсів. Ваші витрачені зусилля мають бути виправдані отриманим результатом. Переконаний, що це стосується всього — вас особисто чи бізнесу в якому ви працюєте.
Буває так, що покриття тестами (smoke, acceptance, unit, integration, manual, automation тощо) — недостатнє, і реальну картинку можна побачити лише під навантаженням подібному реальному середовищу.
Дуже круто, якщо є можливість зробити тестове середовище наближене до проду й на ньому поганяти складні операції. Не проводити тест і вирішувати помилки після їх прояву на проді часто виходить в рази дорожче.
Дуже ймовірно що у вас з’явиться простіше рішення або зникне необхідність виконання такої задачі, бо знайдеться інше рішення. У такому випадку ви дослухаєтеся до нашої першої поради 🙂 Також ви зможете врахувати конфлікт версій, подумати, як можна оптимізувати швидкість реіндексу і що ще можна зробити ефективніше.
Поділіться, будь ласка, своїм досвідом: як ви робите реіндекс? Також було б цікаво дізнатися, як ви проводите тестування навантажених систем і складних операцій до того, як залити код в прод. Чи є у вас тестове середовище і як ви розумієте, що все пройде окей?
Блогер та розробник Джозеф Круз розповів, чому не варто писати ідеальний код та чому це…
Днями я завзято нила про щось ChatGPT (експериментую між сеансами з живим терапевтом). І от…
«Крутіть колесо, щоб отримати знижку до 50%!» «Натисніть тут, щоб відкрити таємничу пропозицію!» «Зареєструйтесь зараз,…
Дуже хочеться робити якісь десктопні апки. Сумую за часами коли всі програми були offline-first, і…
Надсилаючи криптовалюту, багато новачків ставлять запитання: як працюють комісії та чому вони відрізняються в різних…
Нова афера набирає обертів — ось детальний розбір того, як фальшиві потенційні роботодавці намагаються вкрасти…