Как работать с ветками Git и миграциями Rails

111

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

  • Что было бы хорошим решением для "пары" git ветвей с состояниями БД?

  • Что бы эти "состояния" на самом деле были?

    Мы не можем просто дублировать базу данных, если она имеет несколько ГБ в размере.

  • И что должно произойти слияниями?

  • Будет ли решение переходить на базы данных noSQL?

    В настоящее время мы используем MySQL, mongodb и redis


EDIT: Похоже, я забыл упомянуть очень важный момент, меня интересует только среда разработки, но с большими базами данных (размером в несколько ГБ).

  • 0
    Что вы делаете, что у вас есть среда, в которой работает ваша основная ветка, базу данных которой можно изменять другими ветками? Я не понимаю, каков ваш рабочий процесс или почему вы считаете, что вам нужно синхронизировать ветки с конкретными базами данных.
  • 3
    Допустим, у нас есть таблица в нашей базе данных с клиентами (имя, адрес электронной почты, телефон), а в ветке мы разбиваем один из столбцов (имя -> first_name + last_name). До тех пор, пока мы не объединяем ветку с мастером, мастер и все остальные ветки на его основе не будут работать.
Теги:
database

11 ответов

53

Когда вы добавляете новую миграцию в любую ветвь, запустите rake db:migrate и зафиксируйте как перенос , так и db/schema.rb

Если вы это сделаете, в процессе разработки вы сможете переключиться на другую ветвь с другим набором миграций и просто запустите rake db:schema:load.

Обратите внимание, что это будет воссоздать всю базу данных, а существующие данные будут потеряны.

Вероятно, вы хотите только запустить производство из одной ветки, с которой вы очень осторожны, поэтому эти шаги там не применяются (просто запустите rake db:migrate, как обычно там). Но в разработке не должно быть большого труда воссоздать базу данных из схемы, что будет делать rake db:schema:load.

  • 5
    Я думаю, что это только решит проблему со схемой, данные будут потеряны при каждой миграции, и их больше никогда не будет видно. Было бы хорошей идеей сохранить какой-нибудь db-data-patch, который сохраняется при выходе из ветви, и другой, который загружается при переходе в другую ветку? Патчи должны содержать только те данные, которые будут потеряны на пути вниз (миграция).
  • 4
    Если вы хотите загрузить данные, используйте db/seeds.rb Это не должно быть слишком разрушительным, чтобы уничтожить вашу db/seeds.rb данных разработки, если вы настроите там разумные начальные данные.
Показать ещё 3 комментария
16

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

  • Прежде чем переключать ветки, откат (rake db:rollback) до состояния до точки ветвления. Затем, после переключения ветвей, запустите db:migrate. Это математически корректно, и пока вы пишете скрипты down, это сработает.
  • Если вы забудете сделать это до переключения ветвей, в общем, вы можете безопасно переключиться назад, откат и снова переключиться, так что я думаю, что это рабочий процесс, это возможно.
  • Если у вас есть зависимости между переходами в разных ветвях... ну, вам придется много думать.
  • 2
    Вы должны иметь в виду, что не все миграции обратимы, тем не менее, первый предложенный шаг не гарантирует успеха. Я думаю, что в среде разработки хорошей идеей было бы использовать rake db:schema:load и rake db:seed как сказал @noodl.
  • 0
    @pisaruk Я знаю, что вы ответили на это шесть лет назад, но, читая, мне любопытно, что будет примером необратимой миграции. Я с трудом представляю себе ситуацию. Я предполагаю, что самым простым будет отброшенный столбец, содержащий кучу данных, но его можно «перевернуть», чтобы получить пустой столбец или столбец с некоторым значением по умолчанию. Вы думали о других случаях?
Показать ещё 1 комментарий
13

Здесь script я написал для переключения между ветвями, которые содержат разные миграции:

https://gist.github.com/4076864

Он не решит все проблемы, о которых вы упомянули, но с учетом имени ветки он будет:

  • Откажитесь от любых миграций по вашей текущей ветке, которые не существуют на данной ветке.
  • Отменить любые изменения в файле db/schema.rb
  • Отметьте данную ветку
  • Запустите любые новые миграции, существующие в данной ветке
  • Обновление тестовой базы данных

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

  • 1
    Этот скрипт делает именно то, что я хочу сделать, я бы хотел, чтобы его вставили в автоматическую проверку.
  • 1
    Это только что, я раздвоил твою суть и сделал это крюком после оформления заказа : gist.github.com/brysgo/9980344
Показать ещё 4 комментария
4

Возможно, вы должны принять это как подсказку о том, что ваша база данных разработки слишком велика? Если вы можете использовать db/seeds.rb и меньший набор данных для разработки, тогда ваша проблема может быть легко решена с помощью schema.rb и seeds.rb из текущей ветки.

Это предполагает, что ваш вопрос связан с развитием; Я не могу себе представить, почему вам нужно регулярно переключаться на производство.

  • 0
    Я не знал о db/seeds.rb , я посмотрю на это.
3

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

В разработке (с использованием postgresql):

  • sql_dump db_name > tmp/branch1.sql
  • git checkout branch2
  • dropdb db_name
  • createdb db_name
  • psql db_name < tmp/branch2.sql # (из предыдущего переключателя ветки)

Это намного быстрее, чем утилиты rake в базе данных с около 50K записями.

Для производства поддерживайте ведущую ветвь как sacrosanct, и все миграции проверяются, shema.rb должным образом сливается. Пройдите стандартную процедуру обновления.

  • 0
    Для достаточно небольших размеров базы данных и выполнения этого в фоновом режиме при проверке ветки выглядит очень хорошим решением.
3

Я боролся с той же проблемой. Вот мое решение:

  • Убедитесь, что все schema.rb и все миграции отмечены всеми разработчиками.

  • Для развертывания должен быть один человек/машина. Позвольте называть этот аппарат как машину слияния. Когда изменения вытягиваются на машину слияния, автоматическое слияние для schema.rb не будет выполнено. Без вопросов. Просто замените содержимое каким-либо предыдущим содержимым для schema.rb(вы можете отложить копию или получить ее из github, если вы ее используете...).

  • Вот важный шаг. Миграции от всех разработчиков теперь будут доступны в папке db/migrate. Идем дальше и запускаем команду exec rake db: migrate. Это приведет к тому, что база данных на машине слияния сравняется со всеми изменениями. Он также будет регенерировать schema.rb.

  • Зафиксировать и вывести изменения во все репозитории (пульты и индивидуумы, которые также являются пультами). Вы должны быть готовы!

2

Отдельная база данных для каждой ветки

Это единственный способ летать.

Обновление 16 октября 2017 года

Я вернулся к этому через некоторое время и сделал некоторые улучшения:

  • Я добавил еще одну задачу рейка пространства имен, чтобы создать ветвь и клонировать базу данных одним махом, с bundle exec rake git:branch.
  • Теперь я понимаю, что клонирование от мастера не всегда то, что вы хотите сделать, поэтому я сделал более явным, что задача db:clone_from_branch принимает переменную среды SOURCE_BRANCH и TARGET_BRANCH. При использовании git:branch он автоматически использует текущую ветвь как SOURCE_BRANCH.
  • Рефакторинг и упрощение.

config/database.yml

И чтобы вам было проще, вот как вы обновляете свой файл database.yml для динамического определения имени базы данных на основе текущей ветки.

<% 
database_prefix = 'your_app_name'
environments    = %W( development test ) 
current_branch  = `git status | head -1`.to_s.gsub('On branch ','').chomp
%>

defaults: &defaults
  pool: 5
  adapter: mysql2
  encoding: utf8
  reconnect: false
  username: root
  password:
  host: localhost

<% environments.each do |environment| %>  

<%= environment %>:
  <<: *defaults
  database: <%= [ database_prefix, current_branch, environment ].join('_') %>
<% end %>

lib/tasks/db.rake

Здесь задача Rake, чтобы легко клонировать вашу базу данных из одной ветки в другую. Для этого требуются переменные среды SOURCE_BRANCH и TARGET_BRANCH. Исходя из @spalladino задача.

namespace :db do

  desc "Clones database from another branch as specified by `SOURCE_BRANCH` and `TARGET_BRANCH` env params."
  task :clone_from_branch do

    abort "You need to provide a SOURCE_BRANCH to clone from as an environment variable." if ENV['SOURCE_BRANCH'].blank?
    abort "You need to provide a TARGET_BRANCH to clone to as an environment variable."   if ENV['TARGET_BRANCH'].blank?

    database_configuration = Rails.configuration.database_configuration[Rails.env]
    current_database_name = database_configuration["database"]

    source_db = current_database_name.sub(CURRENT_BRANCH, ENV['SOURCE_BRANCH'])
    target_db = current_database_name.sub(CURRENT_BRANCH, ENV['TARGET_BRANCH'])

    mysql_opts =  "-u #{database_configuration['username']} "
    mysql_opts << "--password=\"#{database_configuration['password']}\" " if database_configuration['password'].presence

    `mysqlshow #{mysql_opts} | grep "#{source_db}"`
    raise "Source database #{source_db} not found" if $?.to_i != 0

    `mysqlshow #{mysql_opts} | grep "#{target_db}"`
    raise "Target database #{target_db} already exists" if $?.to_i == 0

    puts "Creating empty database #{target_db}"
    `mysql #{mysql_opts} -e "CREATE DATABASE #{target_db}"`

    puts "Copying #{source_db} into #{target_db}"
    `mysqldump #{mysql_opts} #{source_db} | mysql #{mysql_opts} #{target_db}`

  end

end

lib/tasks/git.rake

Эта задача создаст ветвь git для текущей ветки (master или иначе), проверит ее и клонирует текущую базу данных ветвей в новую базу данных ветвей. Он имеет пятно AF.

namespace :git do

  desc "Create a branch off the current branch and clone the current branch database."
  task :branch do 
    print 'New Branch Name: '
    new_branch_name = STDIN.gets.strip 

    CURRENT_BRANCH = `git status | head -1`.to_s.gsub('On branch ','').chomp

    say "Creating new branch and checking it out..."
    sh "git co -b #{new_branch_name}"

    say "Cloning database from #{CURRENT_BRANCH}..."

    ENV['SOURCE_BRANCH'] = CURRENT_BRANCH # Set source to be the current branch for clone_from_branch task.
    ENV['TARGET_BRANCH'] = new_branch_name
    Rake::Task['db:clone_from_branch'].invoke

    say "All done!"
  end

end

Теперь вам нужно всего лишь запустить bundle exec git:branch, ввести новое имя ветки и начать убивать зомби.

2

Я полностью ощущаю лаваш, который у вас здесь. Поскольку я думаю об этом, реальная проблема заключается в том, что у всех ветвей нет кода для отката определенных ветвей. Я в мире джанго, поэтому я не знаю, как это хорошо. Я играю с идеей, что миграции живут в своем собственном репо, который не получает разветвленных (git -подмодулей, о которых я недавно узнал). Таким образом, все ветки имеют все миграции. Липкая часть гарантирует, что каждая ветвь ограничена только теми миграциями, которые им нравятся. Выполнение/отслеживание этого вручную было бы лайтом и подвержено ошибкам. Но для этого не созданы никакие инструменты миграции. Это тот момент, когда я без пути.

  • 0
    Это хорошая идея, но что происходит, когда ветка переименовывает столбец? Остальные ветви будут смотреть на сломанный стол.
  • 0
    гм - это липкая часть - какая отрасль заботится о том, какие миграции. так что вы можете пойти "синхронизировать", и он знает, "отменить эту миграцию", чтобы столбец вернулся.
2

Вы хотите сохранить "среду db" для каждой ветки. Посмотрите на smudge/clean script, чтобы указать на разные экземпляры. Если у вас закончились экземпляры db, попробуйте script отменить временный экземпляр, поэтому, когда вы переключаетесь на новую ветку, она уже существует и просто должна быть переименована в script. Обновления БД должны выполняться непосредственно перед выполнением ваших тестов.

Надеюсь, что это поможет.

  • 0
    Это решение подходит только для «временных» веток. Например, если у нас есть ветвь «край», где мы тестируем все виды сумасшедших вещей (возможно, с другими дочерними ветвями), а затем время от времени сливаем их с мастером, две базы данных разойдутся (их данные не будут быть таким же)
  • 0
    Это решение хорошо для полной противоположности. Это очень хорошее решение, если вы создаете версию своего сценария версии базы данных.
0

Я бы предложил один из двух вариантов:

Вариант 1

  • Поместите свои данные в seeds.rb. Хорошим вариантом является создание ваших данных семян с помощью FactoryGirl/Fabrication gem. Таким образом, вы можете гарантировать, что данные синхронизируются с кодом, если мы предположим, что фабрики обновляются вместе с добавлением/удалением столбцов.
  • После переключения из одной ветки в другую запустите rake db:reset, которая эффективно удаляет/создает/семя базы данных.

Вариант 2

Вручную поддерживать состояния базы данных, всегда запуская rake db:rollback/rake db:migrate до/после проверки ветки. Предостережение заключается в том, что все ваши миграции должны быть обратимыми, иначе это не сработает.

0

В среде разработки:

Вы должны работать с rake db:migrate:redo, чтобы проверить, являются ли ваши script обратимыми, но имейте в виду, что всегда следует иметь seed.rb с совокупностью данных.

Если вы работаете с git, вы должны изменить change.rb с изменением миграции и выполнением db:migrate:redo для начала (загрузить данные для новой разработки на другой машине или новой базе данных)

Помимо "обмена", с вашими методами вверх и вниз ваш код всегда будет сценарием для "изменения" в этот момент и при запуске с нуля.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню