Cara bekerja dengan migrasi Git branch and Rails


131

Saya bekerja pada aplikasi rel dengan beberapa cabang git dan banyak dari mereka termasuk migrasi db. Kami mencoba untuk berhati-hati tetapi kadang-kadang beberapa kode master meminta kolom yang dihapus / diganti nama di cabang lain.

  1. Apa yang akan menjadi solusi yang bagus untuk "pasangan" cabang git dengan status DB?

  2. Apa sebenarnya "keadaan" ini?

    Kami tidak bisa hanya menggandakan basis data jika ukurannya beberapa GB.

  3. Dan apa yang harus terjadi dengan penggabungan?

  4. Apakah solusinya akan diterjemahkan ke database noSQL juga?

    Kami saat ini menggunakan MySQL, mongodb dan redis


EDIT: Sepertinya saya lupa menyebutkan poin yang sangat penting, saya hanya tertarik pada lingkungan pengembangan tetapi dengan database besar (ukuran beberapa GB).


Apa yang Anda lakukan sehingga Anda memiliki lingkungan yang menjalankan cabang master Anda yang databasenya dapat dimodifikasi oleh cabang lain? Saya tidak mengerti apa alur kerja Anda atau mengapa Anda pikir Anda perlu menjaga cabang tetap sinkron dengan database tertentu.
Jonah

3
Katakanlah kita memiliki tabel dalam database kita dengan klien (nama, email, telepon) dan dalam cabang kita membagi salah satu kolom (nama -> first_name + last_name). Sampai kita menggabungkan cabang dengan master, master dan semua cabang lain berdasarkan itu akan gagal.
Kostas

Jawaban:


64

Saat Anda menambahkan migrasi baru di cabang apa pun, jalankan rake db:migratedan lakukan migrasi dan db/schema.rb

Jika Anda melakukan ini, dalam pengembangan, Anda akan dapat beralih ke cabang lain yang memiliki kumpulan migrasi berbeda dan cukup dijalankan rake db:schema:load.

Perhatikan bahwa ini akan membuat ulang seluruh database, dan data yang ada akan hilang .

Anda mungkin hanya ingin menjalankan produksi dari satu cabang yang sangat hati-hati, sehingga langkah-langkah ini tidak berlaku di sana (jalankan rake db:migrateseperti biasa di sana). Namun dalam pengembangan, seharusnya bukan masalah besar untuk membuat ulang database dari skema, yang rake db:schema:loadakan dilakukan.


5
Saya pikir ini hanya akan menyelesaikan masalah skema, data akan hilang dengan setiap migrasi turun tidak pernah terlihat lagi. Apakah itu ide yang baik untuk menyimpan beberapa jenis db-data-patch yang akan disimpan ketika pindah dari cabang dan yang lain dimuat ketika pindah ke cabang lain? Tambalan seharusnya hanya berisi data yang akan hilang pada saat turun (migrasi).
Kostas

4
Jika Anda ingin memuat data, gunakan db/seeds.rb itu seharusnya tidak terlalu merusak untuk membatalkan pengembangan DB Anda jika Anda menyiapkan beberapa data benih yang masuk akal di sana.
Andy Lindeman

tidak perlu nuke apa pun. lihat solusi saya di bawah ini. Perlu diketahui bahwa Anda akan memiliki banyak contoh dan ketika Anda beralih cabang, data tidak ada di sana. Ini benar-benar baik jika Anda mengembangkan dengan tes.
Adam Dymitruk

Terima kasih Andy, jawaban ini juga pertanyaan saya. Dan setuju untuk menggunakan db/seeds.rbuntuk melakukan ripopulasi data db yang hilang
pastullo

Untuk aplikasi rumit besar di mana Anda perlu mereproduksi bug kehidupan nyata secara lokal, sama sekali tidak mungkin untuk menggunakan file seed, Anda memerlukan data nyata dari produksi (atau pementasan). Dan memulihkan database bisa memakan waktu cukup lama, jadi tidak ada ini bukan solusi yang baik untuk kasus saya.
Joel_Blum

21

Jika Anda memiliki database besar yang tidak dapat dengan mudah direproduksi, maka saya akan merekomendasikan menggunakan alat migrasi normal. Jika Anda menginginkan proses yang sederhana, inilah yang saya rekomendasikan:

  • Sebelum beralih cabang, kembalikan ( rake db:rollback) ke status sebelum titik cabang. Kemudian, setelah berganti cabang, jalankan db:migrate. Ini benar secara matematis, dan selama Anda menulis downskrip, itu akan berhasil.
  • Jika Anda lupa melakukan ini sebelum berganti cabang, secara umum Anda dapat dengan aman beralih kembali, kembalikan, dan beralih lagi, jadi saya pikir sebagai alur kerja, itu layak.
  • Jika Anda memiliki ketergantungan antara migrasi di berbagai cabang ... yah, Anda harus berpikir keras.

2
Anda harus ingat bahwa tidak semua migrasi dapat dibalik, yang mengatakan, langkah pertama yang disarankan tidak dijamin berhasil. Saya pikir di lingkungan pengembangan ide yang baik adalah menggunakan rake db:schema:loaddan rake db:seedseperti yang dikatakan @noodl.
pisaruk

@pisaruk Saya tahu Anda menjawab ini enam tahun lalu, tetapi membaca saya ingin tahu seperti apa contoh migrasi yang tidak dapat dikembalikan. Saya mengalami kesulitan membayangkan suatu situasi. Saya kira yang paling sederhana adalah kolom yang dijatuhkan yang berisi banyak data, tetapi ini bisa "dibalik" untuk memiliki kolom kosong atau kolom dengan beberapa nilai default. Apakah Anda memikirkan kasus lain?
Luke Griffiths

1
Saya pikir Anda menjawab pertanyaan Anda sendiri! Ya, kolom yang dijatuhkan adalah contoh yang bagus. Atau migrasi data yang merusak.
ndp

13

Berikut ini skrip yang saya tulis untuk beralih antar cabang yang berisi migrasi berbeda:

https://gist.github.com/4076864

Itu tidak akan menyelesaikan semua masalah yang Anda sebutkan, tetapi diberi nama cabang itu akan:

  1. Kembalikan migrasi yang ada di cabang Anda saat ini yang tidak ada di cabang yang diberikan
  2. Buang segala perubahan pada file db / schema.rb
  3. Lihatlah cabang yang diberikan
  4. Jalankan migrasi baru yang ada di cabang yang diberikan
  5. Perbarui basis data pengujian Anda

Saya menemukan diri saya secara manual melakukan ini sepanjang waktu di proyek kami, jadi saya pikir akan menyenangkan untuk mengotomatiskan prosesnya.


1
Script ini melakukan persis apa yang ingin saya lakukan, saya ingin melihatnya dimasukkan ke dalam checkout checkout otomatis.
brysgo

1
Ini baru saja dalam, saya bercabang inti Anda dan membuatnya menjadi post-checkout hook: gist.github.com/brysgo/9980344
brysgo

Dalam naskah Anda, apakah Anda benar-benar bermaksud mengatakan git checkout db/schema.rbatau maksud Anda git checkout -- db/schema.rb? (Yaitu dengan tanda hubung ganda)
user664833

1
Ya ... saya tidak tahu tentang garis ganda pada saat itu. Tetapi perintah akan bekerja sama kecuali Anda memiliki cabang yang dipanggil db/schema.rb. :)
Jon Lemmon

@ brysgo mengembangkan perintah git_rails ( github.com/brysgo/git-rails ) berfungsi dengan baik. Terima kasih untukmu Jon :)
Zia Ul Rehman Mughal

7

Pisahkan Database untuk setiap Cabang

Ini satu-satunya cara untuk terbang.

Perbarui 16 Oktober 2017

Saya kembali ke ini setelah beberapa waktu dan melakukan beberapa perbaikan:

  • Saya telah menambahkan tugas rake namespace lain untuk membuat cabang dan mengklon database dalam satu gerakan, dengan bundle exec rake git:branch.
  • Saya menyadari sekarang bahwa kloning dari master tidak selalu apa yang ingin Anda lakukan, jadi saya membuatnya lebih eksplisit bahwa db:clone_from_branchtugas yang diambil SOURCE_BRANCHdan TARGET_BRANCHvariabel lingkungan. Saat menggunakannya git:branchakan secara otomatis menggunakan cabang saat ini sebagai SOURCE_BRANCH.
  • Refactoring dan penyederhanaan.

config/database.yml

Dan untuk membuatnya lebih mudah bagi Anda, inilah cara Anda memperbarui database.ymlfile Anda untuk secara dinamis menentukan nama basis data berdasarkan cabang saat ini.

<% 
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

Inilah tugas Rake untuk mengkloning basis data Anda dari satu cabang ke cabang lainnya dengan mudah. Ini membutuhkan variabel a SOURCE_BRANCHdan TARGET_BRANCHlingkungan. Didasarkan dari tugas @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

Tugas ini akan membuat cabang git dari cabang saat ini (master, atau lainnya), memeriksanya dan mengkloning basis data cabang saat ini ke dalam basis data cabang baru. Ini AF yang apik.

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

Sekarang, yang perlu Anda lakukan adalah menjalankan bundle exec git:branch, masukkan nama cabang baru dan mulai membunuh zombie.


4

Mungkin Anda harus menganggap ini sebagai petunjuk bahwa database pengembangan Anda terlalu besar? Jika Anda bisa menggunakan db / seeds.rb dan kumpulan data yang lebih kecil untuk pengembangan maka masalah Anda dapat dengan mudah diselesaikan dengan menggunakan schema.rb dan seeds.rb dari cabang saat ini.

Itu mengasumsikan bahwa pertanyaan Anda berkaitan dengan pengembangan; Saya tidak bisa membayangkan mengapa Anda harus secara teratur berganti cabang dalam produksi.


Saya tidak tahu db/seeds.rb, saya akan memeriksanya.
Kostas

3

Saya berjuang dengan masalah yang sama. Ini solusinya:

  1. Pastikan bahwa schema.rb dan semua migrasi diperiksa oleh semua pengembang.

  2. Harus ada satu orang / mesin untuk penyebaran ke produksi. Sebut mesin ini sebagai mesin gabungan. Ketika perubahan ditarik ke mesin gabungan, gabungan otomatis untuk schema.rb akan gagal. Tidak ada masalah. Ganti saja konten dengan konten apa pun yang sebelumnya untuk schema.rb (Anda dapat menyisihkan salinannya atau mendapatkannya dari github jika Anda menggunakannya ...).

  3. Inilah langkah penting. Migrasi dari semua pengembang sekarang akan tersedia di folder db / bermigrasi. Maju dan jalankan bundle exec rake db: migrate. Ini akan membawa database pada mesin gabungan sejajar dengan semua perubahan. Ini juga akan membuat ulang schema.rb.

  4. Komit dan dorong perubahan ke semua repositori (remote dan individu, yang juga remote). Anda harus selesai!


3

Inilah yang telah saya lakukan dan saya tidak yakin bahwa saya telah membahas semua pangkalan:

Dalam pengembangan (menggunakan postgresql):

  • sql_dump db_name> tmp / branch1.sql
  • git checkout branch2
  • dropdb db_name
  • Createdb db_name
  • psql db_name <tmp / branch2.sql # (dari switch cabang sebelumnya)

Ini jauh lebih cepat daripada utilitas rake pada database dengan sekitar 50 ribu catatan.

Untuk produksi, pertahankan cabang master sebagai sakral dan semua migrasi diperiksa, shema.rb digabung dengan benar. Ikuti prosedur peningkatan standar Anda.


Untuk ukuran basis data yang cukup kecil dan melakukan ini di latar belakang ketika memeriksa cabang terlihat seperti solusi yang sangat bagus.
Kostas

2

Anda ingin melestarikan "lingkungan db" per cabang. Lihatlah skrip smudge / clean untuk menunjuk ke instance yang berbeda. Jika Anda kehabisan instance db, minta skrip memutar instance temp sehingga ketika Anda beralih ke cabang baru, itu sudah ada di sana dan hanya perlu diganti namanya dengan skrip. Pembaruan DB harus dijalankan sebelum Anda menjalankan tes.

Semoga ini membantu.


Solusi ini hanya baik untuk cabang "sementara". Sebagai contoh, jika kita memiliki "edge" cabang jika kita menguji semua jenis hal gila (mungkin dengan cabang-cabang lain) dan kemudian menggabungkannya ke master dari waktu ke waktu, 2 basis data akan terpisah (data mereka tidak akan sama).
Kostas

Solusi ini bagus untuk kebalikannya. Ini adalah solusi yang sangat bagus jika Anda membuat versi skrip versi database Anda.
Adam Dymitruk

2

Saya benar-benar mengalami pita yang Anda miliki di sini. Ketika saya memikirkannya, masalah sebenarnya adalah bahwa semua cabang tidak memiliki kode untuk mengembalikan cabang tertentu. Saya di dunia Django, jadi saya tidak tahu menyapu dengan baik. Saya mempermainkan ide bahwa migrasi hidup dalam repo mereka sendiri yang tidak bercabang (git-submodule, yang baru-baru ini saya pelajari). Dengan begitu semua cabang memiliki semua migrasi. Bagian lengket memastikan setiap cabang dibatasi hanya untuk migrasi yang mereka pedulikan. Melakukan / mencatat itu secara manual akan menjadi pita dan cenderung kesalahan. Tetapi tidak ada alat migrasi yang dibangun untuk ini. Itulah titik di mana saya tanpa jalan ke depan.


Ini adalah ide yang bagus tetapi apa yang terjadi ketika cabang mengganti nama kolom? Sisa cabang akan melihat meja yang rusak.
Kostas

um - itu adalah bagian lengket - cabang mana yang peduli tentang migrasi mana. jadi Anda bisa "menyinkronkan" dan ia tahu, "kembalikan migrasi ini" sehingga kolom kembali.
JohnO

1

Saya akan menyarankan satu dari dua opsi:

Pilihan 1

  1. Masukkan data Anda seeds.rb. Pilihan yang bagus adalah membuat data seed Anda melalui permata FactoryGirl / Fabrikasi. Dengan cara ini Anda dapat menjamin bahwa data sinkron dengan kode jika kami menganggap, bahwa pabrik diperbarui bersama dengan penambahan / penghapusan kolom.
  2. Setelah berpindah dari satu cabang ke cabang lainnya, jalankan rake db:reset, yang secara efektif menjatuhkan / membuat / menambakan basis data.

pilihan 2

Secara manual menjaga status database dengan selalu menjalankan rake db:rollback/ rake db:migratesebelum / setelah checkout cabang. Peringatannya adalah bahwa semua migrasi Anda harus dapat dibalik, jika tidak ini tidak akan berhasil.


0

Tentang lingkungan pengembangan:

Anda harus bekerja dengan rake db:migrate:redountuk menguji apakah skrip Anda dapat dibalik, tetapi perlu diingat selalu harus seed.rbdengan populasi data.

Jika Anda bekerja dengan git, Anda seed.rb harus diubah dengan perubahan migrasi, dan eksekusi db:migrate:redountuk permulaan (memuat data untuk pengembangan baru di komputer lain atau database baru)

Terlepas dari "perubahan", dengan metode Anda atas dan ke bawah, kode Anda selalu menjadi skenario untuk "perubahan" pada saat ini dan kapan mulai dari nol.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.