Стендап Сьогодні

Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті

Підписатись на RSS
📢 Канал в Telegram @stendap_sogodni
🦣 @stendap_sogodni@shevtsov.me в Федиверсі

24.11.2023

Зробив інструмент для аналізу графу ресурсів Terraform

Сьогодні ставала задача — знайти, де саме використовуються деякі ресурси в Терраформі. Задача ніби нескладна, та з нею порається Terraform Language Server в доповненні для VS Code. Проте є ситуації, коли цей пошук нетривіальний та доповнення його недовиконує. Наприклад, коли залежність проходить через модулі, та не просто модулі, а й ресурси output та var. Зʼявилася ідея оптимізувати, тож написав маленький додаток.

Пошук інструментів, що існують, гарних результатів теж не дав. Зате знайшов корисну команду terraform graph, яка генерує граф ресурсів на мові DOT. Сам по собі граф теж не підходить, бо коли ресурсів тисяча, то нічого наочного в графі немає. Зате граф — хороша основа для машинного аналізу.

…В контексті Terraform цей граф це також добре тому, що він переносний, тобто не привʼязаний до конфігурації, доступів, та іншого, а також тому, що він не містить жодних секретів (хіба що саму структуру ресурсів.)

Оскільки вивчення графу — це операція не з одного кроку, вирішив зробити не утиліту командного рядка, а графічну, а саме, звісно, клієнтський вебдодаток. Для розбору файлів DOT знайшов пакет ts-graphviz. А решта - Svelte (який вже точно став моїм вибором №1 для простих додатків.)

Зокрема, я винайшов рішення для var та інших проміжних ресурсів. В додатку є особливий режим, який робить їх “прозорими” та відображає успадковані залежності. Також, оскільки граф не містить інформацію про розташування ресурсів в коді, то додав можливість скопіювати рядок оголошення ресурсу, щоб можна було легко знайти його в редакторі.

Сам додаток TFDig та його код. Якість поки на рівні “пару годин писав”, ви мої альфа-тестери. :)


23.11.2023

Історія однієї фічі: коректування накопичень

Контекст: в Сінтрі скоро будуть накопичення; внески на накопичення створюються як витрати з бюджету. Але зʼявилася потреба також додавати коректування накопичень, які фактично є теж внесками, але вже без відповідної витрати: таке коректування моделює зовнішню зміну до накопичення. Ось мій процес розробки по кроках.

GoalDeposit = { isCorrection: true } | { expenseID: string }; // та інші поля


22.11.2023

Правила безпеки для Firebase Firestore

База даних Firebase Firestore особлива тим, що вона займає місце повноцінного бекенду, до якого звертається клієнтський додаток. А значить, окрім звичайної перевірки даних, яку роблять всі бази, Firestore також мусить робити й авторизацію.

Механізми валідації та авторизації поєднані в Firestore в єдину систему правил безпеки. Правила безпеки — то є попросту функції, які визначають, чи дозволений той, чи інший запит. Правила впорядковані за шляхом.

Найпростіші правила перевіряють зміст документа, а також наявність в користувача доступу до нього — в контексті запиту міститься ID користувача, який можна порівняти з елементами шляху, або з атрибутами.

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

В цілому, правила це зручний інструмент, та мені подобається їхня завершеність. Що не подобається: правила абсолютно непрозорі для клієнтів — клієнт просто отримує повідомлення “бракує дозволу” як на помилки валідації, так і взагалі на логічні помилки в правилах (наприклад, звертання до атрибута, якого не існує.)

Щоб знайти справжню причину, потрібно дивитися в логи Firestore та зіставляти їх з діями клієнтів. Це робити незручно. Тому я й наполягав на написанні вичерпних тестів на правила безпеки — тести, оскільки працюють з емулятором, мають доступ до справжніх помилок.


21.11.2023

Нюанси перетворення індексів в OpenSearch

Перетворення індексів в OpenSearch (або ElasticSearch) кажуть, що вони схожі на матеріалізовані розрізи в реляційних базах таких, як PostgreSQL.

Схожість в тому, що перетворення утворюють новий індекс, який побудований на даних з індексу-джерела. (Якщо що, то “індексом” в OpenSearch називається колекція документів, а ніяк не механізм пошуку. Але, якщо нахилити голову, то це і є “індекс для пошуку”, а таблиці до нього просто немає.)

Але алгоритм перетворення суттєво відрізняється від матеріалізованих розрізів. По-перше, перетворення завжди інкременті. Повторний запуск перетворення обробляє тільки нові дані. Хоча, в той самий час, перетворення займають набагато більше часу, ніж побудова розрізу схожої складності — очевидно, OpenSearch не розрахований на операції, які обробляють буквально всі документи.

По-друге, цікаво те, як саме тут реалізована інкрементальність. OpenSearch відстежує перелік документів, які змінилися від останнього виконання. Далі — обчислюються всі комірки агрегації, які містять ці документи. І, нарешті, кожна з комірок обчислюється повторно. (В порівняння з розрізами в Redshift, це досить просто для розуміння.)

Складно стає, коли стає потрібно видалити вихідні дані та зберегти при цьому агрегації. З видаленими документами самими по собі OpenSearch нічого не робить. Але якщо відповідні комірки будуть обчислені наново — що відбудеться, якщо нові чи оновлені документи потраплять до тих самих комірок — то вже без врахування видалених документів.

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


20.11.2023

Ролапи в ElasticSearch тепер застарілі?

Не встигли ми інтегрувати ролапи (ні, серйозно - не встигли), аж раптом остання версія ElasticSearch - 8.11 - оголошує, що вони deprecated. Хоч в нас не ElasticSearch, а OpenSearch, я б все одно не покладав великих надій на цю функцію, бо на практиці OpenSearch слідує за ElasticSearch по своїм планам. (OpenSearch це гілка ElasticSearch з відкритою ліцензією.)

Як я розумію, офіційна причина депрекації в тому, що є функція downsampling, яка робить те ж саме, тільки більш ефективно. Але downsampling працює тільки для часових рядів, коли ролапи підтримували агрегацію по набору атрибутів.

Тому інша, краща альтернатива ролапам — це перетворення індексів - transform. Раніше я віддав перевагу ролапам, бо вони виглядали більш спеціалізованими, але за реальним досвідом видно, що працюють вони однаково: роблять пошук з агрегацією, сторінка за сторінкою, та зберігають в новий індекс.

Але важлива різниця в тому, що в ролапах документи, що складають агрегацію, були приховані від користувача, в той час, як перетворення утворюють звичайні вихідні індекси, з яким можна робити все, що з іншими індексами, включаючи ще одне перетворення. Це те, що мені в ролапах найбільш не подобалось, бо зберігати місяці та роки даних в невидимих документах якось… неспокійно.

А ще перетворення здатні на повноцінний заскриптований map-reduce, тобто можна реалізувати майже будь-яку логіку.


19.11.2023

Оптимізація зображень для вебу

Сьогодні в черговий раз прочищав блог від тих проблем, які встигли накопичитись. Для цього в мене безплатний акаунт Ahrefs, який без $99 на місяць небагато всього робить, проте надає корисний аудит проблем з сайтом.

Одною з проблем виявилось те, що в мене забагато великих світлин. Дивно, що Ahrefs не каже прямо, що таке “завелика” світлина, але за розміром найменшою такої зрозуміло, що поріг дорівнює одному мегабайту. Причому, не мебібайту, а саме одному мільйону байтів, що буде важливо нижче.

Звісно, виправляти це вручну — робота не для мене, я краще відшукаю утиліту та буду писати скрипт. Знайшов jpegoptim. Головне тут — це можливість вказати бажаний розмір результату… щоправда, в кібібайтах. Емпірично визначив, що якщо вказувати розмір у 920 кб, то світлини будуть виходити точно менше за 1000000 байтів.

Залишилось знайти всі великі файли JPEG та застосувати до них jpegoptim. Для того підійде стандартна команда find. Єдине, що виявилось, що пару файлів я не хочу оптимізувати (вони стосуються глобуса). Тому додав також файл ігнорування, а щоб застосувати його, довелося дізнатися про команду comm - вона робить, фактично, операції над двома множинами рядків.

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

Остаточний скрипт, якщо він потрібний чи цікавий, можна забрати тут.


18.11.2023

Піраміда складності проєкту

Спостереження при доповненні додатка на Firebase + Redux Toolkit + React. Але воно, мабуть, справджується для інших архітектур теж.

Додаток мій поділений на три шари. Це інтеграційний шар для Firebase, шар логіки Redux Toolkit, та презентаційний шар React. Відповідна кількість коду зростає; інтеграція з Firebase це 1000 рядків, логіки десь в пʼять разів більше, а презентації ще в чотири рази більше ніж логіки.

Але ось, що я помітив: складність змін зростає у зворотному напрямку. Нововведення до моделі можна тижнями обмірковувати, а маленька зміна призводить до декількох годин роботи. Навпаки, коректувати компоненти React, поки загальна логіка залишається незмінною можна легко та швидко.

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

Стосовно Firestore, складність обумовлена тим, що база фактично програмується мовою правил безпеки, та шукати в них помилки через використання додатка виходить дуже неефективно. Краще написати тести. Точніше, не так: інвестиції в тести окупляться саме в найскладніших частинах проєкту. Нехай вже компоненти React залишаються без тестів, головне, щоб модель була ретельно ними покрита.


17.11.2023

Struct, Data та OpenStruct в Ruby

Типу в продовження колишнього поста про структури в Ruby… Наразі в Ruby є три класи, які пропонують абстракцію “структури даних”. Чому так, в чому різниця, та який треба використовувати?

Насамперед про OpenStruct. Його офіційно рекомендують не використовувати (та видають попередження.) Чому так? Бо це буквально обгортка для звичайного хеша, написана на чистому Ruby. Та було б ще не так погано, якби це був просто хеш — але до нього додається ще метапрограмування, що й утворює не дуже швидкий клас — особливо для низькорівневого використання.

Тепер, Struct та Data. Це класи-брати. Вони мають спільний код та написані на C. Тут для збереження полів використовується масив, а методи утворюються без метапрограмування. Така реалізація і працює швидше, і займає менше памʼяті. Обидва класи утворюють звичайні класи-нащадки, з якими можна робити що завгодно, в тому числі й додавати нові атрибути та методи.

Struct та Data майже у два рази швидше за хеш, тому їх використання має практичний сенс, коли треба зберігати багато однотипних обʼєктів.

Що ж таке новий тип Data? Це обрізана за можливостями Struct, де відсутні методи-сеттери та деякі інші. Технічно, іншої різниці ви там не знайдете. Семантично, це реалізація патерну “value object”, тобто простого незмінного набору даних. Ось детальніше на трекері Ruby. Звісно, незмінність в Ruby відносна та примарна, але все одно має сенс брати Data для коду у функціональному стилі.

До речі, запропонував та реалізував Data харків’янин Віктор Шепелєв.


16.11.2023

Автоматизація macOS: витягання поточної вкладки з Safari

Я останнім часом багато зберігаю закладок в Obsidian. Зазвичай просто копіюю посилання з браузера та зберігаю у належний документ. Все б добре, але хотілося б також автоматично отримувати й назву сторінки. А ще краще відразу ціле посилання у синтаксисі Markdown, щоб не робити його вручну.

Напевно, є безліч способів це робити, але більшість з них починається зі встановлення чогось новенького, яке отримує доступ до всіх моїх вкладок. Хотілося обійтись наявними засобами. З таких найбільш очевидними є засоби автоматизації macOS: Shortcuts та AppleScript.

Shortcuts - то нове графічне оточення для побудови скриптів з готових блоків. AppleScript - стародавня мова та оточення для автоматизації. Задачу можна розвʼязати кожним з них, як видно з ілюстрації. Також кожний зі скриптів можна привʼязати до комбінації клавіш Hyper+C за допомогою BetterTouchTool (може й інакше, але я так звик.) Працює бездоганно, я дуже задоволений. 🥳

Є один важливий аспект. У Shortcuts кожна дія має налаштування приватності, тому під час запуску в тебе спитають дозволу на перегляд сторінки — причому дозвіл привʼязаний до домену, тому доведеться бачити його часто. У AppleScript немає таких детальних дозволів, тому тебе просто один раз спитають, чи дозволяєш BetterTouchTool (де-факто власнику скрипту) взаємодіяти з Safari. Тому я зупинився саме на рішенні на AppleScript.

…Ну й бібліотека засобів у AppleScript та Shortcuts абсолютно різна. На щастя, принаймні можна викликати один скрипт з іншого. Та й скрипти оболонки теж. Мак взагалі надзвичайно дружня для автоматизації система, якщо мати бажання.

(Минулої осені я як раз писав скрипт, щоб зберігати вкладки з Safari до Reeder. От, дійшла черга ці вкладки опрацьовувати та каталогізувати.)


15.11.2023

Вкладені підказки у VSCode; власні налаштування кольорів

💡 Десь близько року у VS Code є така функція, як вкладені підказки (inlay hints.) Вони додають “фантомний” допоміжний текст до коду — наприклад, назви параметрів функцій або їхні типи. В тих мовах, де це підтримується (а для мене це TypeScript та Go), це дуже зручно. Раджу відкрити налаштування, пошукати inlay та подивитися на численні можливі підказки.

До речі, офіційної української локалізації VS Code немає, отже доводиться вигадувати переклади функцій власноруч.

Є один неприємний мені аспект цих підказок — колір, яким вони відображаються за замовчуванням. Він надто близький до звичайного тексту, тож важко стає відрізнити. А відрізняти важливо, бо ті ж типи деколи хочеться написати вручну, та незрозуміло, які з типів є “фантомними”.

🎨 Тому розібрався та знайшов налаштування workbench.colorCustomizations. Ним можна перепризначити будь-який з контекстних кольорів. До речі, для підказок кольорів цілих три — перераховувати їх не буду, бо легко знайти в редакторі налаштувань знов-таки за ключем inlay. (Не знаю, чи всі знають, що окрім графічного діалогу налаштувань у VSCode можна редагувати їх у вигляді JSON, для чого є команда Open User Settings (JSON).)

🌑 Отже, зробив у себе підказки блідішими. Нарешті, залишився останній нюанс — блідий текст у світлій темі стає яскравим у темній. А я один з прихильників автоматичного перемикання тем. Тому довелося знайти ще, що у colorCustomizations можна вкласти ключ з назвою теми та вказати інші кольори для темної теми. Які й можна побачити на ілюстрації.