New We are sorting MAX channels into categories and territories. Read the news
Public channel ID 70941834150135

Махровый fullstack

А у нас в канале:

- Бэкенд, фронтенд и случайные мысли - в произвольных сочетаниях.
- Иногда литература, иногда всё, что взбредёт в голову.

Постится как душе просится.

Освежает разум. Возможны побочные эффекты.

Запаски:
- https://vk.ru/ordo_code
- https://t.me/ordo_code

Channels in category
2 093
This channel's rank
№ 1 481
Open category →
Open in MAX

Posts for the period

By reactions

Search and the whole feed — after free registration. Custom dates — with a subscription; the channel owner sees them for free. Sorting by reactions — with a subscription. Sign up

Loading…

Как перенести дамп в 20Гб и не потерять ни бита
— Когда mysqldump уже не вывозит —


TL;DR

mydumper + myloader — интересная альтернатива классическому mysqldump для больших баз. Параллельная выгрузка, дробление таблиц, многопоточный импорт и возможность возобновить восстановление.

Для многих вынос дата-центров яндекса в начале октября обернулся неудобствами. Для кого-то финансовыми потерями и потерей данных. И мне досталось рикошетом и небольшим опытом...

В инфраструктуре яндекса лежала БД с весьма чувствительными данными. И как только сервис стал доступен встала задача извлечь данные из облака и скачать на флешку и положить под матрац и перебраться на резервную площадку. В моём случае — перенести какие-то жалкие 20Гб.

По старинке снял дамп через mysqldump и начал заливать на резервный стенд. Поставил заливку и пошёл кофе пить, а когда вернулся получил вполне ожидаемы результат — ноутбук ушёл поспать. Вторая и третья поптыки так же завершились неудачно, но причина была уже или в сети, или в сервере, или в магнитных бурях — ноутбуку спать не давал и теребонькал его за тачпад и клавиши — а аплоад рвался на разных этапах.

Двое из ларца – mydumper/myloader

— mydumper — снимает дамп в несколько потоков, разбивая таблицы на отдельные файлы. Полноценного автоматического продолжения прерванного дампа нет, но если данные гарантированно не изменялись, можно сохранить полностью выгруженные таблицы и отдельно доснять недостающие.
— myloader — параллельно восстанавливает базу и умеет продолжать прерванный импорт через --resume при наличии файла состояния.

Первая часть марлезонского балета

Снимаем дамп — синтаксис максимально похож на привычный mysqldump:



mydumper \
--host=<DB_HOST> \
--port=3306 \
--user=<DB_USER> \
--ask-password \
--ssl \
--ssl-mode=VERIFY_IDENTITY \
--ca=CA.pem \
--database=<DB_NAME> \
--skip-ddl-locks \
--no-backup-locks \
--sync-thread-lock-mode=LOCK_ALL \
--outputdir=./backup-sql \
--threads=4 \
--rows=100000 \
--trx-tables \
--verbose=3

Из интересных параметров:
--threads=4 — четыре параллельных потока выгрузки.
--rows=100000 — ориентир на 100 тысяч строк в одном фрагменте большой таблицы.
--trx-tables — оптимизация блокировок для транзакционных таблиц, например InnoDB.
--skip-ddl-locks — отключает дополнительные DDL-блокировки.
--no-backup-locks — запрещает использовать backup-locks.
--sync-thread-lock-mode=LOCK_ALL — синхронизирует потоки с помощью блокировок таблиц вместо глобального FLUSH TABLES WITH READ LOCK.

Облачный mysql оказался довольно ревнивым к своим привилегиям: сначала не дал BACKUP_ADMIN, потом отказал в FLUSH TABLES. Поэтому пришлось подбирать режимы синхронизации эмпирически.

Вторая часть марлезонского балета

Когда дамп наконец снялся, выяснилось, что myloader на macOS категорически не желает отключать SSL, которого на целевом сервере не было.

Попытки уговорить его через волшебный --ssl-mode=DISABLED закончились ничем. Оказалось, моя сборка использовала MariaDB Connector/C, с которым возникли проблемы при отключении SSL.

Пришлось доставать docker-образ с mysql-клиентом:

docker pull mydumper/mydumper:latest

И дальше дело пошло веселее. Для импорта через Docker команда получилась такой:



docker run --rm -it \
--entrypoint myloader \
-v "$(pwd)/backup:/backup" \
mydumper/mydumper:latest \
--host=<DB_HOST> \
--port=3306 \
--protocol=tcp \
--user=<DB_USER> \
--ask-password \
--ssl-mode=DISABLED \
--database=<DB_NAME> \
--directory=/backup \
--threads=4

В итоге база переехала, данные остались целы.

Осталось только купить флешку и положить её под матрац.

Так...на всякий случай.

#mysql #mydumper #myloader #devops
Views: 5
Go to post Open in MAX Report an inaccuracy
Тысяча и одна ошибка

— Не будь как Шазерезада - экономь квоту —

TL;DR

Одна ошибка может случиться тысячу раз, но в Bugsnag достаточно сообщить о ней один раз за 15 минут.
Кто о чём, а вшивый о багах.

Подключил я намедни проект Laravel к Bugsnag для сбора ошибок в коде, дабы не проспать чего апокалиптического. Но апокалипсис устроил сам... 😈

Проект ворочает довольно приличными объёмами данных и в случае катаклизма Bugsnag быстро бы оброс репортами.

Сказано - сделано!

Буквально за ночь доступные квоты были израсходованы! Источник проблем оказался внешний, но лимиты от этого не увеличились. 🤔

Чтобы решить подобную проблему на будущее было решено немного притормозить отправку репортов.

Основные требования к решению:
— Единый механизм дросселирования для любых типов исключений;
— Легкое добавление нового типа репорта без изменения существующей логики;
— Возможность самостоятельно определять, какие ошибки считать одинаковыми;
— Единая точка настройки ограничений, без размазывания логики по всему проекту;
— Максимально использовать штатные возможности Laravel и избнжать вендорлок сервиса мониторинга;
— Ограничение частоты репортов не должно влиять на выполнение бизнес-логики или обработку самих исключений;

1. Готовим максимально простой интерфейс


interface ThrottledReport
{
public const int THROTTLE_MINUTES = 15;
public function throttleKey(): string;
public function throttleMinutes(): int;
}

Основная идея - это throttleKey. Именно он определяет, какие ошибки считать одинаковыми. А throttleMinutes - определяет на сколько притормозить отправку повторяющихся репортов.

2. Добавляем trait с реализацией таймера по умолчанию


trait HasReportThrottle
{
public function throttleMinutes(): int
{
return static::THROTTLE_MINUTES;
}
}

3. Далее подключаем обработку эксепшена в bootstrap


->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
if ($e instanceof ThrottledReport) {
return Limit::perMinutes($e->throttleMinutes(), 1)
->by($e::class . ':' . $e->throttleKey());
}

return null; // остальные исключения без ограничений
});
})

Тут есть важный нюанс: троттлинг работает на уровне Laravel Exception Handler, поэтому подавленные репорты не попадут не только в Bugsnag, но и в стандартные логи приложения. Но для моего решения — это приемлемо.

И ещё момент — важно учесть, что лимит хранится в кэше, поэтому важно иметь настроенный механизм кэширования. Например, redis.

4. Создаём своё исключение

final class DoNotSpamException extends RuntimeException implements ThrottledReport
{
use HasReportThrottle;

public const int THROTTLE_MINUTES = 60;

public function throttleKey(): string
{
return 'my-unique-key-for-error';
}
}



5. И используем
report(new DoNotSpamException('Something went wrong'));

‼️ Но тут важно помнить — это решение уменьшает частоту сообщений об ошибках, а не частоту возникновения самих ошибок. О количестве и качестве ошибок заботимся сами! ☕️

#php #laravel #exception #throttling #backend #web #разработка
Views: 10
Go to post Open in MAX Report an inaccuracy
Loading image…
Территориальный конфликт: война за 172.19.0.0/16

— Когда контейнер ищет внешний сервер у себя под носом —

TL;DR

Разбираемся, куда пропадают пакеты, при чём тут маршрутизация и как закончить войну за IP-адреса.

Кто‑то скажет — сделал из мухи слона! Да у меня каждый день такие приключения! Но для меня получился интересный кейс, и я лучше зафиксирую его для потомков.

Случилось страшное – из docker-контейнера недоступен удалённый хост. При этом с хост-машины никаких проблем с доступность нет – что надо пингуется, что пингуется, то роутится. Хост для определённости - 172.19.10.50.

WTF?!

Диагностика. Раунд первый

Для начала проверяем подсети, которые создал docker:


docker network inspect $(docker network ls -q) \
--format '{{.Name}}: {{range .IPAM.Config}}{{.Subnet}} {{end}}'

В моём случае обнаружились:


bridge: 172.17.0.0/16
traefik: 172.19.0.0/16
app-backend: 172.23.0.0/16
app-notifications: 172.29.0.0/16

В целом, картина уже ясна – видим пересечение сетей docker и внешнего хоста.

Для пущей убедительности проверяем маршрут в сетевом пространстве docker:


docker run --rm --network host \
nicolaka/netshoot \
ip route get 172.19.10.50

Бинго!


172.19.10.50 dev br-caf585119744 src 172.19.0.1

Cреда docker считает, что внешний сервер находится непосредственно в локальной сети.

Первое лечение. Неудачное

Перенёс сеть traefik в другой диапазон – 10.250.10.0/24, предварительно убедившись, что подсеть не используется где-то ещё.


networks:
traefik:
name: traefik
driver: bridge
ipam:
config:
- subnet: 10.250.10.0/24

Тут надо сказать, что app-backend и app-notifications используют общие external-сети. Поэтому сети создавались отдельно, а не средствами docker compose.



docker network create app-backend
docker network create app-notifications

Остановил использующие сеть проекты, удалил старые сети, поднял сети заново.
И… внешний сервер снова недоступен.

WTF?!

Диагностика. Раунд второй

Смотрим подсети ещё раз:


traefik: 10.250.10.0/24
app-backend: 172.18.0.0/16
app-notifications: 172.19.0.0/16

Постоянству docker можно позавидовать – не отпускает подсеть 172.19.0.0/16.

Лечение. Теперь окончательное

После повторного конфликта стало понятно: доверять docker автоматическое распределение адресов конкретно в этом окружении — идея так себе.
И поэтому сети создаём с явным указанием подсетей:


docker network create \
--driver bridge \
--subnet 10.250.11.0/24 \
app-backend

docker network create \
--driver bridge \
--subnet 10.250.12.0/24 \
app-notifications

Проверяем итоговую адресацию:


docker network inspect traefik app-backend app-notifications \
--format '{{.Name}}: {{range .IPAM.Config}}{{.Subnet}}{{end}}'

Получаем:


traefik: 10.250.10.0/24
app-backend: 10.250.11.0/24
app-notifications: 10.250.12.0/24


Проверяем доступность внешнего сервера:


docker run --rm \
--network container:notifications-php \
nicolaka/netshoot \
ping -c 3 172.19.10.50

И вот теперь всё заработало как надо!
Для кого-то это база, а для меня кирпичик опыта!

Полезное открытие: nicolaka/netshoot

У минималистичных docker-образов, таких как alpine, debian:slim часто отсутствуют даже базовые сетевые утилиты: ping, curl, nslookup, ip. Когда возникает необходимость разобраться с сетевыми проблемами, то удобнее использовать образ nicolaka/netshoot с целым арсеналом сетевых инструментов.

Но самое интересное — возможность подключиться к сетевому пространству уже работающего контейнера:


docker run --rm -it \
  --network container:notifications-php \
  nicolaka/netshoot

Теперь мы находимся в отдельном диагностическом контейнере, но используем сетевые интерфейсы, IP-адреса и маршруты notifications-php.
При этом исходный контейнер не пересобирается, а после выхода диагностический контейнер автоматически удалится благодаря --rm.

Отличная DevOps-выручалочка. Крайне рекомендую.

#docker #dockercompose #networking #devops
Views: 10
Go to post Open in MAX Report an inaccuracy
Loading image…
Великий исход из Bitbucket
– 347 репозиториев. Нормальный человек бы удалил –

tl;dr

Как перенести 347 репозиториев и не сойти с ума. Чудеса автоматизации с go, api и git –mirror

А помните в году так в 2023 пронеслась новость - Bitbucket заблокировал репозитории для разработчиков из России? Ох и испугался же я - ибо все существующие репозитории были на нём. Но делать нечего - взял и скачал все репозитории одним архивом на скорую руку в надежде в будущем перезалить куда-нибудь да и забросил его на антресоль.

Шло время. Репозитории настаивались как хорошее вино и покрывались слоем благородной пыли, а переносить на новый сервис руки так и не доходили - это ж надо всё в ручном режиме переносить, но ни времени ни желания это делать не было.

Но тут что-то торкнуло заглянуть в аккаунт Atlassian.

А, Батюшки-святы! Аккаунт живой! Репозитории на месте! Дальше судьбу решил не искушать и таки перенести всё богатство.

Вооружившись Go, документацией к API и некоторым количеством терпения был создан скрипт, переносящий репозитории из Bitbucket -> GitFlic. Но получился небольшой миграционный конвейер.

Bitbucket API → git clone --mirror → GitFlic API → git push --mirror

Работает он в нескольких режимах: скачивание репозиториев и режим миграции.

По API Bitbucket скрипт получает полный список репозиториев с поддержкой постраничной загрузки и после чего каждый репозиторий клонируется локально.

Из интересного - git clone выполняется с флагом --mirror: создаётся bare-репозиторий, но без рабочей копии, но с полной историей, ветками, тегами и остальными git-особенностями, что заодно уменьшает объём промежуточных данных на диске.

Далее проверяется наличие проекта в GitFlic и если он не найден - создаётся через API GitFlic, после чего локальное зеркало отправляется командой git push --mirror.

Для безопасного повторного запуска уже скачанные зеркала пропускаются, а в консоль по ходу дела сыпется информация о процессе миграции.

И так несколько сотен раз.

На этом можно было бы и закончить, но тут появился Андрей (Андрей, привет! 🍺), заставивший пошебуршать по проектам, а меня дополнительно обуяло любопытство - что же у меня скопилось за почти 20 лет работы.

Скопилось 347 уникальных репозиториев — разной степени паршивости и полезности, конечно же, но всё же.

Содержат 29 133 уникальных коммитов, из которых 12 199 моих.

И, естественно, что коммиты не все мои - командную разработку никто не отменял. Но так или иначе, к каждому проекту были приложены собственные руки, мозги и нервы.

По языкам получился такой расклад:

PHP -- 395 293
JavaScript -- 141 132
CSS / SCSS -- 70 766
HTML -- 14 387
Dart -- 6 037
TypeScript -- 5 731
Swift -- 2 945
Ruby -- 2 666
Kotlin -- 1 847
Vue -- 1 545
Python -- 1 540


Также были найдены археологические слои: C/C++, Objective-C, голый SQL с триггерами и функциями, Java и 112 файлов на Go.

С типами проектов всё довольно стандартно, как подытожил ИИ: технологический профиль портфеля преимущественно веб-ориентированный. А то мы не знали ;)

В итоге, несколько часов программирования и 20Гб архивов плавно переехали на новое, не подверженное политическим веяниям, безопасное хранилище, а на SSD освободилось 40Гб. Почему 40? А у меня, оказывается, два архива было. Для надёжности, наверняка, а не благодаря Альцгеймеру ;)

#git #bitbucket #gitflic #golang #автоматизация #миграция
Views: 18
Go to post Open in MAX Report an inaccuracy
VKontakte Telegram Odnoklassniki
Favorites at datool.ru

Follow the channel in your account

Sign up at datool.ru to add the channel “Махровый fullstack” to favorites and track its metrics.

Sign up
For channel owners

Verify your channel

Sign in or sign up at datool.ru, then add our bot to the channel following the instructions in your account. Once ownership is verified, the lock disappears and channel management becomes available.

Sign in or sign up

Report an inaccuracy

Describe what needs fixing. If you have a source, add a link.

You are leaving datool.ru

The link leads to an external website. datool.ru is not responsible for its content — check the address before entering passwords or payment details.

Redirecting in 10 s

Go