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

131

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

  1. Что было бы хорошим решением, чтобы «связать» ветки git с состояниями БД?

  2. Какими бы были эти «состояния» на самом деле?

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

  3. А что должно происходить со слияниями?

  4. Будет ли решение переведено и на базы данных noSQL?

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


РЕДАКТИРОВАТЬ: Похоже, я забыл упомянуть очень важный момент, меня интересует только среда разработки, но с большими базами данных (размером несколько ГБ).

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

Ответы:

64

Когда вы добавляете новую миграцию в любую ветку, запустите 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для пополнения потерянных данных БД
Пастулло
Для больших сложных приложений, где вам нужно воспроизводить реальные ошибки локально, абсолютно невозможно использовать файл семян, вам нужны реальные данные из производства (или стадии разработки). А восстановление базы данных может занять некоторое время, поэтому нет, это не лучшее решение для моего случая.
Joel_Blum
21

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

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

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

https://gist.github.com/4076864

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

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

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

Джон Леммон
источник
1
Этот скрипт делает именно то, что я хочу сделать, я бы хотел, чтобы он был помещен в ловушку автоматической проверки.
brysgo
1
Это только что, я раздвоил вашу суть и сделал это после оформления заказа
brysgo
Вы действительно хотели сказать в своем сценарии git checkout db/schema.rbили имели в виду git checkout -- db/schema.rb? (т.е. с двойным тире)
user664833
1
Ну да ... Я тогда не знал о двойных тире. Но команда будет работать так же, если у вас нет вызванной ветки db/schema.rb. :)
Джон Леммон
Разработанная @brysgo команда git_rails ( github.com/brysgo/git-rails ) отлично работает. Спасибо, Джон :)
Зия Уль Рехман Могол
7

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

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

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

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

  • Я добавил еще одну задачу rake пространства имен, чтобы создать ветку и клонировать базу данных одним махом с помощью 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, позволяющая легко клонировать вашу базу данных из одной ветки в другую. Это принимает переменные среды a SOURCE_BRANCHи a 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 от текущей ветки (основной или другой), проверит ее и клонирует базу данных текущей ветки в базу данных новой ветки. Это гладкая автофокусировка.

namespace :git do

  desc "Create a branch off the current branch and clone the current branch's 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, ввести новое имя ветки и начать убивать зомби.

Джошуа Пинтер
источник
4

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

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

noodl
источник
Я не знал об этом db/seeds.rb, я разберусь.
Костас
3

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

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

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

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

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

Tabrez
источник
3

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

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

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

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

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

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

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

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

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

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

Johno
источник
Это хорошая идея, но что происходит, когда ветка переименовывает столбец? Остальные ветви будут смотреть на сломанный стол.
Костас
гм - это липкая часть - какая ветка заботится о том, какие миграции. так что вы можете пойти «синхронизировать», и он знает, «отменить эту миграцию», чтобы столбец вернулся.
JohnO
1

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

Опция 1

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

Вариант 2

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

Александр
источник
0

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

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

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

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

Даниэль Антонио Нуньес Кархуайо
источник