Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni
🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!Пости з тегом #OpenSearch
13.05.2024
OpenSearch в тестовому оточенні
Як забезпечити кожному тесту чистий аркуш в OpenSearch? В теорії — просто. Проте щоб це ще й не гальмувало збірку, довелося попрацювати.
-
Зазвичай з PostgreSQL ми вживаємо транзакції. В OpenSearch транзакцій немає. Значить, просто скасувати зміни після тесту не вийде. Другий підхід — це команда
TRUNCATE
. Такої в OpenSearch теж немає; щоправда, є API “delete by query”, яким можна видалити з індексу всі документи, але вона не ефективна. -
Отже, залишається тільки видаляти цілі індекси та створювати наново. На жаль, створення індексів не така швидка операція, щоб робити її без потреби. Тому я порефакторив код запитів, щоб він допускав відсутність індексів (тобто інтерпретував як порожній результат.) Таким чином, індекси ми створюємо тільки тоді, коли в них пишуться дані. (А значить, і видаляти потрібно не так багато, а коли тест не звертається до OpenSearch - то взагалі нічого.)
-
До речі, на відміну від PostgreSQL, схема даних зберігається в шаблонах, а не в самих індексах. Тому створювати індекси можна навіть не окремою операцією, а неявно, під час додавання до індексу першого документу.
-
Нарешті, спочатку я видаляв індекси так, як чищу таблиці в PostgreSQL, тобто кожний індекс окремим викликом. Виявилося, що це повільно (і навіть паралелізація викликів не допомагає.) Тому перейшов до очищення всіх індексів за префіксом
test
. -
Ідея, якою я ще не скористався: для кожного тесту генерувати унікальний префікс для ізоляції даних. Тоді можна видаляти всі тестові дані наприкінці.
17.05.2024
Перевірка унікальності в OpenSearch
…А немає її! В ті чи інші причини. Певно, головним чином тому, що індексація відбувається асинхронно, а до індексації перевірки не буде.
Проте є один метод, перевірений часом. Це зробити унікальний ключ ідентифікатором. Таке працює в багатьох NoSQL базах, наприклад, в DynamoDB практично лягає в основу. Тільки з міркувань рівномірного розподілення значень в OpenSearch краще брати не прямо ключ (скажімо, URL сторінки), а хеш від нього.
(Взагалі підхід з використанням справжніх значень як ідентифікаторів називається “натуральні ключі” та був популярним в часи “голих” SQL баз. А з приходом ORM як головного методу доступу — занепав.)
Ідентифікатори в OpenSearch, звісно, унікальні. Але й тут криється нюанс - звичайна операція індексації не заборонить повторюваний ідентифікатор, а просто перезапише документ (тобто зробить “upsert”). А нам потрібна операція create
.
Я вже натрапляв на проблему в тестах, що згенеровані фабрикою документи час від часу збігалися ідентифікаторами, вставлялися без create
, та призводили до випадкових помилок.
01.06.2024
OpenSearch: база, де все відбувається не відразу
Нарешті зміг для себе зрозуміти, що мене понад усе дратує в роботі з OpenSearch: майже нічого в цій базі не відбувається синхронно. Я звик, що база даних є синхронною; звісно, реплікація займає час, але той вузол, з яким працюєш, все робить коли йому скажеш. В OpenSearch все не так.
Навіть з базовими діями — наприклад, індексацією одного документа — є спеціальний параметр refresh=wait_for
, який змусить виклик чекати, поки документ не стане проіндексований. За замовчуванням індексація буде відкладена. Це відразу помічаєш у тестах, бо без цього параметра вони ніколи не будуть стабільними.
А коли маєш справу зі складнішими операціями, взагалі краще думати про них як про процеси; в деяких випадках — буквально. Наприклад, в PostgreSQL масове оновлення (UPDATE ... WHERE
) теж може тривати навіть години, але таблиця може бути тільки у двох станах: не оновлена та оновлена. З OpenSearch масове оновлення опрацює скільки встигне та повернеться — далі запускай наново.
Так само і перетворення хоч виглядають, як матеріалізовані розрізи, але працюють сторінка за сторінкою, тобто результат завжди буде неповним: причому не в тому сенсі, що відповідати деякому старому стану вхідних даних — а бути десь посередині, причому ми ніколи не впевнені, де саме.
Все це добре, коли вас влаштовує кінцева узгодженість, а чи буде вона досягнена зараз, чи через хвилину — байдуже. Та це, певно, найбільша перешкода у використанні OpenSearch як традиційної бази даних.
12.02.2025
Оптимізація: треба йти глибше!
Далі, коли шукаєш причини уповільнення, мусиш ставити під сумнів кожний свій висновок та знаходити для нього обʼєктивні обґрунтування.
Вчора хотів написати пост “чому OpenSearch повільний, коли у вас надто багато шардів?” (Шард — це неподільний блок даних, як партиція; індекс — тобто колекція — складається з фіксованого набору шардів.) Всі радять робити шарди розміром у 10-50 Гб… але чи значить це, що робити їх меншими обовʼязково погано?
Виявилося, не дуже; дійсно, кожен шард коштує трохи памʼяті, але поки памʼять не вичерпана, додаткові шарди не суттєво впливають на швидкість операцій. Негативно — ні, але позитивно — теж ні, навіть попри те, що шард також є одиницею паралелізації. Бо щоб пришвидшити щось паралелізацією, потрібно більше ядер процесора, а не потоків.
Але це я знаю після того, як перевірив. А міг би залишитись із такою гадкою, витратити час на переформування індексів, та… нічого не отримати на заміну. Та це не перша та не остання ланка в розслідуванні! Перевіряти потрібно кожну.
Легко вказати на неприємну частину проєкту та сказати: проблема тут! (Мені, наприклад, доводилося витрачати час на алгоритмічну оптимізацію “некрасивого коду” там, де можна було обійтися як максимум кешем.) Але взагалі шукати причини — це ціле мистецтво. Треба знати, що важливо, що не важливо, та параноїдально собі не довіряти (що, до речі, називається науковим методом.)
Ото було колись — шукав, чому API повільний. Локально наче все було чудово, але більшість часу API проводив у серіалізації, тому я довго обирав оптимальний формат. А потім виявилося, що в продакшні є ще рукостискання TLS, та вся проблема зовсім не в серіалізації, а у встановленні підключення. Так що завжди перевіряти!
02.03.2025
Затримка оновлення в OpenSearch
В OpenSearch/ElasticSearch є така цікава особливість, що між внесенням змін та їхнім ефектом минає кілька секунд. Тобто буквально доведеться почекати, щоб побачити, що щось змінилось.
Причиною є те, що OpenSearch видає результати тільки через словники (“індекси” мовою інших баз), а оновлення словника відбувається періодично — як мінімум, щосекундно. Бо оновлення є дорогою операцією та краще, коли воно працює з цілою пачкою змін.
Зауважу, що затримка ніяк не є наслідком того, що OpenSearch є розподіленою базою. Навіть один індекс на одній машині буде оновлюватися так саме повільно. Взагалі OpenSearch оптимізований для великого масштабу, а на малому працює не набагато швидше. Також затримка не впливає на надійність — зміни нікуди не дінуться.
З досвідом традиційних СУБД це є повною нісенітницею. Проте типові використання OpenSearch - наприклад, аналіз журналів — не потребують моментального застосування змін. Будь-яка архітектура з OpenSearch всередині повинна розділяти запис та читання та передбачати ці затримки. Тому на роль головної бази - OLTP - вона фізично не підходить.