Русский
Русский
English
Статистика
Реклама

Миграции

Перевод Как мы потерпели неудачу, а затем преуспели в переходе на TypeScript

02.06.2021 18:06:54 | Автор: admin

К старту курса о Fullstack-разработке на Python, где также рассматривается TypeScript, мы перевели статью о миграции в Heap.io компании, которая предоставляет платформу аналитики продуктов, c языка CoffeeScript на TypeScript; TS в Heap.io начали использовать более 4 лет назад. Несмотря на широкое предпочтение TypeScript среди инженеров, миграция была медленной, а чёткого пути к 100 % кода TS не было.


На самом деле, если цель состояла в том, чтобы полностью переключиться на TS, мы двигались в неправильном направлении. Да, код на TS добавлялся, но количество кода на CoffeeScript росло быстрее. TypeScript и CoffeeScript нацелены на один и тот же рантайм Node.js, который, как ожидалось, должен был облегчить переход, но, несмотря на желание большого числа разработчиков перейти на TypeScript, мы не набрали большого импульса и не направлялись к будущему без CoffeeScript.

В начале 2019 года мы возобновили наши усилия по переходу с CoffeeScript на TypeScript. На этот раз мы решили: чтобы вдохнуть жизнь в нашу миграцию, от нас потребуется переосмысление стратегии преобразования существующего кода. Это привело нас к новому набору руководящих принципов. Следуя им, мы превратили то, что казалось трудноразрешимой задачей, в управляемый, хорошо понятный процесс и сумели значительно изменить форму кривых на графике:

Количество строк кода в разработкеКоличество строк кода в разработке

Миграция стека в равной степени касается и технологий, и людей

Самое важное, что мы осознали в ходе этого нового процесса, заключалось в том, что успешная миграция должна быть сосредоточена не только вокруг технологий, но и вокруг людей. Как инженеров нас, как правило, привлекают технические мотивы и (особенно) детали миграции: нам всем нравится идея обрести уверенность в нашем коде, перейдя на TypeScript, а также мы рады потратить часы на то, чтобы точно выяснить, как настроить конфигурацию TypeScript.

Как оказалось, более важные соображения лежат на стороне уравнения с людьми: как заставить наших коллег принять новую парадигму? Этот вопрос поднимает множество других. Например: как сделать разработчиков счастливыми и продуктивными в этой новой модели? Какие барьеры или трения мы можем устранить, чтобы преимущества новой парадигмы стали очевидными? Ответы на эти вопросы помогли нам разработать новый процесс миграции, которым сегодня очень гордимся.

Новый опыт разработки должен предлагать очевидное улучшение

Мы быстро поняли, что для того, чтобы ввести команду в новую парадигму, наши разработчики должны были чувствовать, что они будут более продуктивными, когда будут работать на TS. Если бы команда рассматривала это изменение просто как нейтральный сдвиг между синтаксисами, мы бы никогда не получили общее согласие. Если бы переключение не сделало каждый их день продуктивнее, инерция победила бы даже в случае предпочтения инженером типизированного кода.

Мы начали с определения областей кодовой базы, которые в случае преобразования приведут к существенному повышению производительности, зная, что стратегическое преобразование файлов будет привлекательнее произвольного преобразования. Например, наш слой доступа к данным (ORM) покрывает всё, и большинство файлов каким-то образом работают с ORM. Введение типов на уровне доступа к данным было множителем всей работы; почти каждый преобразованный в будущем файл выиграет от хорошо типизированных моделей баз данных и утилит.

Кроме того, мы сделали приоритетом оснастку и конфигурацию. Большинство разработчиков использовали один из нескольких редакторов, поэтому мы создали конфигурации редактора, которые будут работать сразу. Мы также добавили конфигурации отладки, облегчающие установку точек останова и пошаговое выполнение кода.

Наконец, мы сошлись на наборе согласованных правил линтинга, которые позволили нам писать код в едином стиле по всей организации и сделать переход более удобным.

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

Технические барьеры нужно ломать

Когда мы начали анализировать шаблоны внедрения TypeScript, стало ясно, что использование TypeScript для наших инженеров не было простым, им часто приходилось импортировать специальные утилиты (ts-node/register) или создавать промежуточные файлы CoffeeScript, которые не делали ничего, кроме импорта их эквивалентов TypeScript. Короче говоря, история взаимодействия языков существовала, но требовала много бойлерплейта и слишком много проб и ошибок.

Были ли эти трения основным блокирующим миграцию фактором? Трудно сказать, но мы знаем, что путь наименьшего сопротивления даёт большую силу, и, если мы хотим, чтобы люди переключались, нам нужно сделать TypeScript простым и очевидным выбором.

Чтобы добиться этого, мы отдавали приоритет усилиям, которые позволили бы разработчикам писать на TS в любом компоненте или сервисе. Будь то бэкенд, фронтенд, скрипты или задачи devops, мы хотели, чтобы наши инженеры могли писать код в TypeScript и чтобы он просто работал. В итоге мы прописали переменную среды NODE_OPTIONSс -r ts-node/register, чтобы существующие (использующие команду coffee для запуска файлов CoffeeScript) рабочие процессы также продолжали работать.

Преобразование должно быть простым, безопасным и автоматизированным

Миграция на другой язык может быть рискованной: то, что может показаться эквивалентным синтаксисом между CoffeeScript и ES6/TypeScript, на самом деле может вообще не быть эквивалентным. И разработчики могут рассматривать преобразование как хорошую возможность для рефакторинга, а это ещё хуже; переписывание делает рискованную миграцию ещё более рискованной.

Чтобы снизить риски, нам нужен был дисциплинированный процесс преобразования файлов без введения регрессий, а также нужно было помочь уйти от соблазна сделать больше, чем просто конверсия один языка в другой. Кроме того, процесс должен был быть быстрым.

Мы остановились на процессе из двух этапов это автоматическое преобразование файла CoffeeScript, за которым немедленно следует ручное добавление аннотаций основных типов и изменений, связанных с линтером. Ключевой момент состоял в том, чтобы противостоять искушению рефакторинга кода любым осмысленным способом. Такое преобразование становится механическим применением простых, безопасных правил, не влияющих на поведение во время выполнения.

Для первоначального преобразования мы использовали скрипт, преобразующий файл .coffee в файл .ts. В целях перехода от CoffeeScript к JavaScript ES6 Под капотом работал decaffeinate. Поскольку весь JavaScript ES6 является синтаксически правильным TypeScript, на выходе получался рабочий файл. (Мы обнаружили, что decaffeinate очень зрелый и надёжный инструмент.) В истории Git шаг преобразования представлен одним отдельным коммитом.

Однако работа ещё не была закончена. Мы используем TypeScript в строгом режиме, поэтому была отключена такая функция, как "implicit any". Мы использовали это окно преобразования как возможность создавать аннотации типов для элементов, где вывод типов был невозможен. Также мы избегали использования any в этой фазе, вместо этого выбрав более строгий неизвестный. Цель на этом этапе состояла в том, чтобы внести изменения, которые не приведут к изменению поведения во время выполнения. Мы не занимались никаким рефакторингом, а просто выполняли минимальный объём работы, чтобы привести код в состояние, в котором он компилировался, линтовался и проходил тесты.

Если зависимости модуля уже были преобразованы в TypeScript, усилий нужно было очень мало: о типах в основном заботились с помощью импортированных модулей. Это привело к эффекту снежного кома, когда по мере преобразования большего количества модулей преобразование в целом стало проще и безопаснее.

Этот второй шаг также прошёл отдельным коммитом; такой подход сильно упростил ревью: ревьюер мог легко увидеть, какие изменения были внесены после шага c decafeinate.

Весь процесс был задокументирован в руководстве по преобразованию машинописного текста. Любой разработчик кучи может преобразовать файл и открыть запрос на вытягивание, чтобы он был объединён всего за 5 минут.

#typescript: канал дискуссий и вопросов

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

Для этого мы создали канал #typescript в Slack и гарантировали, что застрявшие разработчики могут выйти из положения. Разработчики, управляющие миграцией, стали отвечать на вопросы и остановились на поиске общих проблем и камней преткновения. Если возникала одна и та же проблема, они знали, что нужно сделать лучше.

Разработчики должны знать, что на любые вопросы о языке и инструментах, которые у них есть, будут даны быстрые ответы. Мы решили, что чемпионы TypeScript должны отдать приоритет ответам, а не собственной работе. Хотя такой подход замедлил их работу, он также устранил ряд потенциально серьёзных препятствий в миграции.

Отслеживание прогресса

С самого начала мы знали, что массовая миграция за одну ночь невозможна и что, скорее всего, для завершения процесса потребуется год или больше. И это было прекрасно: мы решили, что прогресс важнее совершенства.

Отслеживание усилий во времени оказалось полезным. Таким образом возможно было понять, продолжается ли прогресс в миграции. Для визуализации количества строк мы воспользовались Grafana. Вот ещё одна визуализация, показывающая количество файлов во времени:

Уважающее инженеров руководство

К сожалению, одним из факторов, который в наибольшей степени способствует вашему успеху в подобных проектах находится чуть вне вашего контроля, является готовность руководства предоставить вам пространство для выполнения проекта. Проект миграции был направлен снизу вверх: он начался с того, что я представил план руководителям команд и руководителям инженерных подразделений, и после его утверждения нам предоставили свободу выбора наилучшего способа его реализации. Несмотря на то, что мы говорили о включающем сотни тысяч строк переходе, миграция была на 100 % внутренней.

В целом лучшие защитники подобных проектов это, как правило, команды, ежедневно использующие инструмент, в направлении которого идёт миграция. В Heap нам повезло: наше руководство тоже верит в это, а вера поддерживает идею о том, что лидерство наиболее полезно, когда даёт инженерам возможности и уходит в сторону.

Продолжая эту миграцию, мы надеемся продолжать учиться и использовать то, чему мы научились, чтобы ещё больше упростить следующий проект.

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

Ситуации могут быть разными, и если вам не хочется ограничивать себя только одним подходом, владеть языками с разными системами типов, то вы можете обратить внимание на наш курс Fullstack-разработчик на Python, где, кроме Python и TS, студенты также изучают чистый JavaScript.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Практические истории из наших SRE-будней. Часть 3

25.12.2020 10:05:30 | Автор: admin
Рады продолжить цикл статей с подборками из недавних вызовов, случившихся в нашей повседневной практике эксплуатации. Для этого мы описываем свои мысли и действия, которые привели к их успешному преодолению.



Новый выпуск посвящён опыту с неожиданно затянувшейся миграцией одного Linux-сервера, знакомству с Kubernetes-оператором для ClickHouse, способу ускорить восстановление данных в сломавшейся реплике PostgreSQL и последствиями обновления CockroachDB. Если вы тоже думаете, что это может быть полезно или хотя бы просто интересно, добро пожаловать под кат!

История 1. Затянувшийся перенос сервера в виртуальную машину


План миграции


Казалось, что может пойти не так, если требуется перенести legacy-приложение с железного сервера в виртуальную машину? У приложения и его инфраструктуры привычный, хорошо понятный стек: Linux, PHP, Apache, Gearman, MySQL. Причины для миграции тоже обычны: клиент захотел уменьшить плату за хостинг, отказавшись от реального сервера, на котором остался только вспомогательный сервис (парсер соцсетей).

Вообще говоря, конечно, бывают и другие причины для миграции (например, многочисленные удобства в последующем обслуживании инфраструктуры и её масштабирования), но не буду заострять на этом внимание.

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

Общий план выглядит следующим образом:

  1. Произвести очистку сервера, поняв, сколько ресурсов требуется.
  2. Подготовить виртуальный сервер, выделить память, ядра, зарезервировать IP-адреса.
  3. Если требуется минимальный простой организовать внешний балансировщик, который можно переключить на свежесозданный виртуальный сервер, или же запустить копию приложения.
  4. Произвести начальную загрузку с образа выбранной ОС/дистрибутива, содержащего все необходимые драйверы, чтобы скопировать данные в виртуальную машину тем или иным способом.
  5. Создать chroot, чтобы исправить загрузчик системы.
  6. Переключить пользовательские запросы или сервисные задачи на новую систему.

Что ж, воспользуемся таким планом в очередной раз, попутно выясняя, какие нестандартные ситуации, оказывается, могут возникать.

Подготовка к миграции


Перед началом непосредственного переезда мы решили почистить сервер. На нем было занято 300 Гб диска, однако среди них удалось найти давно потерявшие актуальность бэкапы, совсем старые логи, а также излишки в базе данных (см. ниже). В результате файловую систему удалось оптимизировать до 60 Гб.

Отдельно хочется рассказать про похудение MySQL. Дело в том, что MySQL изначально была версии 5.5 и настроена без innodb_file_per_table. Из-за этого, как многие могут догадаться, файл ibdata1 разросся до 40 Гб. В таких ситуациях нам всегда помогает pt-online-schema-change (входит в состав Percona Toolkit).

Достаточно проверить таблицы, которые находятся в shared innodb tablespace:

SELECT i.name FROM information_schema.INNODB_SYS_TABLES i WHERE i.space = 0;

после чего запустить упомянутую команду pt-online-schema-change, которая позволяет совершать различные действия над таблицами без простоя и поможет нам совершить OPTIMIZE без простоя для всех найденных таблиц:

pt-online-schema-change --alter "ENGINE=InnoDB" D=mydb,t=test --execute

Если файл ibdata1 не слишком велик, то его можно оставить. Чтобы полностью избавиться от мусора в файле ibdata1, потребуется сделать mysqldump со всех баз, оставив только базы mysql и performance_schema. Теперь можно остановить MySQL и удалить ibdata1.

После перезапуска MySQL создаст недостающие файлы системного namespace InnoDB. Загружаем данные в MySQL и готово.

Подготовка дисков и копирование


Казалось бы, теперь можно произвести перенос данных с помощью dd, однако в данном случае это не представлялось возможным. На сервере был созданный с md RAID 1, который не хотелось бы видеть на виртуальной машине, так как её разделы создаются в Volume Group, которая создана на RAID 10. Кроме того, разделы были очень большие, хотя занято было не более 15% места. Поэтому было принято решение переносить виртуальную машину, используя rsync. Такая операция нас не пугает: мы часто мигрировали серверы подобным образом, хотя это и несколько сложнее, чем перенос всех разделов с использованием dd.

Что потребуется сделать? Тут нет особой тайны, так как некоторые шаги полностью соответствуют действиям при копировании диска с dd:

  1. Создаем виртуальную машину нужного размера и загружаемся с systemrescuecd.
  2. Делаем разбивку диска, аналогичную серверу. Обычно нужен root-раздел и boot с этим поможет parted. Допустим, у нас есть диск /dev/vda:

    parted /dev/vdamklabel gptmkpart P1 ext3 1MiB 4MiB t 1 bios_grubmkpart P2 ext3 4MiB 1024MiBmkpart P3 ext3 1024MiB 100%t 3 lvm 
    
  3. Создадим на разделах файловые системы. Обычно мы используем ext3 для boot и ext4 для root.
  4. Монтируем разделы в /mnt, в который будем chroot'иться:

    mount /dev/vda2 /mntmkdir -p /mnt/bootmount /dev/vda1 /mnt/boot
    
  5. Подключим сеть. Актуальные версии systemrescuecd построены на ArchLinux и предполагают настройку системы через nmcli:

    nmcli con add con-name lan1 ifname em1 type ethernet ip4 192.168.100.100/24 gw4 192.168.100.1 ipv4.dns "8.8.8.8 8.8.4.4"nmcli con up lan1
    
  6. Копируем данные: rsync -avz --delete --progress --exclude "dev/*" --exclude "proc/*" --exclude "sys/*" rsync://old_ip/root/ /mnt/
  7. Затем монтируем dev, proc, sys:

    mount -t proc proc /mnt/procmount -t sysfs sys /mnt/sysmount --bind /dev /mnt/dev
    
  8. Зайдем в полученный chroot: chroot /mnt bash
  9. Поправим fstab, изменив адреса точек монтирование на актуальные.
  10. Теперь надо восстановить загрузчик:
    1. Восстановим загрузочный сектор: grub-install /dev/vda
    2. Обновим конфиг grub: update-grub
  11. Обновим initramfs: update-initramfs -k all -u
  12. Перезагрузим виртуалку и загрузим перенесенную систему.

Используя этот алгоритм, мы перенесли сотни виртуальных машин и серверов, однако в этот раз что-то пошло не так

Проблема и её решение


Система упорно помнила различные дисковые подразделы, которые были до переноса на сервере. Проблем разобраться с mdadm не было достаточно просто удалить файл /etc/mdadm/mdadm.conf и запустить update-initramfs.



Однако система все равно пыталась найти еще и /dev/mapped/vg0-swap. Оказалось, что initrd пытается подключить swap из-за конфига, который добавляет Debian installer. Удаляем лишний файл, собираем initramfs, перезагружаемся и снова попадаем в консоль busybox.

Поинтересуемся у системы, видит ли она наши диски. lsblk выдает пустоту, да и поиск файлов устройств в /dev/disk/by-uuid/ не даёт результатов. Выяснилось, что ядро Debian Jessie 3.16 скомпилировано без поддержки virtio-устройств (точнее, сама поддержка, конечно, доступна, но для этого нужно загрузить соответствующие модули).

К счастью, модули добавляются в initrd без проблем: нужные модули можно либо прописать в /etc/initramfs-tools/modules, либо изменить политику добавления модулей в /etc/initramfs-tools/initramfs.conf на MODULES=most.



Однако магии и в этот раз не произошло. Даже несмотря на наличие модулей система не видела диски:



Пришлось в настройках виртуальной машины переключить диски с шины Virtio на SCSI такое действие позволило загрузить виртуальную машину.

В загруженной системе отсутствовала сеть. Попытки подключить сетевые драйверы (модуль virtio_net) ни к чему не привели.



Дабы не усложнять задачу и не затягивать переключение, было решено переключить и сетевые адаптеры на эмуляцию реального железа сетевой карты Intel e1000e. Виртуальная машина была остановлена, драйвер изменён, однако при запуске мы получили ошибку: failed to find romfile "efi-e1000.rom".



Поиск дал интересный результат: ROM-файл был потерян в Debian некоторое время назад и возвращать его в пакет коллеги не собирались. Однако этот же файл фигурирует в пакете ipxe-qemu, откуда и был с успехом взят. Оказалось, достаточно распаковать этот пакет (ipxe-qemu) и скопировать /usr/lib/ipxe/qemu/efi-e1000.rom в /usr/share/qemu/efi-e1000e.rom. После этого виртуальная машина с эмулированным адаптером начала стартовать.

Вы думаете, это всё? Конечно же, нет, когда в деле замешан e1000e Данный драйвер известен тем, что может под нагрузкой начать перезапускать сетевой адаптер. Именно это и произошло, когда мы стали загружать базу данных для приложения. Пришлось прибегнуть к старому способу с отключение аппаратного offload:

ethtool -K eth0 gso off gro off tso off

Только после этого стало возможным нормализовать работу системы и наконец-то запустить приложение. Наверняка возможен и другой путь, однако его поиск скорее всего занял бы больше времени, что не входило ни в наши интересы, ни в область понимания клиента: ведь на миграцию был заложен конкретный срок.

История 2. Безопасность для Kubernetes-оператора ClickHouse


Не так давно мы начали использовать ClickHouse operator от Altinity. Данный оператор позволяет гибко разворачивать кластеры ClickHouse в Kubernetes:

  • с репликацией для повышенной надёжности;
  • с шардами для горизонтального масштабирования.

Однако мы столкнулись с неожиданной проблемой: невозможностью задать пароль для юзера default, который используется для работы remote_servers по умолчанию. Всё дело в том, что в шаблонах генерации конфигов кластера нет возможности определения пароля для remote_servers. По этой причине невозможна одновременная работа с distributed-таблицами она будет падать с ошибкой:

[2020-11-25 15:00:20] Code: 516, e.displayText() = DB::Exception: Received from chi-cluster-cluster-0-0:9000. DB::Exception: default: Authentication failed: password is incorrect or there is no user with such name.

К счастью, ClickHouse позволяет сделать whitelist с использованием rDNS, IP, host regexp Так можнодобавить в конфиг кластера следующее:

      users:        default/networks/host_regexp: (chi-cluster-[^.]+\d+-\d+|clickhouse\-cluster)\.clickhouse\.svc\.cluster\.local$

Тогда кластер сможет нормально функционировать. В репозитории оператора есть issue по этому поводу (мы не забыли добавить туда и свой workaround). Однако не похоже, что там будут какие-то движения в ближайшее время из-за того, что потребуется хранить пароли в конфигурации remote_servers.

История 3. Ускоренная перезаливка реплик PostgreSQL


К сожалению, ничто не вечно и любая техника стареет. А это приводит к различным сбоям. Один из таких сбоев произошел на реплике баз данных PostgreSQL: отказал один из дисков и массив перешёл в режим read only.

После замены диска и восстановления работы сервера встал вопрос: как же быстро ввести его в строй, учитывая, что база у проекта довольно объемна (более 2 терабайт)?

Дело осложнялось тем, что репликация была заведена без слотов репликации, а за время, пока сервер приводили в чувство, все необходимые WAL-сегменты были удалены. Архивацией WAL в проекте никто не озаботился и момент для её включения был упущен. К слову, сами слоты репликации представляют угрозу в версиях PostgreSQL ниже 13, т.к. могут занять всё место на диске (а неопытный инженер о них даже не вспомнит). С 13-й версии PgSQL размер слота уже можно ограничить директивой max_slot_wal_keep_size.

Итак, казалось бы, надо вооружаться pg_basebackup и переливать базу с нуля, но по нашим подсчетам такая операция заняла бы 9 дней, и всё это время основной сервер БД работал бы без резерва. Что же делать? У нас же есть почти актуальные файлы, некоторые из которых база вообще не трогает, так как это старые партиции партицированных таблиц Но pg_basebackup требует чистой директории для начала копирования. Вот бы изобрести метод, который бы позволил докачать базу!..

И тут я вспомнил про исходный метод, которым мы снимали бэкапы еще во времена PostgreSQL 9.1. Он описывается в статье документации про Continuous Archiving and Point-in-Time Recovery. Суть его крайне проста и основана на том, что можно копировать файлы PgSQL, если вызвать команду pg_start_backup, а после процедуры копирования pg_stop_backup. В голове созрел следующий план:

  1. Создадим слот репликации для реплики командой на мастере:

    SELECT pg_create_physical_replication_slot('replica', true);
    

    Важно, чтобы при создании второй аргумент функции был именно true тогда база начнёт немедленно собирать сегменты WAL в этот слот, а не будет ждать первого подключения к нему.
  2. Выполним команду на мастере:

    SELECT pg_start_backup('copy', true);
    

    Снова важно, чтобы при создании второй аргумент функции был именно true тогда база немедленно выполнит checkpoint и можно будет начать копирование.
  3. Скопируем базу на реплику. Мы для этой цели использовали rsync:

    rsynс -avz --delete --progress rsync://leader_ip/root/var/lib/postgresql/10/main/ /var/lib/postgresql/10/main/
    

    С такими параметрами запуска rsync заменит изменившиеся файлы.
  4. По окончании копирования на мастере выполним:

    SELECT pg_stop_backup();
    
  5. На реплике положим такой recovery.conf с указанием нашего слота:

    standby_mode = 'on'primary_conninfo = 'user=rep host=master_ip port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'recovery_target_timeline = 'latest'primary_slot_name = replica
    
  6. Запустим реплику.
  7. Удалим слот репликации на реплике, так как он так же скопируется с мастера:

    SELECT pg_drop_replication_slot('replica');
    
  8. Проверим, что она появилась в системной таблице pg_stat_replication.

Однако один момент я всё-таки упустил. Мы выполнили копирование всех WAL-файлов, которые были на мастере. А значит даже тех, которые не требовались. Поэтому на следующий день после перелива реплики место на сервере с репликой начало заканчиваться. И пришлось думать над тем, как удалить бесхозные сегменты WAL.

Мы знаем, что checkpoint_timeout равен 1 часу. Следовательно, надо удалить все файлы старше 1 часа, но от какого момента? Для этого на мастере делаем запрос:

SELECT pg_walfile_name(replay_lsn) from pg_stat_replication;     pg_walfile_name      -------------------------- 0000000200022107000000C8(1 row)

Исходя из него сверяем временную метку файла:

stat /var/lib/postgresql/10/main/pg_wal/0000000200022107000000C8...Access: 2020-12-02 13:11:20.409309421 +0300Modify: 2020-12-02 13:11:20.409309421 +0300Change: 2020-12-02 13:11:20.409309421 +0300

у удаляем все файлы старше. С этим помогут find и bash:

# Вычислим смещениеdeleteBefore=`expr $(date --date='2020-12-02 13:11:20' +%s) - 3600`mins2keep=`expr $(expr $(expr $(date +%s) - $deleteBefore) / 60) + 1`# Удалим файлы размером 16 МБ (стандартный размер сегмента WAL),# которые старше, чем mins2keepfind /var/lib/postgresql/10/main/pg_wal/ -size 16M -type f -mmin +$mins2keep -delete

Вот и всё: реплика была перелита за 12 часов (вместо 9 дней), функционирует и очищена от мусора.

История 4. CockroachDB не тормозит?


После обновления CockroachDB до версии 20.2.x мы столкнулись с проблемами производительности. Они выражались в долгом старте приложения и общем снижении производительности некоторых типов запросов. На CockroachDB 20.1.8 подобного поведения не наблюдалось.

Изначально имелось предположение, что дело в сетевых проблемах в кластере Kubernetes. Однако подтвердить его не удалось: cеть чувствовала себя отлично.



В процессе дальнейшего изучения было обнаружено, что на производительность влияет наличие в кластере CockroachDB базы приложения Keycloak. Решили включить журналирование медленных логов кстати, в CockroachDB это делается командами:

SET CLUSTER SETTING sql.log.slow_query.latency_threshold = '100ms';SET CLUSTER SETTING sql.log.slow_query.internal_queries.enabled = 'true';

Благодаря этому стало ясно, что используемый в приложении драйвер PostgreSQL JDBC при старте делает запросы к pg_catalog, а наличие базы Keyсloak сильно влияет на скорость работы этих запросов. Мы пробовали загрузить несколько копий базы и с каждый загруженным экземпляром скорость работы pg_catalog падала всё ниже и ниже:

I201130 10:52:27.993894 5920071 sql/exec_log.go:225  [n3,client=10.111.7.3:38470,hostssl,user=db1] 3 112.396ms exec "PostgreSQL JDBC Driver" {} "SELECT typinput = 'array_in'::REGPROC AS is_array, typtype, typname FROM pg_catalog.pg_type LEFT JOIN (SELECT ns.oid AS nspoid, ns.nspname, r.r FROM pg_namespace AS ns JOIN (SELECT s.r, (current_schemas(false))[s.r] AS nspname FROM ROWS FROM (generate_series(1, array_upper(current_schemas(false), 1))) AS s (r)) AS r USING (nspname)) AS sp ON sp.nspoid = typnamespace WHERE typname = $1 ORDER BY sp.r, pg_type.oid DESC" {$1:"'jsonb'"} 1 "" 0 { LATENCY_THRESHOLD }

Вот тот же запрос, но с загруженной проблемной базой:

I201130 10:36:00.786376 5085793 sql/exec_log.go:225  [n2,client=192.168.114.18:21850,hostssl,user=db1] 67 520.064ms exec "PostgreSQL JDBC Driver" {} "SELECT typinput = 'array_in'::REGPROC AS is_array, typtype, typname FROM pg_catalog.pg_type LEFT JOIN (SELECT ns.oid AS nspoid, ns.nspname, r.r FROM pg_namespace AS ns JOIN (SELECT s.r, (current_schemas(false))[s.r] AS nspname FROM ROWS FROM (generate_series(1, array_upper(current_schemas(false), 1))) AS s (r)) AS r USING (nspname)) AS sp ON sp.nspoid = typnamespace WHERE typname = $1 ORDER BY sp.r, pg_type.oid DESC" {$1:"'jsonb'"} 1 "" 0 { LATENCY_THRESHOLD }

Получается, что тормозили системные таблицы CockroachDB.

После того, как клиент подтвердил проблемы с производительностью уже в облачной инсталляции CockroachDB, источник проблемы стал проясняться: было похоже на улучшенную поддержку SQL, что появилась в релизе 20.2. План запросов к схеме pg_catalog заметно отличался от 20.1.8, и мы стали свидетелями регрессии.

Собрав все факты, сделали issue на GitHub, где разработчики после нескольких попыток воспроизведения проблемы смогли подтвердить её и пообещали решить в скором времени. Исходя из этого клиент принял решение переходить на новую версию, так как сейчас баг мешает нам только при старте, увеличивая время старта инстанса приложения.

ОБНОВЛЕНО (уже после написания статьи): Проблемы были исправлены в релизе CockroachDB 20.2.3 в Pull Request 57574.

Заключение


Как видно, иногда даже очевидные и простые операции могут повлечь за собой головную боль. Но выход всё равно можно найти, не так ли?.. Надеюсь, эти истории помогут и другим инженерам в повседневной работе. Stay tuned!

P.S.


Читайте также в нашем блоге:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru