2012-07-03 32 views
7

Tôi có đoạn mã sau trong một mô hình đường ray:Cơ sở dữ liệu khóa không làm việc như mong đợi với Rails & Postgres

foo = Food.find(...) 
foo.with_lock do 
    if bar = foo.bars.find_by_stuff(stuff) 
    # do something with bar 
    else 
    bar = foo.bars.create! 
    # do something with bar 
    end 
end 

Mục đích là để đảm bảo rằng một Bar của các loại được tạo ra không được tạo ra hai lần.

Kiểm tra with_lock hoạt động tại bảng điều khiển xác nhận mong đợi của tôi. Tuy nhiên, trong sản xuất, có vẻ như trong một số hoặc tất cả các trường hợp khóa không hoạt động như mong đợi, và thanh dự phòng đang được thử - vì vậy, with_lock không (luôn luôn?) Dẫn đến mã chờ đến lượt nó .

Điều gì có thể xảy ra ở đây?

cập nhật rất tiếc cho tất cả những người đã nói "khóa foo sẽ không giúp bạn" !! ví dụ của tôi ban đầu không có tra cứu thanh. điều này đã được khắc phục ngay bây giờ.

+0

Bạn kiểm tra xem thanh đã tồn tại ở đâu? –

+1

@FrederickCheung: Tại sao bạn lại bận tâm kiểm tra? Bất kỳ kiểm tra nào bên ngoài cơ sở dữ liệu sẽ ** luôn luôn ** có lỗ và điều kiện chủng tộc. –

+0

Cá nhân tôi sẽ không nhưng nó có thể hữu ích cho OP để làm việc ra mà bit của sự hiểu biết của họ là thiếu sót. –

Trả lời

1

Lý do tại sao khóa sẽ không hoạt động trong ứng dụng Rails trong bộ nhớ cache truy vấn.

Nếu bạn cố gắng lấy khóa độc quyền trên cùng một hàng nhiều lần trong một yêu cầu, truy vấn được lưu trong bộ nhớ cache để truy vấn khóa tiếp theo không bao giờ tiếp cận chính DB.

The issue has been reported trên Github.

2

Tại sao bạn không sử dụng hạn chế duy nhất? Nó được làm cho tính độc đáo

+0

tôi có một sự ràng buộc duy nhất tại chỗ, và nó thực hiện công việc của mình. đôi khi một trong những lưu đồng thời không thành công. vì vậy, không có dữ liệu dư thừa nào được tạo ra, nhưng mã sẽ thổi lên thay vì đợi đến lượt của nó. –

+0

Bạn không cần khóa bàn để xử lý lỗi, nó làm cho ứng dụng của bạn cực kỳ chậm. RoR phải có cái gì đó tốt hơn, nhưng tôi không thể giúp bạn ở đó. –

+3

@JohnBachir: Nó thổi lên vì bạn không bắt và xử lý ngoại lệ được nêu ra khi ràng buộc duy nhất bị vi phạm. Bạn ** phải ** bẫy và xử lý những ngoại lệ, không có gì lành mạnh bạn có thể làm trong Rails để ngăn chặn chúng. Chiến lược lành mạnh duy nhất với những thứ này là để cho cơ sở dữ liệu đối phó với nó, mã của bạn nên đơn giản là thử và đối phó với các ngoại lệ và thất bại dự kiến. –

6

Bạn đang bối rối về những gì with_lock làm. Từ fine manual:

with_lock (khóa = true)

Wraps khối thông qua trong một giao dịch, khóa các đối tượng trước khi năng suất. Bạn có thể vượt qua mệnh đề khóa SQL làm đối số (xem lock!).

Nếu bạn kiểm tra những gì with_lock làm trong nội bộ, bạn sẽ thấy rằng nó là ít hơn một wrapper mỏng xung quanh lock!:

khóa (lock = true)

Lấy một khóa hàng trên hồ sơ này. Tải lại bản ghi để lấy khóa được yêu cầu.

Vì vậy, with_lock chỉ cần thực hiện khóa và khóa hàng foo.

Đừng bận tâm với tất cả khóa vô nghĩa này. Cách duy nhất để xử lý tình huống này là sử dụng một ràng buộc duy nhất trong cơ sở dữ liệu, không ai ngoại trừ cơ sở dữ liệu có thể đảm bảo tính duy nhất trừ khi bạn muốn làm những việc vô lý như khóa toàn bộ các bảng; sau đó chỉ cần đi trước và mù quáng thử INSERT hoặc UPDATE của bạn và bẫy và bỏ qua ngoại lệ sẽ được nâng lên khi ràng buộc duy nhất bị vi phạm.

+0

Tôi biết việc sử dụng khóa của tôi trên 'foo' hơi bị hack và gợi ý xử lý lỗi ràng buộc duy nhất của bạn được thực hiện tốt (và thực sự là những gì tôi đã làm) - nhưng tôi vẫn muốn biết tại sao mã hacky này không hoạt động. nó có hành vi mà tôi mong muốn, mà tôi đã xác nhận ở bàn điều khiển và các thử nghiệm trong môi trường dev của tôi. Trong khi đợi khóa trên 'foo' một quá trình sẽ đợi quá trình khác. Tôi đã không bao giờ có bất kỳ lỗi thời gian chờ hoặc bế tắc từ mã này, đó là một tác dụng phụ tôi có thể sợ từ kém thiết kế nó. –

+0

@JohnBachir: Bạn nghĩ 'foo.with_lock' là gì và tại sao bạn nghĩ nó nên giải quyết vấn đề của bạn? –

+1

@muistooshort Tôi không thể đồng ý hơn với tuyên bố, "Đừng bận tâm với tất cả những điều vô nghĩa này". Đề xuất của bạn hoạt động hoàn hảo cho tôi. Cảm ơn bạn. – Hoa

1

Các cách chính xác để xử lý tình trạng này là thực sự đúng trong các tài liệu Rails:.

http://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by

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

("find_or_create_by" không phải là nguyên tử, nó thực sự là một tìm và sau đó một tạo Vì vậy, thay thế với tìm kiếm của bạn và sau đó tạo. Các tài liệu trên trang này mô tả chính xác trường hợp này.)

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