Pull to refresh

Comments 22

Я бы без анестезии ничего в продакшене патчить бы не решился. Как минимум 0.5 нужно.
1) Это вы типа переизобрели JRebel?
2) «программист забыл проверить на null» — а вы не пробовали код покрывать тестами? Слышал, это помогает
3) «достаточно занудного и трудоёмкого процесса выкладывания новой» — может стоит упростить процесс? Уверен, что можно разбить части на кусочки и релизить «атомарно», либо другим способом упростить
А вообще, данный подход из разряда «ху....-ху… и в продакшен». Печально слышать о его использовании в такой компании
zeroturnaround.com столько денег ввалили в рекламу своего jrebel, а вы без него обошлись.
Стандартная замена классов в java очень ограничена, в частности, в статье описываются случаи когда она не работает. JRebel использует несколько другой подход, поэтому он позволяет добавлять новые поля и изменять сигнатуру методов, т.е. очевидно более гибче

JRebel, где может, использует HCR для экономии. Но это все но лишь одно маленькое изменение, которое возможно с HCR — изменение тела метода. Для запатки ок, и разумно когда критично сохранить состояние.

Денег ввалились и в разработку немало. Потому что это не про HCR

Вопрос такой: а если патчуемый код уже заинструменчен каким-нибудь фреймворком в рантайме? Будет ли повторно вызываться инструментация фреймворка или нет?


Другой вопрос: а как вы тестируете сам процесс патчинга? HCR очень деликатная и ограниченная штука, и успешно пропатчить код может лишь в некоторых случаях. И вот у вас зафейлился патч на продакше. Что делать дальше?


Вообще, я нахожу использование такого подхода более чем странным особенно для Одноклассников. Как я себе представляю подобные системы, перезагрузка одного сервиса никак не должна влиять на общую работу: микросервисы, распределенность и все такое… Если же система монолитная или ее рестарт занимает значительное время, то можно попытаться разделить всю логику на контексты внутри приложения при помощи класслоадеров и рестартать их вбелую по мере необходимости. Или например использовать OSGi, где все это уже реализовано из коробки ввиде модулей. Но патчить на продакшне через интерфейс, предназначенный для дебага — это странное решение.

а если патчуемый код уже заинструменчен каким-нибудь фреймворком в рантайме? Будет ли повторно вызываться инструментация фреймворка или нет?
Смотря как фреймворк работает. Если он регистрирует инструментацию через Instrumentation.addTransformer, то после патчинга трансформация будет выполнена повторно.

а как вы тестируете сам процесс патчинга?
Зависит от сложности патча. В общем случае применяем сначала на тестовом сервере, потом на одном продакшн сервере, проверяем функциональность руками и/или автотестами, затем применяем на группе хостов, смотрим графики, и, наконец, на всём продакшне. Если патч сломался, его можно отменить применением обратного патча, либо уже традиционной процедурой апдейта. На практике до откатывания патча ни разу дело не доходило.

перезагрузка одного сервиса никак не должна влиять на общую работу
Перезагрузка одного сервера — да. А если надо рестартануть кластер из 600 инстансов? Это надо делать частями, с выводом трафика и плавным заводом, при этом старт и прогрев приложения может занимать десятки минут. Именно так и делается при плановых апдейтах, но это занимает время. Патчинг не заменяет апдейты. Он для простых, но срочных фиксов.
патчить на продакшне через интерфейс, предназначенный для дебага — это странное решение
Это необычно, согласен. Поэтому и удостоилось статьи. Ключевая особенность фиксов через HCR — что это происходит без даунтайма и без потери состояния. Все текущие соединения, запросы, накопленные данные, кеши, значения переменных — всё остаётся. Не требуется никакой активации/деактивации модулей и никакой миграции со старой версии на новую. OSGi в данном случае не поможет.
подход вполне понятен, и имеет смысл. Но не лучше ли такое делать архитектурно? Т.е. делать resilient architecture, а не патчинг прода.
К примеру на Load Balancer перевести трафик на чистый сервер, потом спокойно рестартануть сервис. Такой подход добавляет большую надежность общей системы, и убирает приседания с байт кодом. что-то типа netflix style.
Комментарием выше я как раз на это ответил. Мы ровно так и делаем при плановых релизах. А патчинг мы используем не для выкладки новой версии, а для небольших оперативных исправлений. Перевести трафик на один «чистый» сервер — не проблема. А когда сервис работает на 600 хостах — процедура сложнее и, главное, дольше.
Хорошая статья, надо будет попробовать. А с Runnable пробовали? Например заменить имплементацию метода run?
Да, без разницы. Можно менять run как и любой другой метод, всё с теми же ограничениями.

Для особенно нетерпеливых и ленивых еще можно поставлять с каждым приложением простую UI висящую на 8080, например, куда в текстовую форму просто копипастится целиком класс и все на лету тоже меняется (если сможет конечно, потому что редефайн сигнатур не переживет и придется рестартовать приложение). Отсутствие адекватного трекинга изменений и версионирования плюс сюрпризы наподобие "почему вижу в коде одно, а на сервере ничего не работает" во время параллельной разработки гарантированы, но для фикс мастера уровень бог такие вещи помехой никогда не были)

Вишенкой на торте будет исправление кода уже прям в техтарии UI, с включенной кирилической раскладкой? :) с, х, а что там еще у нас из близнецов есть? :-)
Зачем какой-то 8080? Прошлый век!
Оформить центр управления в виде игры в самих Одноклассниках, как всякие «весёлые фермы» там сделаны. Желающий пропатчить production-сервер разработчик покупает ОК-и и заливает фикс.
Интересная технология для наложения пластыря на разорванную артерию… но имхо, лучше научиться быстро и просто обновлять версии… Ну чтобы старт приложения не занимал больше десятка секунд, кэш при этом не умирал (желательно), а траффик… ну пусть LoadBalancer с этим живёт, переспросит у соседнего инстанса… Итого, если 600 серверов и апдейтить сразу по 10, то нужно 60 апдейтов, по 10 секунд — это 10 минут обновлений… имхо — это моментально.
10 сек — это хорошо, но есть сервисы где 10 мин стартует, и чтобы радикально ускориться нужно очень сильно поиздержаться. И стартует 10 мин не потому что руки кривые…

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

Со временем, реальная жизнь цинично ломает такие мечты своей суровой реальностью :) Даже вынос всяких расплодившихся кешей наружу и поддержка их в «горячем» состоянии, не всегда позволяет выйти на «десятки секунд»

Допустим, у нас в бегущем приложении есть экземпляр следующего класса:


public class AssignmentOrderMatter {

    private Supplier<String> firstSupplier;

    private Supplier<String> lastSupplier;

    public AssignmentOrderMatter() {
        this.lastSupplier = () -> "Farewell!";
        this.firstSupplier = () -> "Hallo!";
    }

    public String getFirstValue() {
        return firstSupplier.get();
    }

    public String getLastValue() {
        return lastSupplier.get();
    }

}

Мы замечаем опечатку в строке "Hello!", исправляем её, попутно меняем порядок присвоения полей в конструкторе т.к. ну некрасиво же первых инициализировать последними. Скорее всего даже не сразу вспомним потом про эту правку.


Применяем шаманство «Hot Code Replace» и, внезапно, последние стали первыми и getFirstValue() начинает возвращать строку "Farewell!".


apangin, возможен ли в вашей системе такой сценарий или его поймают по дороге в продакшн?
У меня получилось воспроизвести такое поведение: https://github.com/Maccimo/BreakingLambdaBody

Отличный вопрос! Да, есть проблема с лямбдами, что нельзя менять их порядок в исходниках. Автоматического обнаружения таких ситуаций у нас нет. Однако у ошибки мало шансов попасть в продакшн: даже если её не заметят на code review, дальше тестового сервера она вряд ли уйдёт.
Sign up to leave a comment.