Как мы в 2020 году изобретали процесс разработки, отладки и доставки в прод изменений базы данных

    На дворе 2020 год и фоновым шумом вы уже привыкли слышать: «Кубернетес — это ответ!», «Микросервисы!», «Сервис меш!», «Сесурити полиси!». Все вокруг бегут в светлое будущее.

    Подходы в том, что касается баз данных, в нашей компании более консервативны, чем в прикладных приложениях. Крутится база данных у нас не в кубернетесе, а на железе или в виртуалке. Для изменений базы данных процессинга платежных сервисов у нас есть устоявшийся процесс, который включает в себя множество автоматических проверок, большое ревью и релиз с участием DBA. Количество проверок и привлекаемых людей в этом случае негативно влияет на time-to-market. С другой стороны, он отлажен и позволяет надежно вносить изменения в продакшен, минимизируя вероятность что-то сломать. А если что-то сломалось, то нужные люди уже включены в процесс починки. Этот подход делает работу основного сервиса компании стабильнее.

    Большинство новых реляционных баз данных для микросервисов мы заводим на PostgreSQL. Отлаженный процесс для Oracle хоть и надёжный, но несет с собой избыточную сложность для маленьких БД. Тащить тяжёлые процессы из прошлого в светлое будущее никто не хочет. Проработкой процесса для светлого будущего заранее никто не занялся. В итоге получили отсутствие стандарта и разножопицу.



    Если хотите узнать, к каким проблемам это привело и как мы их порешали, — добро пожаловать под кат.

    Проблемы, которые мы решали


    Нет единых стандартов версионирования


    В лучшем случае это DDL SQL-файлы, которые лежат где-то в директории db в репозитории с микросервисом. Совсем плохо, если это просто текущее состояние БД, разное на тесте и на проде, и эталонных скриптов схемы БД нет.

    В ходе отладки ушатываем тестовую базу


    «Я сейчас немного тестовую БД пошатаю, не пугайтесь там» — и пошел отлаживать на тестовой базе данных только что написанный код изменения схемы. Иногда долго, и всё это время тестовый контур не работает.

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

    Методы DAO не покрываются тестами, не проверяются в CI


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

    Гарантии, что ничего не развалится в будущем, нет. Страдает качество и поддерживаемость микросервиса.

    Неизоморфность сред


    Если в тестовый и продакшен контуры изменения поставляются по-разному, то нельзя быть уверенным, что оно будет работать одинаково. Особенно когда на тесте по факту проводится разработка и отладка.

    Объекты на тесте могут быть созданы из-под учетки разработчика или приложения. Гранты накидываются как попало, обычно grant all privileges. Гранты приложению выдаются по принципу «вижу ошибку в логе — даю грант». Часто при релизе забывают про гранты. Иногда после релиза смок-тестирование не покрывает всю новую функциональность и отсутствие гранта выстреливает не сразу.

    Тяжелый и ломучий процесс наката в продакшен


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

    Это замедляет релиз. А в случае проблем увеличивает даунтайм, усложняя доступ разработчика к БД. Скрипты exec.sql и rollback.sql часто не проверялись на тесте, потому что стандарта патчсетирования для не-Oracle нет, а на тест катилось как попало.

    Поэтому бывает такое, что в некритичные сервисы разработчики катят изменения без этого процесса вообще.

    Как можно делать, чтобы было хорошо


    Отладка на локальной БД в докер-контейнере


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

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

    Приведу пример, насколько просто поднять локально БД:

    Пишем двухстрочный Dockerfile:

    FROM postgres:12.3
    ADD init.sql /docker-entrypoint-initdb.d/

    В init.sql делаем «чистую» БД, которую рассчитываем получить и на тесте, и в проде. Она должна содержать:

    • Пользователя-владельца схемы и саму схему.
    • Пользователя приложения с грантом на использование схемы.
    • Требуемые EXTENSIONs

    Пример init.sql
    create role my_awesome_service
    with login password *** NOSUPERUSER inherit CREATEDB CREATEROLE NOREPLICATION;
    create tablespace my_awesome_service owner my_awesome_service location '/u01/postgres/my_awesome_service_data';
    create schema my_awesome_service authorization my_awesome_service;
    grant all on schema my_awesome_service to my_awesome_service;
    grant usage on schema my_awesome_service to my_awesome_service;
    alter role my_awesome_service set search_path to my_awesome_service,pg_catalog, public;
    
    create user my_awesome_service_app with LOGIN password *** NOSUPERUSER inherit NOREPLICATION;
    grant usage on schema my_awesome_service to my_awesome_service_app;
    
    create extension if not exists "uuid-ossp";
    


    Для удобства можно добавить в Makefile таску db, которая (пере)запустит контейнер с базой и оттопырит порт для соединения:

    db:
        docker container rm -f my_awesome_service_db || true
        docker build -t my_awesome_service_db docker/db/.
        docker run -d --name my_awesome_service_db -p 5433:5432 my_awesome_service_db
    

    Версионирование changeset‘ов с помощью чего-то стандартного для индустрии


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

    В общем, нужен контроль. Системы миграции как раз про контроль.
    Не будем вдаваться в сравнение разных систем версионирования схем БД. FlyWay vs Liquibase — не тема этой статьи. Мы выбрали Liquibase.

    Мы версионируем:

    • DDL-структуру объектов бд (create table).
    • DML-содержимое таблиц-справочников (insert, update).
    • DCL-гранты для УЗ Приложения (grant select, insert on ...).

    Запуская и отлаживая микросервис на локальной БД, разработчик столкнется с необходимостью позаботиться о грантах. Единственный легальный способ для него — завести DCL-скрипт в ченджсет. Это гарантирует нам, что гранты доедут до прода.

    Пример sql-патчсета
    0_ddl.sql:
    create table my_awesome_service.ref_customer_type
    (
        customer_type_code    	varchar not null,
        customer_type_description varchar not null,
        constraint ref_customer_type_pk primary key (customer_type_code)
    );
     
    alter table my_awesome_service.ref_customer_type
        add constraint customer_type_code_ck check ( (customer_type_code)::text = upper((customer_type_code)::text) );
    

    1_dcl.sql:

    grant select on all tables in schema my_awesome_service to ru_svc_qw_my_awesome_service_app;
    grant insert, update on my_awesome_service.some_entity to ru_svc_qw_my_awesome_service_app;
    

    2_dml_refs.sql:

    insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)
    values ('INDIVIDUAL', 'Физ. лицо');
    insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)
    values ('LEGAL_ENTITY', 'Юр. лицо');
    insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)
    values ('FOREIGN_AGENCY', 'Иностранное юр. лицо');
    

    Fixtures. Данные для тестов или отладки идут отдельным ченжсетом с контекстом dev
    3_dml_dev.sql:

    insert into my_awesome_service.some_entity_state (state_type_code, state_data, some_entity_id)
    values ('BINDING_IN_PROGRESS', '{}', 1);
    

    rollback.sql:

    drop table my_awesome_service.ref_customer_type;
    


    Пример changeset.yaml
    databaseChangeLog:
     - changeSet:
         id: 1
         author: "mr.awesome"
         changes:
           - sqlFile:
               path: db/changesets/001_init/0_ddl.sql
           - sqlFile:
               path: db/changesets/001_init/1_dcl.sql
           - sqlFile:
               path: db/changesets/001_init/2_dml_refs.sql
         rollback:
           sqlFile:
             path: db/changesets/001_init/rollback.sql
     - changeSet:
         id: 2
         author: "mr.awesome"
         context: dev
         changes:
           - sqlFile:
               path: db/changesets/001_init/3_dml_dev.sql
    


    Liquibase создает на БД таблицу databasechangelog, где отмечает накаченные ченджсеты.
    Автоматически вычисляет, сколько ченджсетов нужно докатить до БД.

    Есть maven и gradle plugin с возможностью сгенерировать из нескольких ченджсетов скрипт, который нужно докатить до БД.

    Интеграция системы миграций БД в фазу запуска приложения


    Здесь мог бы быть любой адаптер системы контроля миграций и фреймворка, на котором построено ваше приложение. Со многими фреймворками он идёт в комплекте с ORM. Например, Ruby-On-Rails, Yii2, Nest.JS.

    Этот механизм нужен, чтобы катить миграции при старте контекста приложения.
    Например:

    1. На тестовой БД патчсеты 001, 002, 003.
    2. Погромист наразрабатывал патчсеты 004, 005 и не деплоил приложение в тест.
    3. Деплоим в тест. Докатываются патчсеты 004, 005.

    Если не накатываются — приложение не стартует. Rolling update не убивает старые поды.

    В нашем стеке JVM + Spring, и мы не используем ORM. Поэтому нам потребовалась интеграция Spring-Liquibase.

    У нас в компании есть важное требование безопасности: пользователь приложения должен иметь ограниченный набор грантов и точно не должен иметь доступ уровня владельца схемы. С помощью Spring-Liquibase есть возможность катить миграции от имени пользователя-владельца схемы. При этом пул соединений прикладного уровня приложения не имеет доступа к DataSource'у Liquibase. Поэтому приложение не получит доступ из-под пользователя-владельца схемы.

    Пример application-testing.yaml
    spring:
      liquibase:
        enabled: true
        database-change-log-lock-table: "databasechangeloglock"
        database-change-log-table: "databasechangelog"
        user: ${secret.liquibase.user:}
        password: ${secret.liquibase.password:}
        url: "jdbc:postgresql://my.test.db:5432/my_awesome_service?currentSchema=my_awesome_service"


    DAO тесты на CI-этапе verify


    В нашей компании есть такой CI-этап — verify. На этом этапе происходит проверка изменений на соответствие внутренним стандартам качества. Для микросервисов это обычно прогон линтера для проверки кодстайла и на наличие багов, прогон unit-тестов и запуск приложения с поднятием контекста. Теперь на этапе verify можно проверить миграции БД и взаимодействие DAO-слоя приложения с БД.

    Поднятие контейнера с БД и накат патчсетов увеличивает время старта Spring-контекста на 1,5-10 сек, в зависимости от мощности рабочей машины и количества патчсетов.

    Это не совсем unit-тесты, это тесты интеграции DAO-слоя приложения с базой данных.
    Называя БД частью микросервиса, мы говорим, что это тестирование интеграции двух частей одного микросервиса. Без внешних зависимостей. Таким образом эти тесты стабильны и могут выполняться на этапе verify. Они фиксируют контракт микросервиса и БД, обеспечивая уверенность при будущих доработках.

    А еще это удобный способ отладки DAO. Вместо того, чтобы вызывать RestController, имитируя поведения пользователя в каком-то бизнес-сценарии, сразу вызываем DAO с нужными аргументами.

    Пример DAO-теста
    @Test
    @Transactional
    @Rollback
    fun `create cheque positive flow`() {
          jdbcTemplate.update(
           "insert into my_awesome_service.some_entity(inn, registration_source_code)" +
                   "values (:inn, 'QIWICOM') returning some_entity_id",
           MapSqlParameterSource().addValue("inn", "526317984689")
       )
       val insertedCheque = chequeDao.addCheque(cheque)
       val resultCheque = jdbcTemplate.queryForObject(
           "select cheque_id from my_awesome_service.cheque " +
                   "order by cheque_id desc limit 1", MapSqlParameterSource(), Long::class.java
       )
       Assert.assertTrue(insertedCheque.isRight())
       Assert.assertEquals(insertedCheque, Right(resultCheque))
    }
    


    Есть две сопутствующие задачи для прогона этих тестов в пайплайне на verify:

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

    Эти две задачи решает библиотека TestContainers. Она использует существующий докер образ для поднятия контейнера с базой данных в состоянии init.sql.

    Пример использования TestContainers
    @TestConfiguration
    public class DatabaseConfiguration {
    
       @Bean
       GenericContainer postgreSQLContainer() {
           GenericContainer container = new GenericContainer("my_awesome_service_db")
                   .withExposedPorts(5432);
    
           container.start();
           return container;
       }
    
       @Bean
       @Primary
       public DataSource onlineDbPoolDataSource(GenericContainer postgreSQLContainer) {
           return DataSourceBuilder.create()
                   .driverClassName("org.postgresql.Driver")
                   .url("jdbc:postgresql://localhost:"
                           + postgreSQLContainer.getMappedPort(5432)
                           + "/postgres")
                   .username("my_awesome_service_app")
                   .password("my_awesome_service_app_pwd")
                   .build();
       }
        
       @Bean
       @LiquibaseDataSource
       public DataSource liquibaseDataSource(GenericContainer postgreSQLContainer) {
           return DataSourceBuilder.create()
                   .driverClassName("org.postgresql.Driver")
                   .url("jdbc:postgresql://localhost:"
                           + postgreSQLContainer.getMappedPort(5432)
                           + "/postgres")
                   .username("my_awesome_service")
                   .password("my_awesome_service_app_pwd")
                   .build();
       }
    


    С разработкой и отладкой разобрались. Теперь нужно доставить изменения схемы БД в продакшен.

    Kubernetes — это ответ! А какой был ваш вопрос?


    Итак, вам надо автоматизировать какой-то CI/CD-процесс. У нас есть обкатанный подход на тимсити. Казалось бы, где тут повод для еще одной статьи?

    А повод есть. Кроме обкатанного подхода, есть и поднадоевшие проблемки большой компании.

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

    И во всем этом «по старинке» проблема — все бегут в светлое будущее, а поддержка легаси… ну вы знаете. Работает и ладно. Не работает — займемся потом. Когда-нибудь. Не сегодня.

    Допустим, вы уже одной ногой по колено в светлом будущем и кубернетес-инфраструктура у вас уже есть. Есть даже возможность сгенерировать еще один микросервис, который сразу заведется в этой инфраструктуре, подхватит нужный конфиг и секреты, будет иметь нужные доступы, зарегистрируется в service mesh инфраструктуре. И всё это счастье может получить рядовой разработчик, без привлечения человека с ролью *OPS. Вспоминаем, что в кубернетесе есть тип ворклоада Job, как раз предназначенный для каких-то сервисных работ. Ну и погнали делать приложение на Kotlin+Spring-Liquibase, стараясь максимально переиспользовать существующую в компании инфраструктуру для микросервисов на JVM в кубере.

    Переиспользуем следующие аспекты:

    • Генерация проекта.
    • Деплой.
    • Доставку конфигов и секретов.
    • Доступы.
    • Логирование и доставка логов в ELK.

    Получаем такой пайплайн:


    Кликабельно

    Теперь мы имеем


    • Версионирование ченджсетов.
    • Проверяем их на выполнимость update → rollback.
    • Пишем тесты на DAO. Бывает даже следуем TDD: запускаем отладку DAO с помощью тестов. Тесты выполняются на свежеподнятой БД в TestContainers.
    • Запускаем локально БД в докере на стандартном порту. Проводим отладку, смотрим, что осталось в БД. При необходимости можем управлять локальной БД вручную.
    • Накатываем в тест и проводим авторелиз патчсетов стандартным пайплайном в teamcity, по аналогии с микросервисами. Пайплайн является дочерним для микросервиса, которому принадлежит БД.
    • Не храним креды от БД в тимсити. И не заботимся о доступах с виртуалок-билдагентов.

    Знаю, что для многих это всё не откровение. Но раз уж вы дочитали, будем рады рассказу о вашем опыте в комментах.
    QIWI
    Ведущий платёжный сервис нового поколения в России

    Comments 34

      +2

      Так что делать с легаси ораклиной?
      Микросервисы это всё понятно. Особенно новые.
      А как быть с полсотней разработчиков разной степени криворукости, которым ушатывать тестовую бд. Но не простую, а тестовый контур сверхбрльшой БД? Которая завязана кучей щупалец с другими сервисами?
      Как взять ее под контроль версий?

        +7
        Выглядит как запрос на отдельную статью о том, как мы решали аналогичную боль =)
        Напишем статью, если это и правда востребовано. Ставьте upvote, если откликнулось.
        Если коротко, то это процесс + техническое решение, основная цель которых — контроль и проверки на всех этапах.
          0
          Было бы интересно почитать
          +1

          Так же. С какого-то момента все изменения схемы — ченжсетами в любом формате. Перед продом стоит препрод, на который нельзя накатить что-то руками. На нём все update+rollback прогоняются вашим CI/CD перед мёржем, после чего делается флэшбэк (если это ораклина), а после мёржа делается update. Для чистоты совести можете обновлять его по выходным.

            0
            Но не простую, а тестовый контур сверхбрльшой БД


            Много лет назад читал на Хабре про железяку с помощью которой версии БД делаются снэпшотами файловой системы. В результате выходит довольно экономично для огромных тестовых БД. А сейчас это и программно доступно, снэпшоты в файловых системах типа ZFS и др.
            0
            В любом случае вся возня с миграциями выглядит как нереальных масштабов костыль. Самое забавное что это можно было бы существенно облегчить интеграция гита с самой базой, но подвижек видимо не будет никогда.

            По поводу несоответствия состояния прода предполагаемому статусу мне нравится такое решение: перед деплоем на прод берется продовая база, кидается на тест и выполняются вообще все ендпоинты API в порядке CRURD и только потом на прод.

              +2

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

                0
                дак без данных берем и кидает, в чем проблема то?) Единственное что тесты на круды придется чуть сложнее писать
                  0

                  Ну ка по подробнее. Зачем тестовая бд без данных?
                  Как на ней проверять интерфейсы, нагрузку и прочее.
                  Цель накатить пустую схему и получить с этого профит (не понятно какой)?

                    0
                    зачем уж совсем без данных, справочники вполне можно и принесть.
                    Профит такой что можно дернуть все ендпоинты и пройти весь их жизненный путь на абсолютно живой базе, это позволит обнаружить вообще любые ошибки.
                +1

                Миграции это и есть интеграция гита с базой. Просто не очевидная для вас

                  0
                  Ну да, конечно, особенно учитывая количество приседаний.
                +3

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

                  0
                  Когда столкнулись с такой задачей, напедалили на Node.JS свою нехитрую утилиту, которая соединяется в обе базы, сравнивает содержимое конкретных таблиц из конфига и генерирует DML SQL скрипты. github.com/qiwi/pg_comparator
                  Кстати, эту утилиту тогда сделал школьник-стажер под моим менторством. Об этом мы рассказывали в другой статье habr.com/ru/company/qiwi/blog/434636
                    0
                    А почему не взяли стандартные инструменты Liquibase?
                    Плагины под maven, gradle тоже присутствуют.
                      0
                      Статья по ссылке — про сравнение двух состояний схемы. Нам нужно было актуализировать в тесте содержимое (данные) справочных таблиц, которое нагенерили пользователи в проде. Стандартных легковесных инструментов сходу не нашли. Буду рад если подкинете такой.
                  0

                  Как пишете и отлаживаете скрипты миграции данных из одной версии базы к другой? Что делаете, если в тестинге миграция работает, а в проде — нет?

                    0
                    Мы стараемся обеспечить максимальную изоморфность сред. Пока не было ситуации, когда в тестинге работает, а в проде — нет. Если такая ситуация возникнет, то в первую очередь будем искать дырку в процессе) А уже потом править ченджсет, если потребуется.
                      0

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


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

                        0
                        Переливка данных из прода в тестинг всегда проходит через фазу деперсонализации, на которой чувствительная информация маскируется. Этот процесс у нас не включен в ci и проходит в полуручном режиме.
                          0
                          Хороший пример с email) Проверку на уникальность в приложении мы тоже сделали бы через уникальный индекс на базе и поиск по нему. Есть еще вариант со вставкой и отловом исключения, но мы стараемся избегать exceptions as control flow. В любом случае, мы столкнулись бы с проблемой неуникальных данных еще в момент реализации такой проверки. Что бы мы делали с такими записями — вопрос бизнес логики приложения и продукта. Универсального ответа не могу дать.
                            0

                            Ну я специально его придумывал, по мотивам реального случая. Проверку уникальности через индекс я поддерживаю, собственно, если идти по этому пути — проблема и возникнет ровно в момент миграции базы в проде, и решать надо было бы именно на стадии миграции, ведь в старом коде проверки не было, и поэтому записи УЖЕ есть в базе.

                              0
                              Только не уникальный индекс а уникальный частичный индекс.
                              upper(email) where is_deleted=false
                        0
                        Круто. Надеюсь, что и мы прикрутим к своему проекту Liquibase.
                          0

                          Довелось мне как-то поработать с выходцами из qiwi. Теперь я хотя бы знаю что они не шутили, а правда так мыслили, спасибо.

                            0

                            интересная тема, отладка и тестирование базы данных. даже писал помню статью на хабре по этому поводу, неоднозначное впечатление я так понял она произвела, но и полностью мой подход не опровергли… посмотрите, может вам чем-то буду полезен. хотя там без легаси и на. net но суть та же, думаю сможете воспроизвести. ключевой момент — изоляция обращений к базе в DAL и тестирование его на реальной базе без mock

                              0
                              Спасибо за наводку, отличная статья! И дискуссия интересная в комментах)
                              0

                              Все это, конечно, замечательно, но что делать с шардингом.

                                +1
                                ИМХО тут надо различать логическую структуру БД и её физическую реализацию.
                                Как бы проблема шардинга, не должна касаться программистов, желательно, чтобы это была внутренняя кухня ДБА.
                                Здесь ТС рассматривает пайплайн для разработчиков.
                                Чтобы «отделить» программистов от ручных изменений в БД.
                                Т.к. все актуальные СУРБД к такому использованию приспособлены менее чем никак, то получается все очень неуклюже.
                                  0

                                  К сожалению, шардинг практически никогда не удается изолировать от аппликейшн-уровня. Он всегда «протекает» в той или иной степени, и в особенности — для случая реляционной СУБД (вернее, для случая «реляционных БД», это ж шардинг, БД много). Соответственно, мигратор должен уметь не просто тупо накатывать-откатывать на одну базу, но делать это на несколько баз, да еще желательно работать с микрошардами (в постгресе одна микрошарда может быть одной схемой или одним префиксом имени таблицы).


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

                                0
                                image
                                  0

                                  Лично мне не нравится идея с упаковкой скриптов в докерфайл. Постгрес поддерживает монтирование файлов с init sql скриптами и sh через волюм. Это удобнее, IMHO.

                                    0
                                    Действительно, монтировать том в контейнер с базой удобно еще и тем, что это дает возможность иметь локально персистентную базу. Тут кому как удобнее, на самом деле. Я описал концепцию в ее самом простом виде.
                                    +1
                                    Хорошего средства для автоматизации контроля версий БД на MSSQL я так и не смог найти, поэтому написал свою утилиту ImportExportDataSql (выложил в бесплатное пользование на обозрение).
                                    Недавно добавил возможность экспорта конфигурации БД.
                                    Экспорт данных в ней тоже имеется (в форматах CSV и SQL).
                                    Настройки хранятся в XML файле, что удобно при переносе сразу нескольких таблиц (результатов SELECT запросов). Написал запросы SELECT один раз и эти запросы выгружаются в формате SQL, в котором уже имеются проверки на уникальность, чтобы не возникало ошибок нарушения ключей.
                                    Что касается сбора всех изменений структуры БД — написал системный триггер, который сохраняет историю изменений в отдельную таблицу с помощью функции EVENTDATA().

                                    Only users with full accounts can post comments. Log in, please.