Quay lại quá trình di chuyển Rails không thành công


82

Làm thế nào để bạn khôi phục lại một lần di chuyển đường ray bị lỗi? Tôi mong đợi điều đó rake db:rollbacksẽ hoàn tác quá trình di chuyển không thành công, nhưng không, nó sẽ quay trở lại lần di chuyển trước đó (lần di chuyển không thành công trừ đi một). Và rake db:migrate:down VERSION=myfailedmigrationcũng không hoạt động. Tôi đã gặp phải điều này một vài lần và nó rất bực bội. Đây là một thử nghiệm đơn giản mà tôi đã thực hiện để khắc phục sự cố:

class SimpleTest < ActiveRecord::Migration
  def self.up
    add_column :assets, :test, :integer
    # the following syntax error will cause the migration to fail
    add_column :asset, :test2, :integer
  end

  def self.down
    remove_column :assets, :test
    remove_column :assets, :test2
  end
end

kết quả:

== SimpleTest: di chuyển ============================================= ========
- add_column (: asset,: test,: integer)
   -> 0,0932 giây
- add_column (: asset,: error)
cào bị hủy bỏ!
Đã xảy ra lỗi, tất cả các lần di chuyển sau này đều bị hủy:

sai số đối số (2 cho 3)

được rồi, hãy khôi phục lại:

$ rake db: rollback
== AddLevelsToRoles: hoàn nguyên ============================================= ==
- remove_column (: role,: level)
   -> 0,0778 giây
== AddLevelsToRoles: hoàn nguyên (0,0779 giây) ======================================

Huh? đó là lần di chuyển cuối cùng của tôi trước SimpleTest, không phải lần di chuyển thất bại. (Và ồ, sẽ thật tuyệt nếu đầu ra di chuyển bao gồm số phiên bản.)

Vì vậy, hãy thử chạy xuống SimpleTest di chuyển không thành công:

$ rake db: migrate: down VERSION = 20090326173033
$

Không có gì xảy ra, và cũng không có đầu ra. Nhưng có lẽ nó đã chạy quá trình di chuyển? Vì vậy, hãy sửa lỗi cú pháp trong quá trình di chuyển SimpleTest và thử chạy lại.

$ rake db: migrate: up VERSION = 20090326173033
== SimpleTest: di chuyển ============================================= ========
- add_column (: asset,: test,: integer)
cào bị hủy bỏ!
Mysql :: Lỗi: Tên cột trùng lặp 'test': ALTER TABLE `asset` ADD` test` int (11)

Không. Rõ ràng là di chuyển: xuống không hoạt động. Nó không thất bại, nó chỉ là không thực thi.

Không có cách nào để loại bỏ bảng trùng lặp đó ngoài việc truy cập thủ công vào cơ sở dữ liệu và xóa nó, sau đó chạy thử nghiệm. Phải có một cách tốt hơn thế.

Câu trả lời:


79

Thật không may, bạn phải dọn dẹp thủ công các lần di chuyển không thành công cho MySQL. MySQL không hỗ trợ thay đổi định nghĩa cơ sở dữ liệu giao dịch.

Rails 2.2 bao gồm các chuyển đổi giao dịch cho PostgreSQL. Rails 2.3 bao gồm di chuyển giao dịch cho SQLite.

Điều này không thực sự giúp bạn cho vấn đề của bạn ngay bây giờ, nhưng nếu bạn có lựa chọn cơ sở dữ liệu cho các dự án trong tương lai, tôi khuyên bạn nên sử dụng một cơ sở dữ liệu có hỗ trợ DDL giao dịch vì nó làm cho việc di chuyển dễ chịu hơn nhiều.

Cập nhật - điều này vẫn đúng vào năm 2017, trên Rails 4.2.7 và MySQL 5.7, được Alejandro Babio báo cáo trong một câu trả lời khác tại đây.


1
Cảm ơn vô cùng. Tôi sẽ thực hiện các dự án mới với PGSQL nên thật tốt khi biết đó là một lựa chọn.
insane.dreamer

Đây vẫn là câu trả lời tốt nhất, vì vậy điều này xứng đáng nhận được tiền thưởng imho.
nathanvda

20

Để chuyển đến một phiên bản được chỉ định, chỉ cần sử dụng:

rake db:migrate VERSION=(the version you want to go to)

Nhưng nếu quá trình di chuyển không thành công, bạn sẽ phải dọn dẹp nó trước. Một cách sẽ là:

  • chỉnh sửa downphương pháp di chuyển để chỉ hoàn tác phần upđã hoạt động
  • di chuyển trở lại trạng thái trước đó (nơi bạn bắt đầu)
  • sửa lỗi di chuyển (bao gồm cả việc hoàn tác các thay đổi của bạn đối với down)
  • thử lại

Cảm ơn. Có, tôi biết tôi có thể di cư lại cho đến khi di chuyển không thành công, nhưng trong trường hợp tôi có lịch sử di cư lâu dài, điều này đôi khi có thể là vấn đề. Lý tưởng nhất là họ nên thực hiện tất cả chỉ là tốt, nhưng thường xuyên hơn không Tôi đã có họ thất bại partway, và sau đó có một mớ hỗn độn lớn hơn :-)
insane.dreamer

20

OK, mọi người, đây là cách bạn thực sự làm điều đó. Tôi không biết những câu trả lời trên đang nói về điều gì.

  1. Tìm ra phần nào của quá trình di chuyển lên đã hoạt động. Nhận xét những người ra.
  2. Đồng thời nhận xét / xóa phần di chuyển bị hỏng.
  3. Chạy lại quá trình di chuyển. Bây giờ nó sẽ hoàn thành các phần không bị hỏng của quá trình di chuyển, bỏ qua các phần đã được thực hiện.
  4. Bỏ ghi chú các bit của quá trình di chuyển mà bạn đã nhận xét ở bước 1.

Bạn có thể di chuyển xuống và sao lưu lại nếu bạn muốn xác minh rằng bạn đã có nó ngay bây giờ.


2
Tôi làm điều gì đó rất tương tự, nhưng tôi thay thế bước 2 bằng, "sửa phần di chuyển bị hỏng."
Don Kirkby

2
Đáng nhấn mạnh điểm cuối cùng - chạy bundle exec rake db:migrate:redo. Nó sẽ lùi lại một bước và tiến một bước, vì vậy bạn có thể xác minh rằng quá trình di chuyển mới nhất của mình đã chạy xuyên suốt. Đây là một thực tiễn tốt bất cứ khi nào bạn phải thúc đẩy quá trình di chuyển cùng với một số cập nhật mã.
mahemoff

12

Tôi đồng ý rằng bạn nên sử dụng PostgreSQL khi có thể. Tuy nhiên, khi bạn gặp khó khăn với MySQL, bạn có thể tránh hầu hết các vấn đề này bằng cách thử di chuyển trên cơ sở dữ liệu thử nghiệm của mình trước:

rake db:migrate RAILS_ENV=test

Bạn có thể hoàn nguyên về trạng thái trước đó và thử lại với

rake db:schema:load RAILS_ENV=test

Nhiều cách giải quyết hơn là một câu trả lời, nhưng đây là một ý tưởng hay chưa từng xảy ra với tôi trước đây.
Emily

10

Vào năm 2015 với Rails 4.2.1 và MySQL 5.7, không thể sửa lỗi di chuyển bằng các hành động rake tiêu chuẩn mà Rails cung cấp, như ở năm 2009.

MySql không hỗ trợ khôi phục các trạng thái DDL (tại MySQL 5.7 Manual ). Và Rails không thể làm gì với điều đó.

Ngoài ra, chúng ta có thể kiểm tra cách Rails thực hiện công việc: Một quá trình di chuyển được bao bọc trong một giao dịch tùy thuộc vào cách bộ điều hợp kết nối phản hồi :supports_ddl_transactions?. Sau khi tìm kiếm hành động này tại nguồn rails (v 4.2.1), tôi thấy rằng chỉ có Sqlite3PostgreSql hỗ trợ các giao dịch và theo mặc định thì nó không được hỗ trợ.

Chỉnh sửa Vì vậy, câu trả lời hiện tại cho câu hỏi ban đầu: Di chuyển MySQL không thành công phải được sửa theo cách thủ công.


Tôi không hoàn toàn hiểu câu trả lời này: ngoại trừ việc cập nhật số phiên bản, nó không thêm gì vào câu trả lời được chấp nhận ban đầu.
nathanvda

1
Rất đúng, cho câu hỏi ban đầu. Đối với tiền thưởng bắt đầu cho Andrew Grimm: "Muốn biết liệu tình hình có thay đổi kể từ khi câu hỏi được đưa ra vào tháng 3 năm 2009." Đó là câu trả lời hiện tại và đưa ra phương pháp để kiểm tra bất kỳ thay đổi nào trong tương lai.
Alejandro Babio

8

Cách dễ dàng để làm điều này là gói tất cả các hành động của bạn trong một giao dịch:

class WhateverMigration < ActiveRecord::Migration

 def self.up
    ActiveRecord::Base.transaction do
...
    end
  end

  def self.down
    ActiveRecord::Base.transaction do
...
    end
  end

end

Như Luke Francl đã lưu ý, "Các bảng MyISAM của MySql [không] hỗ trợ các giao dịch" - đó là lý do tại sao bạn có thể cân nhắc việc tránh MySQL nói chung hoặc ít nhất là MyISAM nói riêng.

Nếu bạn đang sử dụng MySQL's InnoDB, thì cách trên sẽ hoạt động tốt. Bất kỳ lỗi nào trong cả hai hướng lên hoặc xuống sẽ bị loại bỏ.

HÃY NHẬN THỨC một số loại hành động không thể hoàn nguyên thông qua giao dịch. Nói chung, các thay đổi của bảng (giảm bảng, xóa hoặc thêm cột, v.v.) không thể được khôi phục.


5
Đó không phải là câu hỏi của MyISAM hay InnoDB. InnoDB hỗ trợ các giao dịch, nhưng nó không hỗ trợ các thay đổi định nghĩa cơ sở dữ liệu giao dịch (DDL). Trong PostgreSQL, bạn có thể thả một bảng và sau đó quay lại thay đổi đó!
Luke Francl

1
Luke đúng, mysql không hỗ trợ giao dịch khi thay đổi DDL. Tôi phải xem xét việc dọn dẹp một mình chẳng hạn như thêm và xóa một cột khỏi bảng.
Leon Guan


1

Tôi mắc lỗi đánh máy (trong "add_column"):

def self.up

add_column :medias, :title, :text
add_colunm :medias, :enctype, :text

kết thúc

def self.down

remove_column :medias, :title
remove_column :medias, :enctype   

kết thúc

và sau đó là sự cố của bạn (không thể hoàn tác quá trình di chuyển không thành công một phần). sau một số googling không thành công, tôi đã chạy cái này:

def self.up

remove_column :medias, :title
add_column :medias, :title, :text
add_column :medias, :enctype, :text

kết thúc

def self.down

remove_column :medias, :title
remove_column :medias, :enctype

kết thúc

như bạn có thể thấy, tôi vừa thêm dòng chỉnh sửa bằng tay, sau đó lại xóa nó trước khi kiểm tra.


1

Câu trả lời của Alejandro Babio ở trên cung cấp câu trả lời tốt nhất hiện tại.

Một chi tiết bổ sung mà tôi muốn thêm:

Khi quá myfailedmigrationtrình di chuyển không thành công, nó không được coi là đã áp dụng và điều này có thể được xác minh bằng cách chạy rake db:migrate:status, nó sẽ hiển thị đầu ra tương tự như sau:

$  rake db:migrate:status
database: sample_app_dev

 Status   Migration ID    Migration Name
--------------------------------------------------
   up      20130206203115  Create users
   ...
   ...
   down    20150501173156  Test migration

Hiệu ứng còn lại của add_column :assets, :test, :integerviệc được thực thi đối với việc di chuyển không thành công sẽ phải được đảo ngược ở cấp cơ sở dữ liệu bằng một alter table assets drop column test;truy vấn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.