2009-03-26 34 views
77

Làm thế nào để bạn quay trở lại di chuyển đường ray không thành công? Tôi hy vọng rằng rake db:rollback sẽ hoàn tác việc di chuyển không thành công, nhưng không, nó sẽ cuộn lại lần di chuyển trước đó (di chuyển không thành công trừ đi một). Và rake db:migrate:down VERSION=myfailedmigration cũng không hoạt động. Tôi đã chạy vào điều này một vài lần và nó rất bực bội. Dưới đây là một thử nghiệm đơn giản, tôi đã lặp lại trong các vấn đề:Quay lại di chuyển Rails không thành công

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: migrating ===================================================== 
-- add_column(:assets, :test, :integer) 
    -> 0.0932s 
-- add_column(:asset, :error) 
rake aborted! 
An error has occurred, all later migrations canceled: 

wrong number of arguments (2 for 3) 

ok, cho phép cuộn nó lại:

 
$ rake db:rollback 
== AddLevelsToRoles: reverting =============================================== 
-- remove_column(:roles, :level) 
    -> 0.0778s 
== AddLevelsToRoles: reverted (0.0779s) ====================================== 

huh? đó là lần di chuyển cuối cùng của tôi trước SimpleTest, không phải là di chuyển không thành công. (Và oh, nó sẽ được tốt đẹp nếu sản lượng di cư bao gồm số phiên bản.)

Vì vậy, cho phép thử chạy xuống cho SimpleTest di cư thất bại:

 
$ rake db:migrate:down VERSION=20090326173033 
$ 

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

 
$ rake db:migrate:up VERSION=20090326173033 
== SimpleTest: migrating ===================================================== 
-- add_column(:assets, :test, :integer) 
rake aborted! 
Mysql::Error: Duplicate column name 'test': ALTER TABLE `assets` ADD `test` int(11) 

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

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

Trả lời

73

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

Đường ray 2.2 bao gồm di chuyển giao dịch cho PostgreSQL. Đường ray 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 giải quyết 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 về các dự án trong tương lai, tôi khuyên bạn nên sử dụng cơ sở dữ liệu với hỗ trợ DDL giao dịch vì nó giúp 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

Tuyệt vời, Cảm ơn. Tôi sẽ thực hiện các dự án mới với PGSQL để bạn biết đó là một lựa chọn tốt. –

+0

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

20

Để đi đến một phiên bản cụ thể chỉ cần sử dụng:

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

Nhưng nếu một sự chuyển đổi thất bại một phần bằng cách nào, bạn sẽ phải làm sạch nó lên đầu tiên. Một cách sẽ là:

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

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

9

Cách đơn giản để làm điều này là để bọc 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 ý, "MySql [ 's bảng MyISAM không] hỗ trợ giao dịch" - đó là lý do tại sao bạn có thể xem xét 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 InnoDB của MySQL, thì điều trên sẽ chỉ hoạt động tốt. Bất kỳ lỗi nào trong lên hoặc xuống sẽ bị loại bỏ.

ĐƯỢC AWARE 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 bảng (thả bảng, xóa hoặc thêm cột, v.v.) không thể được khôi phục.

+5

Nó không phải là một câu hỏi của MyISAM hoặc 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 trở lại thay đổi đó! –

+1

Luke đúng, mysql không hỗ trợ giao dịch trên các thay đổi của DDL. Tôi phải xem xét việc làm sạch một mình như thêm và loại bỏ một cột từ bảng. –

1

Tôi đã có một lỗi đánh máy (trong "add_column"):

def self.up

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

cuối

def self.down

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

cuối

và sau đó vấn đề của bạn (không thể hoàn tác di cư một phần thất bại). sau một số thất bại googling tôi chạy này:

def self.up

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

cuối

def self.down

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

cuối

như bạn có thể thấy tôi chỉ cần thêm dòng hiệu chỉnh bằng tay, và sau đó gỡ bỏ nó một lần nữa, trước khi tôi kiểm tra nó.

12

Tôi đồng ý rằng bạn nên sử dụng PostgreSQL khi có thể. Tuy nhiên, khi bạn đang mắc kẹt với MySQL, bạn có thể tránh hầu hết những vấn đề này bằng cách cố gắng chuyển đổi của bạn trên cơ sở dữ liệu thử nghiệm của bạn đầu tiên:

rake db:migrate RAILS_ENV=test 

Bạn có thể trở lại trạng thái trước đó và thử lại với

rake db:schema:load RAILS_ENV=test 
+0

Thêm một giải pháp thay vì 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

18

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

  1. Tìm ra phần nào của quá trình di chuyển đã hoạt động. Bình luận những người ra.
  2. Cũng nhận xét ra/xóa phần di chuyển đã bị hỏng.
  3. Chạy lại 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 di cư bạn nhận xét ra ở 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 một cái gì đó rất giống nhau, nhưng tôi thay thế bước 2 bằng, "sửa chữa một phần của di cư đã phá vỡ." –

+1

Đáng chú ý nhấn mạnh điểm cuối cùng - chạy lệnh 'bundle exec rake db: migrate: redo'. Nó sẽ đi một bước trở lại và một bước về phía trước, vì vậy bạn có thể xác minh rằng di chuyển mới nhất của bạn chạy tất cả các cách thức thông qua. Đây là một thực hành tốt bất cứ lúc nào bạn phải đẩy di chuyển cùng với một số cập nhật mã. – mahemoff

8

Tại năm 2015 với Rails 4.2.1 và MySQL 5.7, một sự chuyển đổi thất bại không thể được cố định với hành động cào tiêu chuẩn mà Rails cung cấp, vì nó đang ở năm 2009.

MySql không hỗ trợ rollback của statments DDL (tại MySQL 5.7 Manual). Và Rails không thể làm bất cứ điều gì với điều đó.

Ngoài ra, chúng tôi có thể kiểm tra cách Rails đang thực hiện công việc: Di chuyển là wrapped in a transaction tùy thuộc vào cách bộ điều hợp kết nối phản hồi với :supports_ddl_transactions?. Sau khi tìm kiếm hành động này tại nguồn đường ray (v 4.2.1), tôi thấy rằng chỉ Sqlite3PostgreSql hỗ trợ các giao dịch và bởi default 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 khắc phục thủ công.

+0

Tôi không 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, đối với câu hỏi ban đầu. Đối với tiền thưởng bắt đầu cho Andrew Grimm: "Bạn muốn biết nếu tình hình đã thay đổi kể từ khi câu hỏi được hỏi vào tháng Ba năm 2009." Đó là câu trả lời hiện tại và đưa ra một phương pháp để kiểm tra bất kỳ thay đổi nào trong tương lai. –

1

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

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

Khi di cư myfailedmigration thất bại, nó không được coi là áp dụng, và điều này có thể được xác nhận bằng cách chạy rake db:migrate:status, trong đó sẽ hiển thị kết quả 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 lực còn lại của add_column :assets, :test, :integer đang được thực hiện trên quá trình di chuyển không thành công sẽ phải được đảo ngược ở cấp cơ sở dữ liệu với truy vấn alter table assets drop column test;.

Các vấn đề liên quan