2012-05-17 27 views
15

Tôi đang cố gắng thực thi tính duy nhất của các giá trị trong một trong các trường bảng của tôi. Thay đổi bảng không phải là một lựa chọn. Tôi cần phải sử dụng ActiveRecord để có điều kiện chèn một hàng vào bảng nhưng tôi lo lắng về việc đồng bộ hóa.Điều kiện chủng tộc trong Rails first_or_create

first_or_create trong Rails ActiveRecord ngăn chặn điều kiện chủng tộc không?

Đây là mã nguồn cho first_or_create từ GitHub:

def first_or_create(attributes = nil, options = {}, &block) 
    first || create(attributes, options, &block) 
end 

Có thể rằng một mục trùng lặp sẽ cho kết quả trong cơ sở dữ liệu do các vấn đề đồng bộ hóa với nhiều quy trình?

+2

AR có đầy đủ các điều kiện chủng tộc như thế này. – dbenhur

+0

Xem (SO dup) [Làm cách nào để tránh tình trạng cuộc đua trong ứng dụng Rails của tôi?] (Http://stackoverflow.com/questions/3037029/how-do-i-avoid-a-race-condition-in-my -rails-app) và Rails Cookbook [Tránh điều kiện chủng tộc với khóa lạc quan] (http://underpop.free.fr/r/ruby-on-rails/cookbook/I_0596527314_CHP_3_SECT_19.html) – dbenhur

+0

@dbenhur - Tôi không thể sử dụng khóa lạc quan vì nó liên quan đến việc thêm một trường vào bảng. Một trong những điều kiện của tôi là tôi không thể thêm một trường để nó không trùng lặp. –

Trả lời

5

Có, có thể.

Bạn có thể giảm đáng kể cơ hội xung đột với optimistic hoặc pessimistic locking. Tất nhiên khóa lạc quan đòi hỏi phải thêm một trường vào bảng, và khóa bi quan không quy mô là tốt - cộng, nó phụ thuộc vào khả năng lưu trữ dữ liệu của bạn.

Tôi không chắc chắn liệu bạn có cần thêm sự bảo vệ hay không nhưng có sẵn.

+0

Tôi không thể sử dụng khóa lạc quan vì sửa đổi bảng không phải là một tùy chọn. Vấn đề với khóa bi quan là tôi không thể chỉ định mức cô lập khóa mà tôi cần thông qua Rails mà không chèn cú pháp cụ thể của InnoDB. Tôi đã kết thúc bằng cách sử dụng 'first_or_create' bất chấp khả năng của các hàng trùng lặp. Mặc dù tôi nghĩ rằng tôi có thể đã sử dụng trình trợ giúp xác thực trong ActiveRecord để đảm bảo tính duy nhất. –

+2

Họ bị cùng một điều kiện chủng tộc –

17

Các Rails 4 documentation for find_or_create_by cung cấp một mẹo có thể hữu ích cho tình trạng này:

Xin lưu ý phương pháp này là không nguyên tử, nó chạy đầu tiên một SELECT, và nếu không có kết quả một INSERT là đã cố gắng. Nếu có các chủ đề hoặc quy trình khác, có một điều kiện chủng tộc giữa cả hai cuộc gọi và có thể là trường hợp bạn kết thúc với hai bản ghi tương tự.

Cho dù đó là một vấn đề hay không phụ thuộc vào logic của ứng dụng, nhưng trong trường hợp đặc biệt, trong đó hàng có một hạn chế UNIQUE một ngoại lệ có thể tăng lên, chỉ cần thử lại:

begin 
    CreditAccount.find_or_create_by(user_id: user.id) 
rescue ActiveRecord::RecordNotUnique 
    retry 
end 

tương tự lỗi bắt có thể hữu ích cho Rails 3. (Không chắc chắn nếu lỗi ActiveRecord::RecordNotUnique tương tự được ném trong Rails 3, vì vậy việc triển khai của bạn có thể cần khác nhau.)

+0

Chỉ cần cho bản ghi: Rails 3 ném một 'ActiveRecord :: StatementInvalid' (những gì không phải là rất cụ thể). – spickermann

+0

Rất tiếc.Vì vậy, bạn vẫn có khả năng làm một 'rescue ActiveRecord :: StatementInvalid' để thử lại, ngay cả khi nó không đọc cũng như trong Rails 4? –

+0

Bạn có thể. Nhưng vấn đề là 'StatementInvalid' cũng bao gồm SQL không hợp lệ,' NULL' trong các cột không null và vân vân. Bạn có thể thử lại những thứ sẽ không bao giờ hoạt động. Có lẽ bạn muốn sử dụng một cái gì đó như https://github.com/nfedyashev/retryable#readme để tránh chạy vào một vòng lặp. – spickermann

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