Отличная статья, спасибо! От себя могу сказать, что установка GitLab'а на виртуалках по описанию понравилась больше, чем поднятие его же на двух docker-контейнерах (под сам gitlab + runner). С докер-подходом больше внезапных граблей с дефолтным конфигом, настройкой reverse-proxy, обеспечения сборки докер-образов внутри докера и т.д. Хотя он тоже работает.
Ещё совет тем, кто хочет попробовать настроить себе CI/CD: gitlab сейчас будет советовать 2 фичи - хорошо подумайте, прежде чем согласиться ) Первая фича - Auto DevOps. В идеале - у вас будет сборка, тесты и деплой в кубернетес просто так. Вообще ничего делать не надо. Из проблем - если вам нужно что-то кастомизировать, то это сложно. А ещё это медленно работает. И я считаю, что buildpacks и heroku - это какое-то переусложненное решение проблемы сборки, в которой за пару дней можно с нуля разобраться.
Вторая фича - как раз GitOps (flux) для деплоя. На мой взгляд, само по себе настраивается сложно, а оправдано наоборот, только в очень простых случаях (деплоим сразу в прод).
Ну, без docker registry в любом случае не обойтись - сам образ контейнера кубер должен откуда-то взять.
Чем лучше - вопрос хороший ) Почему авторы werf предлагают использовать werf вместо голого helm - понятно. Лично я не использовал werf, но использовал GitOps-решения (flux). Мои впечатления - GitOps хорош для управления на уровне кластера/неймспейса, но отдельные микросервисы я бы предпочел деплоить хелмом. Особенно в связке с GitLab'ом, где из коробки есть флоу, когда, к примеру, деплой на staging идёт автоматом, а на production - по ручному аппруву.
Но, повторюсь, сам werf я не использовал. Его даже не позиционируют как GitOps (хотя по описанию werf не очень понятно почему). Так что может им действительно удобнее
Я таких не знаю. Подобное решение можно придумать: делаем "умный" прокси, который "прячет" от клиента смену бэкенда, и сам переподключается к новым. Пока мы работаем с обычными очередями, у нас будут только две проблемы: задержки при переключении бэкендов и проблемы при обновлении самого прокси. Проблемы с потенциальной потерей сообщений можно решить на уровне типа очередей брокера (использовать гарантию at least once). С пользовательскими очередями будет сложнее, но если мы используем внешнюю схему аутентификации типа OAuth2, то тоже все будет работать. Но готовых решений такого типа я не встречал.
Просто проблема, которую я рассматриваю в статье, не в том, что мы рвем TCP-соединение с конкретным бэком при обновлении. У нас есть 2 независимых API: с клиентом вебсокет-сервера и с генератором событий. А так как API независимы, мы не можем на маршрутизаторах достичь того, чтобы сообщение автоматически попадало на нужный бэкенд. Отсюда и необходимость в брокере.
Ну, по крайней мере, в общем случае не можем такого достичь. Когда это сделать получается, рассматриваемой проблемы по сути и нет: если сообщение сразу падает на нужный бэкенд, он его может сразу переслать клиенту, брокер просто не нужен (например, если оба API основаны на REST и используют одинаковые балансировшики). Но что делать, когда, как например в рассмотренном примере, за балансировку клиентов отвечает сервис Kubernetes, а за балансировку генераторов - консюмеры AMQP-очередей?
Потрогал библиотеку, что-то отлично зашло, что-то не очень. Если в деталях:
Не требуется объявлять TestContainers DataSource. Создание спрятано в Extensions. На мой взгляд, скорее минус, чем плюс - в URL я могу указать конкретную версию image’а, а здесь она прибита;
assert’ы на SQL-операции (ExpectedSqlQuery). Не встречал такого, любопытная идея. Используется для JPA, позволяет провалидировать, что в результате выполнения метода в базу улетят, к примеру, 2 INSERT’а и один SELECT. Но проверяется только количество, а не содержимое.
Database Rider. Jupiter Tools подтягивают ее как зависимость, и эта библиотека прям топ. По сути, это тот же синтаксический сахар вокруг DBUnit’а, который пробовал сделать рассмотренный мной spring-test-dbunit, только здесь это получилось хорошо. Из фич:
Проект живой, периодически обновляется;
Нет акцента на XML;
Наполнение базы с before- и after-выражениями (удобно, когда есть основной тестовый набор данных, но в некоторых тестах его надо чуть-чуть поменять);
Поддержка выражений на JS и Groovy в DataSet’ах;
Связанные переменные при валидации (удобно для foreign key);
Поддержка Cucumber’а.
Database Rider мне очень хорошо зашел, утащу к себе в проекты.
согласен, здесь дело именно в том, что кубер нельзя рассматривать как одно целое - это набор компонентов, между которыми как раз eventual consistency. один компонент обрабатывает запрос на остановку пода, другой - посылает сигнал процессу, третий - управляет трафиком. хотя с точки зрения каждого отдельного компонента он отработал корректно, с точки зрения внешнего наблюдателя это выглядит как рассинхронизированное состояние. но никто и не давал гарантий, что внутреннее состояние никогда таким не может быть
Хм, прикольно, не знал. Вернее, знал, что если мы хотим сделать insert / update с JPQL, то нам не дадут, потому что Not supported for DML operations [update ...] и ошибочно эктраполировал это и на nativeQuery. Но, да, с Native Query таких проверок нет и все работает. Спасибо!
В статье я имею ввиду, что внешний идентификатор выдает не наша база, а какой-то сторонний сервис (поэтому он и внешний). Вы можете использовать как первичный ключ его, а можете сделать, чтобы первичный ключ генерировала база (я предпочитаю этот вариант). Но и во втором случае поле для внешнего ключа имеет смысл сделать уникальным, как раз чтобы ни при каких обстоятельствах не создались 2 записи с одинаковым внешним ID
Да, если мы используем PreparedStatement. Ну или вернее так - со Spring Data JPA это не сработает. При попытке объявить в репозитории модифицирующий метод, который возвращает не void и не int / Integer, вы получите ошибку, которая, собственно, и говорит:
Modifying queries can only use void or int/Integer as return type!
Ну, разработчики могли и не делать объекты Comparable. В этом случае дерево будет строится в по identity hashcode'у (в конечном счете). Работать будет корректно, за O(log n). Вот только относительно медленно, потому что пока дело дойдет до взятия хэшей, HashMap успеет еще посравнивать имена классов, например.
Но скорее всего, в программе авторства разработчиков, которые не осилили написание хэш-функции, смотреть на ассимптотическую сложность бессмысленно, поскольку производительность можно испортить куда более эффективными способами
alexesDev Если не секрет — в связке с чем? Интересно было бы узнать про опыт с кубером. Видел в качестве ingress'а nginx и istio, а вот traefik как-то не попадался, хотя по статистике довольно популярный
dkopitsa Спасибо за замечание, в качестве маршрутизатора по сервисам «из коробки» — отличное решение. И с API GoDaddy он тоже умеет работать, оказывается.
Отличная статья, спасибо! От себя могу сказать, что установка GitLab'а на виртуалках по описанию понравилась больше, чем поднятие его же на двух docker-контейнерах (под сам gitlab + runner). С докер-подходом больше внезапных граблей с дефолтным конфигом, настройкой reverse-proxy, обеспечения сборки докер-образов внутри докера и т.д. Хотя он тоже работает.
Ещё совет тем, кто хочет попробовать настроить себе CI/CD: gitlab сейчас будет советовать 2 фичи - хорошо подумайте, прежде чем согласиться ) Первая фича - Auto DevOps. В идеале - у вас будет сборка, тесты и деплой в кубернетес просто так. Вообще ничего делать не надо. Из проблем - если вам нужно что-то кастомизировать, то это сложно. А ещё это медленно работает. И я считаю, что buildpacks и heroku - это какое-то переусложненное решение проблемы сборки, в которой за пару дней можно с нуля разобраться.
Вторая фича - как раз GitOps (flux) для деплоя. На мой взгляд, само по себе настраивается сложно, а оправдано наоборот, только в очень простых случаях (деплоим сразу в прод).
Ну, без docker registry в любом случае не обойтись - сам образ контейнера кубер должен откуда-то взять.
Чем лучше - вопрос хороший ) Почему авторы werf предлагают использовать werf вместо голого helm - понятно. Лично я не использовал werf, но использовал GitOps-решения (flux). Мои впечатления - GitOps хорош для управления на уровне кластера/неймспейса, но отдельные микросервисы я бы предпочел деплоить хелмом. Особенно в связке с GitLab'ом, где из коробки есть флоу, когда, к примеру, деплой на staging идёт автоматом, а на production - по ручному аппруву.
Но, повторюсь, сам werf я не использовал. Его даже не позиционируют как GitOps (хотя по описанию werf не очень понятно почему). Так что может им действительно удобнее
Я таких не знаю. Подобное решение можно придумать: делаем "умный" прокси, который "прячет" от клиента смену бэкенда, и сам переподключается к новым. Пока мы работаем с обычными очередями, у нас будут только две проблемы: задержки при переключении бэкендов и проблемы при обновлении самого прокси. Проблемы с потенциальной потерей сообщений можно решить на уровне типа очередей брокера (использовать гарантию at least once). С пользовательскими очередями будет сложнее, но если мы используем внешнюю схему аутентификации типа OAuth2, то тоже все будет работать. Но готовых решений такого типа я не встречал.
Просто проблема, которую я рассматриваю в статье, не в том, что мы рвем TCP-соединение с конкретным бэком при обновлении. У нас есть 2 независимых API: с клиентом вебсокет-сервера и с генератором событий. А так как API независимы, мы не можем на маршрутизаторах достичь того, чтобы сообщение автоматически попадало на нужный бэкенд. Отсюда и необходимость в брокере.
Ну, по крайней мере, в общем случае не можем такого достичь. Когда это сделать получается, рассматриваемой проблемы по сути и нет: если сообщение сразу падает на нужный бэкенд, он его может сразу переслать клиенту, брокер просто не нужен (например, если оба API основаны на REST и используют одинаковые балансировшики). Но что делать, когда, как например в рассмотренном примере, за балансировку клиентов отвечает сервис Kubernetes, а за балансировку генераторов - консюмеры AMQP-очередей?
Потрогал библиотеку, что-то отлично зашло, что-то не очень. Если в деталях:
Не требуется объявлять TestContainers DataSource. Создание спрятано в Extensions. На мой взгляд, скорее минус, чем плюс - в URL я могу указать конкретную версию image’а, а здесь она прибита;
assert’ы на SQL-операции (ExpectedSqlQuery). Не встречал такого, любопытная идея. Используется для JPA, позволяет провалидировать, что в результате выполнения метода в базу улетят, к примеру, 2 INSERT’а и один SELECT. Но проверяется только количество, а не содержимое.
Database Rider. Jupiter Tools подтягивают ее как зависимость, и эта библиотека прям топ. По сути, это тот же синтаксический сахар вокруг DBUnit’а, который пробовал сделать рассмотренный мной spring-test-dbunit, только здесь это получилось хорошо. Из фич:
Проект живой, периодически обновляется;
Нет акцента на XML;
Наполнение базы с before- и after-выражениями (удобно, когда есть основной тестовый набор данных, но в некоторых тестах его надо чуть-чуть поменять);
Поддержка выражений на JS и Groovy в DataSet’ах;
Связанные переменные при валидации (удобно для foreign key);
Поддержка Cucumber’а.
Database Rider мне очень хорошо зашел, утащу к себе в проекты.
спасибо, выглядит интересно! попробую на днях и вернусь сюда с комментом
согласен, здесь дело именно в том, что кубер нельзя рассматривать как одно целое - это набор компонентов, между которыми как раз eventual consistency. один компонент обрабатывает запрос на остановку пода, другой - посылает сигнал процессу, третий - управляет трафиком. хотя с точки зрения каждого отдельного компонента он отработал корректно, с точки зрения внешнего наблюдателя это выглядит как рассинхронизированное состояние. но никто и не давал гарантий, что внутреннее состояние никогда таким не может быть
Хм, прикольно, не знал. Вернее, знал, что если мы хотим сделать
insert
/update
с JPQL, то нам не дадут, потому чтоNot supported for DML operations [update ...]
и ошибочно эктраполировал это и на nativeQuery. Но, да, с Native Query таких проверок нет и все работает. Спасибо!В статье я имею ввиду, что внешний идентификатор выдает не наша база, а какой-то сторонний сервис (поэтому он и внешний). Вы можете использовать как первичный ключ его, а можете сделать, чтобы первичный ключ генерировала база (я предпочитаю этот вариант). Но и во втором случае поле для внешнего ключа имеет смысл сделать уникальным, как раз чтобы ни при каких обстоятельствах не создались 2 записи с одинаковым внешним ID
Да, если мы используем PreparedStatement. Ну или вернее так - со Spring Data JPA это не сработает. При попытке объявить в репозитории модифицирующий метод, который возвращает не void и не int / Integer, вы получите ошибку, которая, собственно, и говорит:
Modifying queries can only use void or int/Integer as return type!
ну так-то любой туториал можно сократить до букв RTFM ) я как раз и хотел написать про то, что еще можно сделать, кроме добавления в код нативного SQL
я про нее вскользь говорил, когда писал про оптимистические блокировки, а так - да, хороший вариант избежать лишних блокировок на уроне БД
Но скорее всего, в программе авторства разработчиков, которые не осилили написание хэш-функции, смотреть на ассимптотическую сложность бессмысленно, поскольку производительность можно испортить куда более эффективными способами