트랜잭션
MongoDB 서버 버전 4.0 에는 다중 문서 트랜잭션이 도입되었습니다. (단일 문서 내의 여러 필드에 대한 업데이트는 모든 버전의 MongoDB 에서 원자적으로 이루어집니다.) Ruby 운전자 버전 2.6.0 트랜잭션에 대한 지원 을 추가합니다.
트랜잭션 사용
트랜잭션 을 시작하려면 애플리케이션 에 세션이 있어야 합니다.
트랜잭션을 사용하는 데 권장되는 방법은 with_transaction
헬퍼 메서드를 사용하는 것입니다.
session = client.start_session session.with_transaction do collection.insert_one({hello: 'world'}, session: session) end
with_transaction
헬퍼는 다음을 수행합니다.
제공된 블록을 호출하기 전에 트랜잭션을 시작하고, 블록이 완료되면 트랜잭션을 커밋합니다.
차단에 포함된 작업이나 커밋 작업으로 인해 일시적인 트랜잭션 오류가 발생하면 차단 및/또는 커밋이 다시 실행됩니다.
블록은 여러 번 호출될 수 있으므로 멱등성이어야 합니다.
차단은 commit_transaction
또는 abort_transaction
을(를) 호출하여 트랜잭션을 명시적으로 커밋하거나 중단할 수 있습니다. 이 경우 with_transaction
은 커밋이나 중단을 시도하지 않습니다(그러나 차단 밖으로 전파된 일시적인 트랜잭션 오류가 발생하면 차단을 다시 시도할 수 있습니다).
트랜잭션의 커밋 결과를 알 수 없는 경우에도 차단이 다시 시도됩니다. 예를 들어, 커밋 중에 cluster가 투표를 거치는 경우 이런 일이 발생할 수 있습니다. 이 경우 차단을 다시 시도하면 토폴로지의 주 서버가 변경되었을 수 있습니다.
현재 with_transaction
는 실행 시작 후 120초가 지나면 차단 재시도를 중지하고 커밋합니다. 이 시간은 구성할 수 없으며 향후 드라이버 버전에서 변경될 수 있습니다. 그렇다고 해서 with_transactions
의 전체 런타임이 120초 이하로 보장되는 것은 아니며, 벽시계 시간이 120초가 지나면 더 이상의 재시도가 시작되지 않습니다.
트랜잭션에 대한 더 많은 제어가 필요한 경우 낮은 수준의 API도 사용할 수 있습니다.
with_transaction
는 start_transaction
와 동일한 읽기 고려 (read concern), 쓰기 고려 (write concern), 읽기 설정 (read preference) 옵션을 사용합니다.
session = client.start_session session.with_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary} ) do collection.insert_one({hello: 'world'}, session: session) end
블록 내의 오류 처리 with_transaction
with_transaction
블록 내부의 명령이 실패하면 서버의 트랜잭션이 중단될 수 있습니다. 이 상황은 일반적으로 드라이버에 의해 투명하게 처리됩니다. 그러나 애플리케이션이 이러한 오류를 포착하고 다시 발생시키지 않으면 드라이버는 트랜잭션이 중단되었는지 여부를 확인할 수 없습니다. 그러면 드라이버는 차단을 무기한 다시 시도합니다.
이러한 상황을 방지하려면 애플리케이션이 with_transaction
블록 내의 오류를 조용히 처리하지 않아야 합니다. 애플리케이션이 차단 내의 오류를 처리해야 하는 경우 오류를 다시 발생시켜야 합니다.
session.with_transaction do collection.insert_one({hello: 'world'}, session: session) rescue Mongo::Error::OperationFailure => e # Do something in response to the error raise e end
애플리케이션이 사용자 지정 방식으로 오류를 처리하다 해야 하는 경우 대신 낮은 수준의 API 를 사용해야 합니다.
로우 레벨 API
트랜잭션은 세션에서 start_transaction
메서드를 호출하여 시작할 수 있습니다.
session = client.start_session session.start_transaction
트랜잭션을 시작할 때 읽기 고려 (read concern), 쓰기 고려 (write concern), 읽기 설정 (read preference)을 지정할 수도 있습니다.
session = client.start_session session.start_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary})
트랜잭션에서 데이터베이스에 대한 변경 사항을 유지하려면 트랜잭션을 명시적으로 커밋해야 합니다. 세션이 열린 트랜잭션으로 종료 되면 트랜잭션이 중단 됩니다. 트랜잭션을 명시적으로 중단할 수도 있습니다.
트랜잭션을 커밋하거나 중단하려면 세션 인스턴스에서 commit_transaction
또는 abort_transaction
를 호출합니다.
session.commit_transaction session.abort_transaction
참고: 미해결 트랜잭션은 데이터베이스와 같은 서버의 다양한 객체에 대한 잠금을 보유할 수 있습니다. 예를 들어 다음 스니펫의 삭제 호출은 서버가 만료되고 트랜잭션이 중단될 때까지 transactionLifetimeLimitSeconds 초(기본값 60) 동안 중단됩니다.
c1 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db) session = c1.start_session c1['foo'].insert_one(test: 1) session.start_transaction c1['foo'].insert_one({test: 2}, session: session) c2 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db) # hangs c2.database.drop
트랜잭션은 서버 측 세션과 연결되므로 클라이언트를 닫아도 이 클라이언트가 시작한 트랜잭션이 중단되지는 않습니다. 애플리케이션은 abort_transaction
을(를) 호출하거나 서버 측에서 트랜잭션이 시간 초과될 때까지 기다려야 합니다. 트랜잭션을 커밋하거나 중단하는 것 외에도 애플리케이션은 이 세션에서 트랜잭션이 진행 중인 경우 중단할 세션을 종료할 수도 있습니다.
session.end_session c2 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db) # ok c2.database.drop
오류 처리
트랜잭션 내부의 명령이 실패하면 서버에서 트랜잭션이 중단될 수 있습니다. 트랜잭션을 중단하는 오류는 오류 레이블에 TransientTransactionError
이(가) 없습니다. 이러한 트랜잭션을 커밋하려는 시도는 NoSuchTransaction
오류와 함께 거부됩니다.
커밋 재시도
트랜잭션 커밋이 실패하면 다시 시도할 수 있습니다 . 이를 위한 Ruby 코드는 다음과 같습니다.
begin session.commit_transaction rescue Mongo::Error => e if e.label?('UnknownTransactionCommitResult') retry else raise end end
트랜잭션 중첩
MongoDB는 트랜잭션 중첩을 지원하지 않습니다. 트랜잭션이 이미 진행 중인 상태에서 start_transaction
또는 with_transaction
을(를) 호출하려고 하면 오류가 발생합니다.