Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
- ActiveRecord
- AmazonRedshift
- API
- AppleScript
- AWS
- AWSLambda
- CGO
- Chezmoi
- CI
- Clojure
- Cloudflare
- CloudflarePages
- CssParser
- CтохастичнийТаймтрекер
- DNS
- Docker
- Dotfiles
- Fly.io
- GCP
- Git
- GitHub
- GitHubActions
- Go
- Golang
- GTD
- HomeAssistant
- Hugo
- I18next
- JavaScript
- JSON
- Kafka
- MacMiniВДорозі
- MacOS
- Markdown
- Mastodon
- Obsidian
- ObsidianCanvas
- OmniWOPE
- OpenSearch
- Oura
- Ping
- Plausible
- ReactNative
- Redis
- RSS
- Ruby
- RubyOnRails
- Sintra
- SMTP
- SQL
- SQLite
- Svelte
- Swift
- SwiftData
- SwiftUI
- Telegram
- Terraform
- TLS
- TypeScript
- Vercel
- VPN
- WeightPlot
- WordPress
- XCode
- Адвент2024
- Бази Даних
- БДС
- Безпека
- Блокнот
- Вебтехнології
- ВолюІнформації
- Гаджети
- ДизайнМовПрограмування
- Ігри
- Інструменти
- ІнтеграційніТести
- Кава
- КеруваннняЗадачами
- Кодогенерація
- Криптографія
- Локалізація
- Маркетинг
- МетаПост
- МоїПроєкти
- Навігація
- Оптимізація
- ОсновиІнтернетБезпеки
- Помічник ШІ
- ПомічникШІ
- ПостПроПохід
- Програмування
- Продуктивність
- Проєкти
- Проза
- РДУГ
- Рівночасність
- РобочийКомп
- Розробка
- РозумнийБудинок
- СинПопросивПриготувати
- Сон
- СтохастичнийТаймтрекер
- ХмарніТехнології
31.10.2025
Наскільки важливо розуміти спеціалізацію бази даних
Трохи допомагав хлопцям із проблемним навантаженням на ElasticSearch. Виявилося, проблема з запитами, де фігурував пошук зі скриптом. Скрипт виконував операцію на кшталт foo - bar > THRESHOLD.
Як побачив, то відразу все стало зрозуміло. OpenSearch будує з документів словники — де кожному значенню поля відповідає множина документів. OpenSearch швидкий, поки ти робиш пошук за словниками. Такий пошук виконується як операції над множинами, та тому є дійсно надзвичайно швидким, навіть коли в запиті згадується багато полів. Це — головна сила OpenSearch.
Але як тільки для пошуку доводиться читати самі документи — швидкість провалюється на порядок. Бо з операцій над множинами ми скочуємося до операцій над документами.
Тобто можливість пошуку за скриптом існує, щоб закрити рідку потребу, зробити щось неможливе можливим. Але вона ніяк не годиться для базового функціоналу. Це може приголомшити, якщо ти приходиш з бази загального призначення — як Postgres - де пошук по foo - bar не сильно відрізняється за пошуком по foo чи bar.
В цьому сенсі я завжди любив документацію з Redis - в ній для кожної команди виписана обчислювана складність. Колись я робив складний рушій на основі Redis, та це було критично важливо для планування.
А у випадку з OpenSearch відповідь така: хочеш шукати за різницею полів — зберігай цю різницю в ще одно поле - fooMinusBar. Та левову частку інтеграції OpenSearch складає саме планування наперед потрібних операцій та полів, які їх уможливлюють.
30.10.2025
Несподівано складна задача конвертації PDF в інший формат
Проблема: “Синево” тепер дають результат аналізів тільки у вигляді PDF. Для перегляду чи друку це нормально, зате для машинної обробки… Скажімо так, в мене була світла ідея передати результат у ШІ та спитати поради.
…ШІ той PDF прочитав “за діагоналлю”. Наплутав всі показники. Я чогось такого й очікував, тож планував конвертувати PDF в текстовий формат. Виявилося, це нелегко.
Звісно, річ не у тім, що PDF є двійковим форматом. Справжній його недолік в тому, що PDF є більше векторним зображенням, ніж текстом. Там немає аж ніякої структури документа.
В PDF відсутня викладка тексту як така. Кожний безперервний рядок стає окремим елементом файлу. Та й таблиці теж відсутні. Те, що ми бачимо, як таблицю — насправді є супом з текстових рядків. “Глюкоза” з координатами 123,456, “40” з координатами 145, 567, “г/моль” з координатами 156, 634.
З цього супу і параграфи не так легко скласти! А як таблицю — то взагалі задача непіднімна. (Як виявляється.)
Найкраще в мене впорався застосунок Tabula - він принаймні вірно визначив таблицю, тільки підписи з перенесеннями перетворював на окремі рядки таблиці. Технічно, далі можна вручну виправити цю таблицю — позливати рядки разом — але в мене на це часу немає. Залишуся без ШІ-терапевта. 😄 (Бо в такому вигляді він теж читає неправильно.)
PDF - формат абсолютно несемантичний. Це нагадує, як важлива семантичність, наприклад, в HTML. Конвертори PDF у HTML існують, звісно, але залишають зміст у вигляді абсолютно позиційованих елементів <div>.
Звісно, в PDF є й потужна перевага — він ідеально відтворюється як на екрані, так і на аркуші. Я вас тільки прошу пропонувати й альтернативні формати.
29.10.2025
ExifTool та робота над домашньою фотобібліотекою
Стала задача закинути кілька (… десятків гігабайтів) старих фотографій в Apple Photos. Здебільшого то скани. Та, хоч технічно можна прямо взяти та імпортувати як є, але тоді я втрачу останні крихти метаінформації.
А саме: в Apple Photos немає дерева каталогів, а фотографії насамперед впорядковані за датою. Плюс, звісно, альбоми, ключові слова, геолокація, але це все вторинне. І всього цього в мене взагалі немає. Є вбудована можливість створити з тек на диску альбоми, втім це мені теж мало допоможе — та й не хочу я купу зайвих альбомів.
Отже, виходить, треба підготувати фотографії так, щоб в Apple Photos в них можна зорієнтуватися. Та допоміг мені в цьому ExifTool - програма, яку обовʼязково треба знати для роботи з метаданими фотографій або навіть відео.
Почнемо з того, що вона сама вміє знаходити фотографії в дереві, робити резервні копії, а також навіть виконувати деяку логіку (наприклад, “записати метатег, тільки якщо його ще немає”.)
Потім, ExifTool здатний записати в титул фотографії її шлях. Це збереже той порядок, який був у фотографіях раніше, та принаймні допоможе їх впізнавати.
Далі, мені пощастило, що фотографії були розкладені в теки за роком. Щоб Apple Photos про це теж знав, можна взяти рік зі шляху, та записати у метатег DateTimeOriginal. Тепер хоч приблизно все в порядку!
Вийшло щось таке — вся робота однією командою:
exiftool -r \
'-Title<${directory}' \
-if 'not $DateTimeOriginal' '-DateTimeOriginal<${directory; $_=(split m!/!, $_)[1] . ":01:01 12:00:00"}' \
.
28.10.2025
Long Poll та проблеми зі звʼязком
Памʼятаєш, я казав, що дивним чином під час останнього збою AWS в нас застряг SQS - нічого не отримував, але й не відвалювався? Знайшов причину.
Отримання повідомлень з SQS працює за моделлю Long Poll: клієнт робить запит до сервера, а сервер відповідає тоді, коли в черзі зʼявляться повідомлення. Однак якщо черга порожня, то сервер через зазначений інтервал дасть порожню відповідь. В деяких реалізаціях Long Poll працює інакше — це клієнт обриває підключення. Але в AWS SQS все залежить саме від сервера.
Отже, що відбувається під час обриву звʼязку? Тут є важливий момент: обривів звʼязку не існує. Підключення TCP - це лише домовленість між клієнтом та сервером про майбутній обмін пакетами. Але відрізнити відсутність пакетів від “обриву” просто неможливо. Немає ніякого другого каналу, через який ми можемо “побачити” сервер та дізнатися, чи він досі підключений. Ти знаєш про це тільки з того, що він надіслав тобі пакет.
А отже виходить, якщо клієнт чекає відповідь через Long Poll від відключеного сервера — він ніколи не отримає пакетів, але також не отримає й помилки. І буде чекати звістки… нескінченно.
Вихід з цього зрозумілий: впровадити тайм-аут з боку клієнта. Що взагалі гарна ідея в будь-яких ситуаціях, як бачимо! Обриви звʼязку — абсолютно нормальна ситуація в інтернеті.
В деяких AWS SDK цей тайм-аут вже зробили — наприклад, aws-sdk-js та aws-sdk-php. А в aws-sdk-go - поки ні. Зате його можна додати вручну з тим же ж результатом. Тайм-аут цей повинен трохи перебільшувати інтервал очікування, і тоді ми будемо надійно захищені від обривів.
27.10.2025
Що такого особливого в AWS us-east-1?
Якщо ти трохи спостерігаєш за подіями в інфраструктурі, може в тебе є два питання. Ну те, що us-east-1 - це регіон, тобто ізольована частина AWS, розташована умовно на сході США — сподіваюся, і так зрозуміло. Та, хоч в AWS є понад 30 регіонів, чому здається, що всі користуються AWS us-east-1? Та чому як проблеми з AWS - то обовʼязково саме us-east-1? Вони що дурні?
us-east-1 є найстарішим регіоном в AWS. Звісно, тут набереться найбільше клієнтів, особливо тих, що вже великі та успішні. Але також, цей регіон обріс найбільшим шаром технічного боргу. Попри це…
us-east-1 є першим регіоном для всього нового. Будь-який сервіс спочатку запускається в us-east-1, та інші регіони інколи суттєво відстають. А значить, є суттєво стабільнішими. :) Мабуть, в ідеальному світі самий “заборгований” регіон не був би ще й передовим по фічах, але маємо те що маємо.
us-east-1 є найдешевшим регіоном. Та це безперечно приваблює бізнеси. Я гадаю, роль “гама-тестерів” врахована в ціні, щоб клієнти не розбігалися, а навпаки — прибігали. Хоча я раджу обирати будь-який інший регіон (наприклад, eu-west-1.)
us-east-1 містить контрольну площину. Такі фундаментальні сервіси, як IAM чи Route 53 - існують саме в цьому регіоні. Тож, деякі проблеми в us-east-1 будуть розповсюджуватися й на всі інші регіони. Наприклад, проблеми з DNS чи моделлю авторизації.
Отже, якщо підсумувати, us-east-1 є “столицею” AWS. Проблеми в ньому й більш ймовірні, й більш помітні, ніж в будь-якому іншому регіоні. Тож немає нічого дивного в тому, що він завжди на слуху.
24.10.2025
Всі способи, якими я хостив PHP
-
Перші спроби були на Windows. Про Linux та тим паче macOS я нічого не знав. Здається що під Windows була готова збірка Apache+PHP+MySQL (може то був WAMP?), але можливо я ставив їх окремо. Запамʼятався графічний інтерфейс вебсервера Apache з рожевим пером. Все було супер просто, бо файли з диска ставали сторінками сайту. Та попри цю простоту, я навіть — як це зараз модно казати — селф-хостив форум — так, це було на Windows.
-
На шаред-хостінгу. Це коли хостинг дає тобі теку на FTP, ти туди заливаєш ті ж файли та вони стають сайтом. Власне, розроблялося все теж на Windows, а що там було на сервері — мені було мало цікаво.
-
На першій роботі через nginx. Перший раз довелося дізнатися, що взагалі-то на світі існує не тільки Apache. Та зʼявилися речі, які локально не працюють — хоча не пригадаю, які саме. (Ой, на тім роботі ще була купа Macromedia Flex, але то ціла інша історія.)
-
На виділеному хостингу… поки через Apache. Бо Apache був набагато простіше в налаштуванні, ніж nginx - та й PHP там був вбудований. Встановив сервер, дописав трохи конфігурації — і можеш так само копіювати файли, як і раніше.
-
Там же ж, але вже із nginx та PHP-FPM. Це фактично сервер застосунків для PHP - запускається окремим процесом, кешує скомпільований код, та служить бекендом для nginx. Спонукало перейти те, що я тримав також кілька застосунків на Rails, та вже nginx став більш зрозумілим рішенням — зворотнім проксі — ніж Apache, який пропонував підхід “все в одному”.
Цікаво, що починався PHP з парадигмою “я вказую шлях до файлу зі скриптом на сервері та викликаю його”, а прийшов до “застосунок є сервісом, який обробляє запити”. І тепер скриптові мови не так вже й відрізняються від компільованих.
23.10.2025
Як я написав форум з нуля, а потім переписав
…Багато років тому, ще до соціальних мереж, в Дніпрі був студентський форум. Створили його Діма та Макс, форум зібрав навколо себе вагому кількість користувачів, це було цікаво та весело, зʼявилася спільнота, яка щотижня збиралася офлайн.
Але в певний момент форум зник. Ну, тобто сайт перестав працювати. Я вже не памʼятаю чому — чи то технічні проблеми, чи то організаційні. Так чи інакше, спільнота залишилася, сайту немає, а власники кудись ділися — може, відпочивати поїхали.
Зате був я. Я на той час захоплювався програмуванням ігор, а про вебтехнології знав небагато. Ну, сайт в мене вже був, але ж то HTML на якомусь безплатному хостингу. На PHP я може щось зовсім іграшкове до того робив.
Отже, думаю, немає старого форуму — напишу новий! І написав. За кілька днів в мене вже була робоча версія форуму на PHP+MySQL. До того ж я не знав, де його хостити, тому кілька тижнів форум жив в мене на локальній машині, доступній ще й через GPRS. Кожна сесія давала мені нову IP адресу, яку я оголошував десь по ICQ та форум оживав. Головне, що спільноті було де спілкуватися.
А потім власники першого форуму повернулися, відновили його, та форумів стало два. Я, звісно, свій не захотів зупиняти, тому кілька місяців в нас були політичні сутички, розколи і таке інше. От, наприклад, тоді я дізнався, що форум не обовʼязково писати з нуля, а можна взяти готовий рушій. У них був vBulletin, або в народі — булка.
Хоча хіба справжній програміст буде брати готовий рушій? Та ні… Коли я почав розуміти, що написаний за тиждень форум був написаний жахливо — а весь його код був у кількох величезних файлах — то взяв та переписав рушій наново ще раз, з нуля. Друга версія була значно краще продумана та навіть трохи страждала від “синдрому другої версії”, тобто випуск її трохи затягнувся.
Звісно, потім зʼявилися соціальні мережі та вбили обидва форуми. Тільки згодом я зрозумів, наскільки це визначна, але дурна праця — писати форум з нуля. Що тут сказати? Принаймні цей проєкт приніс мені першу роботу.
22.10.2025
Авторизація OAuth з iOS
Невдовзі мій стохастичний таймтрекер Ping набуде інтеграції з Beeminder. (Beeminder це сервіс для обліку просування до цілей — який штрафує тебе, якщо ти не виконуєш обіцянок.) Власне, ідею стохастичного таймтрекінгу винайшли саме автори цього сервісу, тому така інтеграція зробить Ping дійсно корисним для однієї дуже важливої спільноти.
Інтеграція починається з автентифікації. Вона у Beeminder потребує діалогу OAuth. Я, звісно, багато разів проходив таку автентифікацію в чужих застосунках на iOS - знаєш, коли відкривається браузер і ти там вводиш логін/пароль. Але не мав гадки, як її реалізують.
Виявилось, все ще простіше, ніж я думав. Браузер відкриває не сам застосунок, а фреймворк Authentication Services. Нам достатньо побудувати URL для авторизації та створити ASWebAuthenticationSession. Та другий момент — щоб повернутися назад, URL редиректу повинен мати особливу схему, щось на кшталт ping://beeminder-auth. Коли браузер побачить цю схему, то завершить автентифікацію та поверне нам остаточний URL - в якому сидить токен.
Гадаю, таким саме чином можна зробити авторизацію через Google або інші провайдери OAuth. А мені залишалося тільки зберегти токен. Для чого є API Keychain services. Я, якщо чесно, спочатку збирався просто в базу зберігати, але виявилось, що є безпечніший спосіб.
21.10.2025
Fail Fast проти Hang In There
Є дві протилежні поведінки застосунку, коли виникають проблеми з його залежностями, або взагалі непередбачені ситуації. (Назви я тільки що вигадав.)
Fail Fast: якщо ми опинилися в ненормальному стані, то за майже визначенням програма не зможе з нього вибратись. Отже, краще, що можна зробити — це зупинити застосунок та дозволити супервайзеру запустити його наново. Переваги цього підходу: з чистого аркуша не виникне внутрішніх розбіжностей, бо ми точно знаємо, що кожна підсистема буде знову в передбаченому стані. Плюс, такий підхід робить застосунок простіше.
Hang In There: застосунок повинен триматися, хай би що трапилося. Кожну помилку пробувати наново. За браком залежності продовжувати без неї, в обмеженому режимі. Взагалі ніколи не зупиняти застосунок, поки не отримаємо на то сигнал. Переваги тут — що не буде перезапуску, власне.
Обидва підходи мають своє застосування. Колись я вважав, що в сучасній інфраструктурі з контейнерами Fail Fast - очевидний вибір. Але дедалі більше хочеться робити застосунки, які не будуть перезапускатися. Зокрема, перезапуск ризикує зробити із поганого гірше, як зі мною вже траплялось.
Вчорашній простій AWS us-east-1 це підкреслив, бо в нашому випадку найдовше не було можливості запускати нові задачі. Отже, те, що трималося — працювало, а те, що очікувало на перезапуск — просто впало до закінчення інциденту. (До речі, також не могли стартувати лямбди, а вони можуть бути тільки Fail Fast.)
Хоча здається, був і протилежний приклад: черги SQS не спричинили відмову споживачів, а просто припинили доставляти повідомлення до перезапуску клієнтів. Поки не зрозумів, чому так сталося.
20.10.2025
Робота із Windows по SSH
Є в мене домашній сервер на Windows. Точніше не так: в мене є ігровий компʼютер на Windows, але оскільки в ньому потужна відеокарта та й решта заліза, він чудово підходить для важких задач, як-от бібліотеки Plex. У 2023 я писав, як перекодовував фільмотеку у HEVC.
Тоді я робив всю роботу на макбуці, а на Windows потім тільки запускав bat-файл для перекодування. Для обмеженої задачі того вистачало. Але тепер стикнувся із тим, що читання файлів у випадковому порядку — з мережевого диска значно повільніше за локальне. Та аналіз кодеків утилітою Mediainfo не закінчився за декілька годин. Отже, вирішив запускати скрипти безпосередньо на Windows.
Але, власне працювати на Windows в мене не було бажання аж зовсім. Зате у Windows є вбудований сервер SSH. Це вже половина справи — можна відкривати термінал та щось там робити. Було б чудово, аби в Windows були нормальні команди оболонки, а не dir замість ls і так далі — але жити можна. От тільки ж Ruby немає.
Ну то й що, chocolatey робить встановлення Ruby в одну команду: choco install ruby mediainfo-cli. Серйозно, без Chocolatey на Windows взагалі немає чого робити. (Також можна було встановити Mise, але поки потреби немає.)
Залишилося питання того, як доставити скрипт на компʼютер із Windows. Окрім того, що можна було змонтувати мережевий диск та редагувати файл прямо там, можна скористуватися звичайним scp, бо він також працює без жодних проблем. Я чомусь про мережевий диск забув, а зробив собі скрипт по типу scp script.rb pc:. && ssh -tt -x 'ruby script.rb'.
От, власне, і все, результату досягнено, те що мережею займало години, локально відбулося за хвилини. Також допомогла рівночасність та клас Thread::Queue, але про то іншим разом.

