2016-09-27 10 views
15

Từ các khối nguyên tử docs of atomic()Không làm tổ phiên bản @atomic() ở Django?

có thể được lồng

này âm thanh như một tính năng tuyệt vời, nhưng trong trường hợp sử dụng của tôi, tôi muốn điều ngược lại: Tôi muốn giao dịch để đảm bảo độ bền ngay sau khi khối trang trí với @atomic() được để lại thành công.

Có cách nào để đảm bảo độ bền trong xử lý giao dịch của django không?

Bối cảnh

Giao dịch là ACID. Chữ "D" là viết tắt của độ bền. Đó là lý do tại sao tôi nghĩ rằng giao dịch không thể lồng nhau mà không mất tính năng "D".

Ví dụ: Nếu giao dịch bên trong thành công, nhưng giao dịch bên ngoài thì không, giao dịch bên ngoài và bên trong sẽ được khôi phục. Kết quả: Giao dịch bên trong không bền.

Tôi sử dụng PostgreSQL, nhưng AFAIK điều này không quan trọng lắm.

Trả lời

7

Bạn không thể thực hiện điều đó thông qua bất kỳ API nào.

Giao dịch không thể lồng trong khi giữ lại tất cả thuộc tính ACID và không phải tất cả cơ sở dữ liệu đều hỗ trợ các giao dịch lồng nhau.

Chỉ khối nguyên tử ngoài cùng mới tạo giao dịch. Các khối nguyên tử bên trong tạo ra một điểm lưu trữ bên trong giao dịch và giải phóng hoặc cuộn lại điểm lưu trữ khi thoát khỏi khối bên trong. Như vậy, các khối nguyên tử bên trong cung cấp nguyên tử, nhưng như bạn đã lưu ý, không phải ví dụ Độ bền.

Vì khối nguyên tử ngoài cùng tạo ra một giao dịch, nó phải cung cấp nguyên tử và bạn không thể cam kết khối nguyên tử lồng nhau vào cơ sở dữ liệu nếu giao dịch có chứa không được cam kết.

Cách duy nhất để đảm bảo rằng khối bên trong được cam kết, là đảm bảo rằng mã trong giao dịch kết thúc thực thi mà không có bất kỳ lỗi nào.

+0

Tôi không thể tìm thấy câu trả lời cho câu hỏi của mình trong văn bản của bạn. Bạn nói rằng tôi cần phải đảm bảo rằng các khối nguyên tử bên ngoài được cam kết. Vâng đó là sự thật. Làm thế nào để làm điều này trong django? – guettli

+0

@guettli Tôi đã cập nhật câu trả lời của mình. Không có API để làm điều đó, cách duy nhất để đạt được điều này là đảm bảo mã trong giao dịch kết thúc mà không có bất kỳ lỗi nào. – knbk

+0

@guettli, nếu bạn muốn quản lý giao dịch ở cấp độ thấp trong django, bạn có thể sử dụng trực tiếp gói giao dịch 'giao dịch'. Ví dụ, bạn có thể thấy https://github.com/2ps/djenga/blob/master/djenga/db/nested_transactions.py đó là một cái gì đó tôi đã viết lại khi django không hỗ trợ các giao dịch lồng nhau trong mysql. – 2ps

6

Tôi đồng ý với câu trả lời của knbk rằng điều đó là không thể: độ bền chỉ xuất hiện ở cấp độ giao dịch và nguyên tử cung cấp điều đó. Nó không cung cấp nó ở mức tiết kiệm điểm. Tùy thuộc vào trường hợp sử dụng, có thể có cách giải quyết.

Tôi đoán trường hợp sử dụng của bạn là một cái gì đó như:

@atomic # possibly implicit if ATOMIC_REQUESTS is enabled 
def my_view(): 
    run_some_code() # It's fine if this gets rolled back. 
    charge_a_credit_card() # It's not OK if this gets rolled back. 
    run_some_more_code() # This shouldn't roll back the credit card. 

Tôi nghĩ rằng bạn muốn một cái gì đó như:

@transaction.non_atomic_requests 
def my_view(): 
    with atomic(): 
     run_some_code() 
    with atomic(): 
     charge_a_credit_card() 
    with atomic(): 
     run_some_more_code() 

Nếu trường hợp sử dụng của bạn là dành cho thẻ tín dụng đặc biệt (như tôi là khi tôi gặp vấn đề này vài năm trước), đồng nghiệp của tôi đã phát hiện ra rằng credit card processors actually provide mechanisms for handling this. Một cơ chế tương tự có thể làm việc đối với trường hợp sử dụng của bạn, tùy thuộc vào cấu trúc vấn đề:

@atomic 
def my_view(): 
    run_some_code() 
    result = charge_a_credit_card(capture=False) 
    if result.successful: 
     transaction.on_commit(lambda: result.capture()) 
    run_some_more_code() 

Một lựa chọn khác là sử dụng một cơ chế bền bỉ không giao dịch để ghi lại những gì bạn đang quan tâm, giống như một cơ sở dữ liệu nhật ký, hoặc một chuỗi các thứ cần ghi lại.

+1

Liên kết của bạn với "bộ xử lý thẻ tín dụng thực sự cung cấp cơ chế để xử lý điều này" có vẻ như Giao thức cam kết hai pha: https://en.wikipedia.org/wiki/Two-phase_commit_protocol – guettli

6

Loại này là độ bềnkhông thể do ACID, với một kết nối. (nghĩa là một khối lồng nhau vẫn được cam kết trong khi khối ngoài được cuộn lại) Đây là hậu quả của ACID, không phải là vấn đề của Django. Hãy tưởng tượng một cơ sở dữ liệu siêu và trường hợp bảng B có một khóa ngoại để bàn A.

CREATE TABLE A (id serial primary key); 
CREATE TABLE B (id serial primary key, b_id integer references A (id)); 
-- transaction 
    INSERT INTO A DEFAULT VALUES RETURNING id AS new_a_id 
    -- like it would be possible to create an inner transaction 
     INSERT INTO B (a_id) VALUES (new_a_id) 
    -- commit 
-- rollback (= integrity problem) 

Nếu giao dịch bên trong "bền" trong khi giao dịch (bên ngoài) bị cuộn lùi thì tính toàn vẹn sẽ bị hỏng. Các hoạt động rollback phải luôn luôn được thực hiện để nó không bao giờ có thể thất bại, do đó không có cơ sở dữ liệu nào sẽ thực hiện một giao dịch độc lập lồng nhau. Nó sẽ là trái với nguyên tắc nhân quả và tính toàn vẹn không thể được bảo đảm sau khi rollback chọn lọc như vậy. Nó cũng chống lại nguyên tử.

Giao dịch có liên quan đến kết nối cơ sở dữ liệu. Nếu bạn tạo hai kết nối thì hai giao dịch độc lập được tạo. Một kết nối không nhìn thấy các hàng giao dịch khác không được cam kết (có thể đặt mức cách ly này, nhưng phụ thuộc vào phần cuối cơ sở dữ liệu) và không có khóa ngoài nào được tạo và tính toàn vẹn được bảo toàn sau khi khôi phục cơ sở dữ liệu thiết kế phụ trợ.

Django hỗ trợ nhiều cơ sở dữ liệu, do đó có nhiều kết nối.

# no ATOMIC_REQUESTS should be set for "other_db" in DATABASES 

@transaction.atomic # atomic for the database "default" 
def my_view(): 
    with atomic(): # or set atomic() here, for the database "default" 
     some_code() 
     with atomic("other_db"): 
      row = OtherModel.objects.using("other_db").create(**kwargs) 
     raise DatabaseError 

Dữ liệu trong "other_db" vẫn được cam kết. Có thể là ở Django để tạo một thủ thuật với hai kết nối đến cùng một cơ sở dữ liệu giống như hai cơ sở dữ liệu, với một số cơ sở dữ liệu, nhưng tôi chắc chắn rằng nó chưa được kiểm tra, nó sẽ dễ mắc lỗi, với các vấn đề với việc di chuyển, tải lớn hơn bởi backend cơ sở dữ liệu phải tạo các giao dịch song song thực sự ở mọi yêu cầu và nó không thể được tối ưu hóa. Tốt hơn là nên sử dụng hai cơ sở dữ liệu thực hoặc để tổ chức lại mã.

Cài đặt DATABASE_ROUTERS rất hữu ích, nhưng tôi chưa chắc chắn liệu bạn có quan tâm đến nhiều kết nối hay không.

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