Верхняя полка📝
Ir al canal en Telegram
Путевые заметки программного инженера и легкоатлета-любителя. Автор: Владимир @Toparvion Плизга Домашняя страница: https://toparvion.pro/
Mostrar másRusia519 589La categoría no está especificada
362
Suscriptores
Sin datos24 horas
Sin datos7 días
+330 días
Archivo de publicaciones
Hard work & hard run 🛠👟
Текущая рабочая неделя выдаётся весьма непростой. Во-первых, она длится уже 12-ый день, и почти каждый из них оказывается полон задач вплоть до "третьей половины дня" включительно. Во-вторых, из-за этого приходится пропускать некоторые тренировки, а это не очень хорошо сказывается на самочувствии, не столько физическом, сколько эмоциональном — уже не раз замечал, что если объём тренировок проседает, а другие нагрузки растут, то, видимо, где-то что-то в организме недовырабатывается, и даже привычные вещи и события постепенно начинают восприниматься не так позитивно, как раньше 🤷🏼♂️
А тут ещё незаметно подкралась очередная дата проведения ЗаБег.РФ — масштабного любительского бегового старта, проводимого одновременно в десятках городов России, а с некоторых пор ещё и в других странах. В последние годы я пропускал этот старт, так как: (1) уже пробегал на нём 21 км несколько раз, и (2) из-за синхронного старта во всех городах, начало забега в Новосибирске приходится на 13:00 — зачастую самый пик жары 🥵
Нынче ЗаБег должен был пройти в юбилейный 10-ый раз, плюс застрявший в Новосибирске холодный циклон обещал, что жары не будет. Да ещё и некогда запланированная поездка в эти выходные отменилась. Словом, всё шло к тому, чтобы вписаться в этот старт, что я и сделал ✍️
Ожидаемо, бежалось нелегко: сказывались и пропущенные тренировки, и непрекращавшийся ни на минуту дождь (хорошо хоть не проливной), и периодические волны ветра, которые сильно мешают будучи встречными, но почему-то остаются незаметными, когда дуют в спину 💨
Найдя себе оправдание в том, что это забег не на рекорд, а так, для поддержания формы, я выбрал сравнительно невысокий, близкий к комфортному темп и бежал с ним почти всю дистанцию. Тем не менее, результат меня приятно удивил, так как оказался на минуту быстрее предыдущего Весеннего полумарафона в апреле ⏱️
И вот теперь моя коллекция медалек этой серии пополнилась ещё одной, шестой по счёту. Да, это далеко не все 10, но среди них есть самая первая, за 2017 год, поэтому мне всё же приятно ощущать себя частью этой большой, правильной и позитивно заряженной истории 🌅
Ну а пока этот заряд ещё греет, сжимаем зубы/булки/кулаки/пр. и погнали трудиться дальше 👷♂️
"Чем горячей была работа,
Тем краше день ваш выходной"
© М. Бернес
#спорт
Дорогой ИИ, спасибо за то, что помогаешь мне писать код, беря на себя часть работы, например, генерацию всякого рутинного кода.
Но если ты и дальше будешь генерить getter'ы и setter'ы такими, то я уж лучше как-нибудь по старинке...
public static boolean isWriting(boolean isWriting) {
return isWriting;
}В сегодняшнем выпуске рубрики #инструменты хочу поделиться сайтом EndOfLife.date — агрегатором сведений о сроках поддержки версий всевозможного ПО: от стандартов и языков программирования до браузеров и офисных программ 🗄
По нему можно быстро выяснить, когда превратится в тыкву ваша версия сервера, СУБД или ОС. А заодно узнать, когда вообще выходили мажорные версии, и какие у них даты истечения поддержки. И всё это без продирания через разношёрстную документацию каждого вендора 📚
На сегодня в каталоге числится 455 программных продуктов. Доступ к их сведениям можно получить не только руками через браузер, но и через REST API, а для особо занятых есть возможность экспортировать данные каждого продукта в iCalendar 📆
Последнее может быть особенно полезно, чтобы в день, когда ваш основной фреймворк окончательно перестанет поддерживаться, разослать всем мудрое: "А ведь я предупреждал!" 👴🏼🧙🏻
In-memory графы на Java — от теории к практике ⚒️
Готовые инструменты, да ещё open-source, — это, конечно, здорово. Но реальность обычно такова, что ни один из них не подходит на 100%. Так было и у меня на недавнем проекте. Поэтому пришлось собирать Франкенштейна:
1️⃣ Основная часть — на Guava Graphs
2️⃣ Отдельные упоротые специфичные кейсы — на JGraphT
3️⃣ Часто востребованные алгоритмы — (полу)самописные
Поясню такой выбор.
1️⃣ Guava Graphs оказалась хороша по трём причинам:
— максимально человеческий API: интуитивно понятный, почти без неожиданностей, хорошо документирован;
— гибкость построения графов: можно указать что надо, а что нет, и не "переплачивать" за ненужные фичи;
— стандартный набор прелестей: большое сообщество, много примеров, регулярные обновления.
2️⃣ Без JGraphT не удалось обойтись из-за того, что:
— нужно уметь экспортировать граф в другие форматы (GraphVizDot, GEXF) для отладки и верификации (см. приложенный скринкаст);
— нужно исполнять нетривиальные алгоритмы, которых нет в Guava, например, выявлять путь образования циклов.
В принципе, JGraphT покрывает и функционал Guava, поэтому можно было обойтись только им. Но помимо всяких полусубъективных факторов (типа удобства API и полноты документации) я не стал так делать потому, что JGraphT расценивает ребра графа как полноценные его сущности и потому создаёт в памяти объект под каждое ребро. А в Guava есть возможность этого не делать и выражать ребра атрибутами вершин 🔖
В нашем случае рёбра отражают однотипные отношения между ячейками таблиц, поэтому не нуждаются ни в лейблах, ни в других свойствах, а значит, не обязаны быть объектами, и это здорово экономит память. Судите сами — сейчас на одном из серверов заказчика расклад по графу такой:
• 516К вершин (ячеек)
• 1,1М рёбер (связей между ячейками)
• 500 Мб занимает граф в памяти 🪙
Согласитесь, это уже не мало. А если бы мы взяли JGraphT, и на каждое ребро создавали бы по объекту в куче, цифры были бы куда более жуткими... 🙈
3️⃣ Собственные алгоритмы потребовались в тех местах, где Guava ничего не предлагает, а конвертировать граф в формат JGraphT неоправданно дорого. Пример такого места — топологическая сортировка, которая нужна при каждом вычислении зависимых ячеек или при полном пересчёте всей таблицы 🔗
Изначально я взял для этого готовый алгоритм с GitHub. Он работал чётко, но выдаваемый им результат можно было исполнять только в один поток, а в наших масштабах этого оказалось недостаточно: таблица на 16,5К ячеек с формулами обсчитывалась 51 секунду. Тогда пришлось менять его на самописный алгоритм, пригодный для исполнения на множестве ядер 🪵
Конечно, слово самописный тут требует кавычек, ибо в 2026 году вряд ли имеет смысл писать такие вещи с нуля. Вместо этого основа реализации создавалась в обнимку с ИИ, а потом дорабатывалась напильником под (порой суровые) реалии целевого проекта 🪚
Заодно с этой доработкой открылись глаза пути оптимизации, позволяющие избегать многих вычислений, исход которых известен заранее. Благодаря этому, обсчёт той же массивной таблицы сократился с 51 сек. до 2 сек. 📉
—
Не скажу, что строение этого Франкенштейна меня полностью устраивает и не подлежит пересмотру. Вопросики к нему есть. В частности, одна строчка в
build.gradle.kts, добавившая зависимость от JGraphT, увеличила размер финального артефакта приложения на 5+ Мб. Стоило ли оно того — вопрос ещё открытый... 🤔
P.S. Приложенный видосик — скринкаст из программы Gephi, о которой я рассказывал в Полке около года назад. На нём запечатлён "игрушечный" граф на 11К ячеек, полученный экспортом (через JGraphT) из нашего приложения не под нагрузкой. Он вам что-нибудь напоминает? 😉
P.P.S. Если вам интересно подробнее почитать про реализацию топологической сортировки, поставьте под этим постом "🤓".Repost from Spring АйО
🍃 Владимир Плизга & Spring АйО
Продовые heap-дампы, конечно, штука полезная, пока вместе с ними не утекают логины, пароли и прочие сюрпризы для ИБ.
В нашем блоге вышла статья от Владимира Плизга — друга Spring АйО и автора, который умеет разбирать сложные JVM-темы без лишнего шума и магии.
Статья о том, как анализировать heap-дампы с прода и при этом не тащить за собой лишние риски:
🔘где в дампе могут всплыть sensitive-данные;
🔘как это проверять на практике;
🔘чем дезинфицировать дампы перед передачей;
🔘и когда брать Eclipse MAT, heap-dump-tool или hprof-redact.
Материал особенно полезен тем, кто работает с JVM, production-инцидентами, OutOfMemory и performance-разбором не только в теории.
🎂 У Владимира Плизга и Алексея Рагозина есть курсы по оптимизации производительности приложений на JVM. Инфа тут.
📎 Читать на Хабре: https://habr.com/ru/companies/spring_aio/articles/1031462/
Обещанный следующий пост выйдет чуть позже, а пока — минутка техники безопасности ⛑
На тренинге по анализу дампов памяти я упоминаю про обфускацию конфиденциальных данных, которые могли попасть в дамп, если он снят с боевого приложения. В подробностях я этот момент не освещаю, хотя он весьма важен и может предотвратить серьёзные проблемы 🚫
Тем, для кого эта тема актуальна, теперь можно погрузиться в неё глубже — на Хабре вышла моя новая статья в блоге у ребят из Spring АйО 👇🏼
Что есть в Java для работы с графами? 🕸
В предыдущем посте упоминался некий движок вычислений на основе графа ячеек (для электронных таблиц). По следам его создания хочу поделиться с вами списком отсмотренных мною opensource-библиотек на #Java для построения и обработки графов в памяти одной машины (т.е. графовые СУБД вне обзора) — вдруг кому пригодится 👀
JGraphT — серьёзный инструмент для серьёзных задач. Содержит реализации множества алгоритмов над графами и кучу интеграций с различными форматами. Хорошо подходит, если требуется глубокая аналитика, а экономия вычислительных мощностей не в приоритете 🚚
Apache Commons Graphs — формально вроде как вариант, но надпись "Last Published: 17 June 2011" на главной странице говорит сама за себя 💀
JUNG (не путать с JUG) — тоже весьма пожилой проект, который едва ли стоит рассматривать всерьёз, однако жизнь там всё-таки теплется за счёт того, что на этом проекте обкатываются всякие экспериментальные алгоритмы и фичи, которые, если приживаются, могут переехать в... 👇🏼
Guava Graphs — компонент одной из самых популярных Java-библиотек в дикой природе. Имеет немало общего с JUNG за счёт одного и того же разработчика. Предоставляет базовый API для работы с графами и минимальный набор готовых алгоритмов; например, умеет детектировать циклы в направленных графах, но не умеет строить их пути. Содержит немало фишек для оптимизации потребляемых ресурсов, например, учёт связей ячейки с самой собой 🪺
FastGraph — не Java, а Kotlin, но какая разница? А разница в скорости обхода — по словам автора, эта библиотечка при обходе в глубину уделывает Guava в 68 раз и JGraphT в 44 раза. Проект очень молодой (меньше года) и пока без большой аудитории, но, как видите, весьма амбициозный 🚀
(самоделка) — в целом, тоже вариант, если чётко понимать, в чём плюсы и минусы такого решения. За основу можно взять готовые наброски из Интернета или, конечно же, попросить ИИ 🤖
Есть и другие, более экзотичные решения, которые я не рассматривал. Если у вас был опыт работы с ними, расскажите, пожалуйста, в комментариях — будет интересно об этом узнать 🙌
Что из этого списка (и в каком виде) пригодилось мне в том проекте расскажу в следующем посте 📻
+3
Помимо начала Java конференции JPoint (кто уже там — шлите фотки!), сегодняшний день примечателен долгожданным релизом платформы AggreGate, над которой я нынче тружусь 👷♂️
В нём было исправлено несусветное количество багов (а сколько ещё добавлено🤭), а также реализовано несколько мощных фич, каждая из которых могла быть стать поводом для отдельного мажорного релиза 💪🏼 (но мы не ищем легких путей)
Из того, к чему довелось приложить руку мне:
— новый компонент Электронные таблицы (а-ля Excel/Google Sheets): там я отвечал за всякие хитрые кастомные функции, а также реализацию параллельного движка вычислений на основе графа ячеек 🪧
— переработанный Редактор выражений: в нём я реализовал всю бэкенд часть от построения многослойных деревьев вычислений в памяти до отдачи точечных результатов клиенту в ленивом режиме (чтобы не потопить) 🌳
Подробнее об этих и других фичах можно почитать в пресс-релизе: https://aggregate.digital/ru/news/release-64.html 🗞
Зная объём и сложность сделанных доработок, мне думается, что выпуск такого релиза — это только первые 90% работы над ним. Дальше, с помощью обратной связи пользователей, его предстоит как следует причесать и стабилизировать 🛠
Наверняка это будет не просто, но, как говорится, дорогу осилит идущий 🙂
Вопрос к знатокам #Java ❔
Что обозначает буква
D, используемая в качестве префикса кастомных JVM-опций?
(типа -Dcom.myapp.someFlag=true)
Я нередко прошу наших инженеров (пользователей разрабатываемой платформы) добавить в файл vmoptions ту или иную кастомную опцию, поддерживаемую бизнес-логикой. Они добавляют и тут же приходят жаловаться, что приложение теперь вообще не стартует, потому что unrecognized VM option... 😵💫
Приходится объяснять, порой извиняясь, что все такие опции нужно предварять не только дефисом, но и префиксом D, так как они кастомные. Они добавляют, всё начинает работать, но потом в догонку всё же спрашивают, а что означает эта дурацкая неочевидная буква? 🧐
Тут я включаю режим JVM-эксперта и такой:
Эээ... Ммм.. Нуу... Это от слова... "Dopolnitelno" 🤓
На просторах нижнего интернета появились якобы материалы наших курсов с Алексеем Рагозиным, которые можно приобрести по весьма скромной цене, в том числе вскладчину 🧱
Пожалуйста, не ведитесь на эту дичь 🦆
Дело в том, что многие материалы давно прошедших тренингов можно получить и бесплатно; достаточно попросить их у нас напрямую. А материалы новейших занятий (например, по мониторингу и метрикам JVM) всё ещё находятся у нас в проработке, поэтому не были и не могли быть слиты ни в каком адекватном виде 💎
Будьте бдительны и помните про сыр 🪤
P.S. Но если там что-то годное, скиньте мне тоже позырить 🤭👀
Одна из главных ценностей больших конференций (для меня) — возможность узнать, что вообще происходит в индустрии. Вот только сделать это через посещение отдельных докладов сложновато, если вообще возможно 🧠
Хорошо, что на JPoint для этого есть State of Java — отдельное выступление, полностью посвященное состоянию IT-ландшафта в релевантной нам части — в Java. Там можно как увидеть общий расклад, так и почерпнуть несколько интересных (порой забавных) инсайтов по узким темам 😉
Но чтобы представленные там результаты были адекватными, нужно охватить как можно больше респондентов, то есть, в том числе, нас с вами 👀
Для этого призываю вас пройти этот опрос от JUG.Ru, если вы причастны к разработке на Java: https://survey.jugru.org/soj2026 📋
Он не маленький (у меня ушло минут 20), но ради общего дела стоит это время потратить ✊
И даже если вы не собираетесь на ближайшую JPoint, всё равно имеет смысл заполнить, так как после конференции подробные результаты опроса будут разосланы всем его участникам 📩
Repost from Spring АйО
🍃 Когда тормозит Java? Владимир Плизга. Алексей Рагозин. Павел Кислов | Spring АйО Подкаст №59
😉 СМОТРЕТЬ НА YOUTUBE
😄 СМОТРЕТЬ В VK ВИДЕО
🥰 СМОТРЕТЬ НА RUTUBE
🗯 Чуть позже...
🤩 СЛУШАТЬ НА SPOTIFY
🤩 СЛУШАТЬ НА APPLE PODCASTS
💬 Аудио версию подкаста можно найти в комментариях
В Spring Айо вышла запись подкаста со мной и Алексеем Рагозиным, где мы говорили о современных подходах и инструментах для анализа и оптимизации производительности приложений на JVM, попутно раздавая спойлеры из наших курсов 🤭
Любопытно, что запись подкаста почти совпала с запуском курсов у самих ребят из Spring Айо — сейчас они набирают желающих на первый курс "Продвинутый Hibernate". Там можно будет глубоко погрузиться в реальные enterprise-кейсы и научиться их правильно "готовить" 🍳
+2
Первый беговой старт летнего сезона 2026 ✅
Новосибирский Весенний Полумарафон — самое близкое мне (в географическом смысле слова) беговое соревнование, так как проходит прямо по улицам Академгородка. А стартово-финишный городок расположен между Гусями — башнями АкадемПарка, которые соединены переходом на уровне 13-го этажа. Благодаря этому организаторы заявляют, что у этого старта самая большая в мире финишная арка (65 метров) 🤓
Вчера этот забег проходил уже в 11-ый раз, а для меня участие в нём стало 5-ым. И как обычно, бежалось весьма нелегко, так как зимой я практически не бегал, а лыжный сезон завершился вот только неделю назад. Можно подумать, что бег на лыжах и в кроссовках взаимозаменяемы, но не деле это не совсем так — в этих видах спорта задействованы разные группы мышц, и характер движения тоже разный. Из-за этого переход с одного на другой осенью и весной всегда даётся мне не просто 🥵
Как бы там ни было, вчера я пробежал за 1:32:05, став 107-ым из 449 мужчин на 21 км (результаты ещё уточняются). Это стало моим 2-ым результатом за 5 попыток (лучше было только в 22-ом году, и то всего на 4 сек). Ноги, конечно, до сих пор офигевают, но кого это теперь волнует?.. 🤷🏻♂️
Примечательным в этот раз стало моё "двойное участие": перед своим забегом я свозил туда пятилетнего сына, который пробежал свой первый соревновательный километр. И хотя в сравнении с другими ребятами его результат получился скромным (5:51, 9-ый из 12), свои основные задачи он уверенно выполнил: пробежал всю дистанцию без перехода на шаг и финишировал в нормальном расположении духа, без соплей и слёз. А на детских забегах, знаете ли, часто бывает иначе 🤭
Теперь мы с ним оба — обладатели медалек с цифровой белкой и номером года в двоичной форме 👨🏻💻
На этом беговой сезон 2026 можно считать открытым. А какие спортивные планы намечены у вас? 🙂
При подготовке к недавно анонсированному подкасту набрёл на любопытный инструмент javaperf — MCP-сервер, предоставляющий ИИ-агентам средства диагностики производительности JVM, например, возможность управлять JDK Flight Recorder'ом и анализировать его записи 🔍
Всего в его арсенале на сегодня 15 тулов ⚒️
Сам я ещё не игрался, поэтому рекомендовать не готов, но выглядит, как минимум, интересно. Если кто-то уже пощупал, поделитесь, пожалуйста, впечатлениями 👇🏼
#инструменты
+9
Лыжный сезон 2025-2026 завершён ✅
Он начался необыкновенно рано — в первой половине октября внезапно выпало много снега, который потом растаял за день. И хотя это была лишь забавная случайность, она стала предзнаменованием насыщенного, мощного лыжного сезона, фактически продлившегося с середины ноября по сегодняшний день 🗓
Я провёл его в той же лыжной школе, но под руководством другого тренера — бывшего члена олимпийской сборной, к которому давно хотел попасть. Совпадение или нет, но с ним мои лыжные навыки за этот сезон выросли весьма заметно, несмотря на то, что я занимаюсь лыжным бегом больше десяти лет, из которых пять — в различных школах ⛷
Скорее всего, никто не помнит, но в ноябре я говорил вам, что купил слот на главный старт этого сезона — лыжный марафон в Кемерово в середине марта (50 км свободным стилем). Ещё шутил, что благодаря публичному объявлению мне будет сложнее отмазываться, если всё же сольюсь. Должен признаться, да, писать эти строки сейчас не просто, ибо я всё же слился... 🙄
Причин, как обычно, несколько. Среди них и внезапные морозы, вычеркнувшие две недели очень важных тренировок, и большая нагрузка по работе, когда приходилось начинать рабочий день по Новосибирску, а заканчивать по Москве, в том числе по выходным, просиживая за компьютером часы плановых тренировок. Но главная (и, возможно, вытекающая из первых двух) причина — это неготовность головой. Трудно объяснить, но это как будто кричишь куда-то вглубь себя: "Ну что, готов!?", а в ответ не задорный отклик, не ободряющий гул и не звонкое эхо, а опустошающая тишина вперемешку с лёгким ропотом и невнятным шёпотом. Конечно, можно было собрать волю в кулак и стартануть назло хандре, но я чувствовал, что этот подвиг обойдётся мне дорого и вряд ли будет в удовольствие, а для меня, любителя, это важно. Поэтому я пропустил этот старт ✖️
Взамен его я взял слот в тех же числах на давно полюбившийся мне лыжный полумарафон (30 км) в родном Академгородке и пробежал его на удивление бодро — стал 18-ым из 53 лыжников, хотя раньше почти никогда не появлялся в верхней половине финишных протоколов. После него ещё стартовал на 10 км, но чувство незакрытого гештальта всё равно не покидало, ведь сопоставимого с марафоном забега так и не было. Отдушиной стало лишь замёрзшее море — только там и только в апреле удалось за один раз пробежать 50 км, пусть и не в соревновательном темпе. И вот тогда, наконец, начало появляться чувство удовлетворения ☺️
Венцом этого периода стал день сегодняшний, а точнее, его "Пляжный забег" — любительский лыжный старт на 10 км вдоль побережья всё ещё замёрзшего водохранилища. Я много слышал об этом соревновании, но всякий раз пропускал из-за слишком позднего срока проведения — стереотипы диванного аналитика не давали понять, как можно в середине апреля бегать на скорость на лыжах, да ещё по морю (звучит как бред). Но этот день показал — очень даже можно, и это новый прикольный опыт, который хочется получить вновь 👍
Я бестолковый бегун в том смысле, что бросаю бег на всю зиму, предаваясь лыжам. А заодно и хреновый лыжник, потому что на полгода бросаю лыжи в пользу бега и плавания вместо того, чтобы всё лето делать специальную силовую работу и гонять на лыжероллерах во имя сохранения техники. И каждый раз переход с одного на другое даётся с большим трудом. И каждый такой раз вспоминается надпись на футболке какого-то случайно встреченного атлета:
Бегаю я медленно, зато плаваю не быстро.И вот настало время совершить этот переход ещё раз. Каким будет следующий сезон загадывать не хочу; главное, чтобы он состоялся. Всем #спорт ✊
+2
Опираясь на эти возможности Borg, я построил такое решение:
1. Каждую ночь специальный скрипт по Cron'у создаёт новую версию всего фотоархива в локальном репозитории Borg на VPS (чтобы в случае отказа сервера бэкап пропал вместе с ним — ну удобно же, да?)
2. Этот же скрипт сразу реплицирует эту версию по SSH на удалённый репозиторий Borg, развернутый дома на NAS.
3. В случае реализации любого из перечисленных выше рисков, последняя версия архива в репозитории окажется испорченной. НО: у меня будет 3 месяца на то, чтобы откатиться к любой предыдущей — именно такую глубину хранения версий я заложил в настройки скрипта 🏛
Восстановление данных в случае Borg не тривиально, ведь он хранит их в своём собственном двоичном формате, и в этом, пожалуй, его главный недостаток. Но всё же предлагаемые им варианты восстановления покрывают все основные потребности. Варианта этих два:
1. Можно извлечь любую версию репозитория в указанную папку, как из архива. Это сделает файлы полностью независимыми от репозитория, но может потребовать много места на диске.
2. А можно смонтировать любую версию репозитория на диск в виде папки с файловой системой FUSE. Содержимое папки будет выглядеть как содержимое архива, но на самом деле это будут данные, прозрачно транслируемые Borg'ом. Благодаря этому папка не занимает места на диске (как при извлечении), но её содержимое доступно только для чтения и только при запущенном Borg'е 🤖
Я пользуюсь именно монтированием, чтобы быстро проверять содержимое копий фотоархива: походить по ним как по обычному дереву каталогов и позаглядывать в произвольные файлы. Работает это заметно медленнее, чем настоящая файловая система, но для таких целей сойдёт 🗂
Резюме
Построенное решение не на 100% страхует меня от риска потерять ценные данные. Но оно существенно снижает этот риск. Кроме того, оно создаёт достаточно удобный резервный канал доступа к фотографиям на случай, если основной канал (Immich) окажется недоступен.
В качестве доказательства — извлечённая из автоматически созданного архива фотография автора этих строк, сделанная до того, как он осознал сложности надёжного резервного копирования... 📸
Сага о семейном фотоархиве
Эпизод II. Воцарение бэкапа.
В начале года я рассказывал вам о том, что за новогодние праздники наконец-то построил себе решение для автоматической загрузки, надёжного хранения и гибкого поиска фоток и видео в семейном архиве. Оно основано на opensource сервисе Immich, который я развернул в облаке (на VPS) ☁️
И хотя, в целом, решение рабочее (3 месяца полёт нормальный), оно в каком-то смысле убивает саму идею владения своим фотоархивом, ведь теперь основное хранилище там, в облаке, а доступ к нему может пропасть по 1000 и 1 причине: от банальной неуплаты аренды до глобального сбоя в Интернете. Понимая это, я ещё тогда заказал себе домой сетевое хранилище (NAS) и к нему два одинаковых жестких диска по 2 ТБ, чтобы объединить их в RAID-1 массив и сохранять каждый байт информации дважды на случай отказа одного из дисков. К слову, ждать отказа не пришлось — один из дисков был куплен за полцены по распродаже на Озоне, приехал без гарантийного талона и сдох на первой же массовой записи. Мораль: скупой платит дважды 💰
По моей исходной задумке, данные фотоархива из облака (с VPS) должны быть скопированы домой (на NAS), а затем как-то автоматически обновляться, чтобы домашняя резервная копия оставалась актуальной. Но когда стал продумывать в деталях, осознал, что не понимаю, как именно они должны "обновляться":
— забирать каждый раз весь архив целиком не вариант, он весит 500 ГБ 🏋️
— значит, нужно переносить только разницу: некий дифф того, что изменилось с последнего переноса 🤏
Здесь важно отметить, что в разницу могут входить не только добавленные файлы, но и удалённые, потому что я периодически наведываюсь в архив, чтобы:
— удалить откровенно неудачные снимки
— убрать дубликаты, пропущенные машинными обучением
— стереть фото домов/цветов/котов, которые казались прикольными на момент снимка, а сейчас я даже не помню, где и когда (и на фига) это снято 🧐
Ок, вроде логичное требование, стоит его учесть. Но что если вдруг:
— я с бодуна наудаляю лишнего?
— или Immich сойдёт с ума (из-за бага) и грохнет часть архива?
— или вирус-шифровальщик проникнет на VPS и превратит все фото в тыкву, требуя выкуп?
Придуманное выше автоматическое обновление во всех этих случаях слепо отразит изменения на домашнюю резервную копию, умножив масштаб проблемы вместо её предотвращения. И, понятное дело, оно не сможет отличить "хорошее" удаление файлов от "плохого" 🤷♂️
Как быть? Решением видится такой компромисс, при котором все изменения всё же будут безусловно отражаться на резервной копии, но эти изменения при случае можно будет легко откатить. Осталось придумать, как это сделать 😏
Прямую наводку на ответ я нашёл в документации на Immich, в статье о расширенном резервном копировании. В качестве основного инструмента там предлагается Borg — бэкапер, написанный на Python. Borg помещает резервируемые файлы в т.н. репозиторий — версионируемое хранилище в собственном двоичном формате. Версионность здесь означает, что репозиторий может содержать множество слепков (версий) исходного архива, но хранятся они не в виде полных копий, а в виде неких диффов к предыдущим слепкам. Таким образом можно восстановить полное состояние архива на любой момент, для которого в репозитории есть слепок (версия). Дополнительно к этому нативно поддерживаются сжатие и шифрование данных в репозитории. Версии могут именоваться произвольным образом и в этом смысле похожи на коммиты в репозиториях Git, да и вся идея Borg во многом перекликается с Git'ом 🪞
Другая важная для меня фича Borg — работа с удалёнными репозиториями по SSH. Именно под неё я наконец-то заполучил себе у провайдера "белый" IP, настроил для него проброс порта на роутере и поднял вторую копию Borg на сетевом хранилище. Правда, вскоре после этого у провайдера не неделю отвалился интернет во всем подъезде. Еси чё, это не я 🙄
Repost from Spring АйО
🔥 Профилирование? Не, не слышал
Друзья, следующий подкаст пройдет не как обычно, а с очень уважаемыми IT индустрией людьми - Владимиром Плизга и Алексеем Рагозиным - настоящими гуру JVM performance.
У нас, конечно же, есть, что обсудить.
Например:
🔘Нужны ли профайлеры в 2к26?
🔘Зачем вообще запрофилировать?
🔘Поможет ли ИИ в анализе перформанса?
❓Но мы ждем и ваши вопросы в комментариях под постом. Самые интересные обсудим на подкасте!
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
