Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
13.01.2024
Огляд фітнес-кільця Oura
З місяць тому мені подарували кільце Oura. Я, власне, нічого революційного від нього не очікував. Наразі я користуюся Apple Watch, та з нарікань до нього хіба те, що в годиннику незручно спати. Але виявилося, що в Oura цікава ментальна модель, яка відрізняється від всього, що я бачив (а бачив я Apple, Fitbit та Garmin). Вона задумана для звичайної людини зі звичайним життям, а не тільки для спортсменів.
Головне те, що Oura пропонує коучінг замість обліку. Набір метрик тут більш-менш такий самий, як з Apple Watch. Але застосунок Oura обчислює з них ряд похідних метрик та порад. Найбільш загальною метрикою є “готовність”, яка показує, наскільки ти в формі сьогодні.
Здебільшого це залежить від якості сну. І тут друга перевага Oura - найбільш детальний аналіз сну, на основі пульсу, варіабельності серцебиття, кількості рухів, часу на засинання та іншого. Навіть температуру міряє.
Також Oura дуже добре автоматично розпізнає як активності, так і сон. Краще, ніж Apple Watch. А ще в переліку активностей є “робота по дому” та “робота на дворі” - завжди на Apple Watch їх бракувало. А ще заряд тримає декілька днів.
Одним словом, якщо така дивина, як фітнес-кільце, тобі цікава, то можу запевнити — воно реально працює та для звичайної людини — не атлета — по ціннісній пропозиції навіть краще, ніж ті фітнес-браслети, які я бачив.
З недоліків: кільце Oura все ж потрібно знімати для багатьох занять, як і будь-яке кільце. А потім його легко забути. Та, також, кільце не має ніякого механізму повідомлень — що логічно, але порівняно з годинником, який може розбудити вібрацією, невигідно. Зате — ніяких відволікань. :)
12.01.2024
Обмежена паралелізація в Go
Знайшов дуже класну бібліотеку conc - вона надає чарівні компоненти для побудови одночасного коду. Наприклад, цикл з обмеженою паралельністю за один рядок:
iter.Iterator[string, []byte]{MaxGoroutines: 10}.Map(myURLs, func(url *string) []byte {
resp, _ := http.Get(*url)
return io.ReadAll(resp.Body)
})
Практично не складніше, ніж звичайний цикл. Особливо подобається використання дженериків. Хто робив таке самотужки, знає, скільки тут приховано складності. Пул горутін, канали для комунікації, група очікування.
А необмежений паралелізм — то страшна річ. Колись вже писав, чому його треба обмежувати для обчислень, але сьогодні натрапив на інший випадок. Код в циклі створював горутіни та звертався до іншого сервера. Ну поки цикл на 10 чи на 100 ітерацій, все добре. Але якщо раптом з’являється потреба в десятках тисяч запитів, то сервіс починав лягати.
Причому поки не помітив той цикл, було дуже дивно: сервіс живий, пінгується, а от чомусь в реальному використанні починає сипати тайм-аутами. Такий вийшов домашній DDoS.
11.01.2024
Map/Reduce в OpenSearch
Сьогодні мав досвід з так званою Scripted metric в OpenSearch. (Така сама є й в ElasticSearch).
Фактично цей механізм дозволяє написати повноцінний скрипт, що обробить всі документи на сервері (ну, або не всі) та видасть результат. Причому працює це приблизно на той же швидкості, що й звичайна агрегація.
Що відкриває чудові можливості, бо набір агрегацій не такий широкий, та деколи складну логіку ними не зробиш. А тут — можна робити будь-що, та не витрачати часу на пересування даних з місця на місце.
Скрипти пишуться мовою Painless - це звичайна імперативна мова, яка має всі потрібні елементи: масиви, словники, цикли, регулярні вирази.
Якщо просто, то агрегація потребує два головних скрипти, та обидва з них мають форму reduce
. Один збирає дані з кожного документа в сегменті до структури-акумулятора, другий — поєднує акумулятори з усіх сегментів. Обмежень на акумулятор немає.
Мені це знадобилося, щоб знайти документи, в яких поле-масив містить дублікати. Не проміж документів дублікати, а всередині одного документа. Такої агрегації я не знайшов, надто специфічна задача. Зате скриптом вийшло майже тривіально.
10.01.2024
Як зрозуміти велику систему?
Довелося розбиратися з великою та заплутаною системою, щоб виправити в ній баги. Добре, коли є документація, а що, якщо її немає?
Раджу зосередитись на лінійному процесі, а не на розгалуженнях. Розгалуженнями можна пізніше зайнятись, коли зрозуміло, що куди йде. Отже, обираємо одну з можливих ниточок поведінки системи.
Починаємо з того, щоб записати те, що ми знаємо — початковий та кінцевий стан. Ну, наприклад: ми знаємо, що є запис в базі, та цей запис зʼявляється на екрані. Та хочемо зрозуміти, як
Далі, кроки, які нам відомі. Абсолютно не обовʼязково тут бути послідовним. Навпаки, краще почати з того, в чому певні. Де стан можна перевірити.
Наприклад, мабуть, з зовнішньої побудови ми знаємо, які у нас є сервіси. То вже якась структура. Всередині сервісу зазвичай складніше, бо код під час переплетений, та шари незрозумілі.
Наші засоби: журнали та глобальний пошук по коду. Знаходимо цікаві речі в журналах, шукаємо по коду. Так само можна роздивлятись конфігурацію, вводи та виводи та інше.
Для кожного кроку записуємо: ввід та вивід; загальні ідеї про поведінку; корисні команди та місця для дослідження; розташування коду; відомі проблеми (якщо можемо їх точно привʼязати до конкретного кроку, наприклад, за журналом).
В будь-якій зрілій системі будуть атавізми — шляхи в нікуди, залишки минулих часів. Такі теж потрібно записати, якщо зустрінеш.
А щодо формату? Дехто полюбляє діаграми, але для мене найкращий формат — то текст. Його швидше редагувати, а саме редагувати доведеться перед усім.
09.01.2024
Ціна виправлення помилки
Скільки разів ми чули історію про знамениту помилку, що була виправлена одним рядком? Ну, наприклад, класична відсутність значення там, де воно очікується. Чи був той рядок приречений стати великою помилкою, коли його писали?
Спостереження: проста помилка переростає в більшу з віком. Поки ти пишеш рядок чи модуль — помилки навіть не мають шансу запуститися. Потім можна помітити її локально — перевіркою вручну. Якщо не вдалося так — написати тест, зафіксувати таким чином поведінку вже цілого модуля. Але не факт, що ми покриємо тестом саме то місце, яке потрібно.
А потім трапляються дві речі. Раз — помилка виходить з наших рук та переходить у суспільство — де користувачів більше та співавторів коду теж. Два — ми забуваємо обставини, в яких помилка була створена, навіть сам час створення, та їх доведеться віднайти наново.
Так найтривіальніша з помилок може перерости в легендарну, яка наробить бід та вимагатиме значного часу на виправлення. Тому не можна залишати код не перевіреним, сподіваючись що час та використання “все виправлять” - щось й виправлять, але якою ціною?
Як уникнути простих помилок? Я думаю, тут нікого не здивує, що я бачу вихід в типізованих мовах: вони як раз запобігають тривіальним помилкам. Але це моя особиста пристрасть.
08.01.2024
Цілочисельні типи в Go: int проти int64
В Go, як у системній мові програмування, є ціла низка цілочисельних типів з явно заданим розміром: int8, int16, int32, int64
(Є ще типи без знаку - uint8
і так далі — але то зараз не важливо.)
Це на відміну від мов вищого рівня, де розмір числа нами не контролюється. Наприклад, Ruby прозоро перейде на довгу арифметику та готово зберігати числа довільного розміру — без всякого попередження. А в JavaScript, як я вже писав, взагалі “цілих” чисел немає — тільки числа з рухомою комою.
В Go ми вільні обрати те число, яке нам потрібно. Але… також можемо взяти тип int
, який, в залежності від платформи, має розмір 32 або 64 біти. Ми знаходимося посеред довгої епохи 64-бітових чисел. Вони всюди, від телефонів до суперкомпʼютерів. Тому, у 100% практичних випадків, int та int64 це один та той самий фізичний тип.
Коли брати int8
, int16
, та int32
більш-менш зрозуміло: або для відображення зовнішніх значень з чітко заданим розміром, або коли потрібно заощадити на розмірі великого масиву даних. В інших випадках економія не виправдана, бо процесор швидше за все працює зі словами своєї “рідної” довжини, тобто int64
.
Але як щодо int
та int64
? Що краще? Тут є декілька аспектів. Перше — типи все ж не сумісні на синтаксичному рівні та потребують явної конвертації. Друге — константи мають тип int
, змінні, що ними ініціалізовані — теж. Так само len
має тип int
. Третє — офіційний посібник радить вживати int
, якщо немає “особливої причини” на int64
.
Яка то може бути “особлива причина”? На мою думку, так само як з меншими типами, int64 має сенс там, де розмір числа заданий ззовні. Коли ми читаємо (або пишемо) число з бази, чи з файлу, чи отримуємо з інтернету — розмір залежить вже не від нас — значить, int64
правильний вибір.
07.01.2024
Як записувати розвʼязки
-
Перелічи кроки, які довелося зробити. Це можуть бути просто інструкції для людини. Може, консольні команди. Якщо йдеться про написання коду, може модулі, які створив чи редагував. Зазвичай перелік починається з мінімальних нотаток, а наскільки їх розкривати — залежить від аудиторії.
-
Якщо знайшли крок у зовнішньому джерелі — статті, StackOverflow - не забуваємо посилання; коли наступного разу будуть питання, повернемось туди.
-
Для кожного неочевидного кроку — пояснення: з якого розуміння вийшов саме такий крок. Потребу визначаємо самі, так би мовити, за “принципом сюрпризу”: якщо щось здивувало, потрібно записати.
-
Також не забудь про неуспішні спроби та напрямки. Після того, як витратив багато часу, та все одно не вийшло — мусиш занотувати, щоб наступного разу не повторити.
-
Особливо якщо є очевидний або “простий” шлях, який виявляється нежиттєздатним пізніше, обовʼязково потрібно про це записати. В мене зазвичай це виходить на другому підході — читаєш документацію — думаєш “навіщо воно так складно?” - через пару годин розумієш, чому. Потім пишу собі: “ні, так робити не треба, бо А, Б, В”
-
Так, до речі, кожного разу як документ використовується, варто оновити його тими деталями, які раніше були невідомі, або просто додалися через зовнішні зміни.
Найбільш обкатаний з відомих мені процесів — це налаштування нашої екосистеми для нового інженера. Так склалося через те, що щоразу ми адаптуємо ті частини, що не спрацювали — традиція така є. А з іншого боку, процес досить складний, тож нові та нові нюанси не припиняють зʼявлятися.
06.01.2024
Записуй свої розвʼязки
Задокументував встановлення DietPi, про яке писав два дні тому.
Я б хотів сказати, що так варто робити для кожного інженерного досягнення, але сам ледве встигаю робити поверхневі пости. Проте цього разу пазл надто довго складався та надто гарно склався, щоб цим нехтувати.
З одного боку, послідовність дій коротка та навіть ніби логічна. З іншого, скільки треба було перекопати форумів, статей та обговорень на Гітхабі, щоб це зрозуміти…
Зате залишається відчуття справжнього розуміння маленької, точкової царинки… от тільки я ж знаю, що це відчуття мине, як тільки зʼявиться наступний контекст. Тому й важливо записувати. До речі, новий контекст вже і так додався, бо треба ще задокументувати перенесення HomeAssistant. Там своє розуміння.
Що я хочу сказати. Записуй свої розвʼязки. В Obsidian, в корпоративну вікі, в коментарі в коді, в блог… головне, щоб не тільки в голову.
05.01.2024
Домашній сервер: HomeAssistant, Pi-Hole
Сьогодні закінчив з налаштуванням HomeAssistant. Бо, хоч він в мене вже був, оновлення за чотири роки потребує низки змін. На щастя, нічого складнішого, ніж редагування конфігурації.
Розумна розетка (Meross) підʼєдналася в Home Assistant теж без проблем. Власне, до розетки підключений витяжний вентилятор (української фірми Vents), який сам по собі розумний — вмикається при наявності вологості в ванній. Але, чого вентилятор не вміє — це вимикатись за розкладом.
А мені не хотілося, щоб він вмикався вночі. Тут і допомогла розумна розетка — коли вона вже є в HomeAssistant, то додати роботу за розкладом це справа на пару хвилин. Та й витрати невеликі - 200-300 грн за розетку, причому є варіанти на декілька розʼємів, так виходить економніше.
Інший сервіс, який варто мати на домашньому сервері — це DNS Pi-hole. Він водночас і прискорює DNS запити — принаймні повторювані, і блокує рекламу, трекери, та й взагалі що захочеш, та ще й послужить для перевірки DNS запитів, якщо для роботи таке знадобиться. А головне, що працюватиме це для всіх пристроїв в мережі без індивідуальних налаштувань.
З DietPi Pi-hole встановлюється дуже просто. Залишається вказати його як DNS сервер на роутері — на моєму Asus навіть є така функція DNS Director, щоб зробити використання Pi-hole примусовим через перенаправлення всіх DNS запитів на нього.
04.01.2024
DietPi: свіжа начинка для ODroid
Колись давно писав, що в мене налаштований Home Assistant. Для цього в мене є мікрокомпʼютер Odroid XU4. Він мене влаштовує настільки, що у 2022 я навіть замовив для нього цільно алюмінієвий корпус, з яким вистачає пасивного охолодження.
Але… проблема в тому, що остання офіційна версія ОС для Odroid XU4 вийшла у 2020. Та в ній, окрім іншого, застарий Python - на якому відмовляється працювати свіжий Home Assistant. А це значить, що плагіни теж втрачають сумісність, тож потроху система перестає працювати. Нарешті, після придбання нової розумної розетки, плагін для якої не можна було встановити, пішов на радикальне оновлення системи.
Мав на прикметі збірку під назвою DietPi. Це сучасний Debian з образами для всіляких мікрокомпʼютерів. Ще там є зручний конфігуратор (наприклад, можна легко змінити тепловий профіль процесора) та репозиторій ніби “оптимізованих” сервісів. Але то поки не можу оцінити.
З встановленням були деякі проблеми, про які розгорнуто тут не буду, але: спочатку ніяк не міг запустити новий образ з eMMC; потім зрозуміти, як оновити версію ядра, бо система встановлюється на приєднаний SSD (причому за замовчуванням — це величезний плюс!) - а ядро та завантажувач залишається на eMMC.
Після розвʼязку цих задач, все нарешті завелося та працює. Встановити Home Assistant на DietPi легше, ніж просто на ODroid (де все робилося вручну.)
Одним словом, дуже задоволений, хоч встановлення потребувало досвіду.