Docs Menu
Docs Home
/ / /
Ruby MongoDB Driver
/

트랜잭션

이 페이지의 내용

  • 트랜잭션 사용
  • 로우 레벨 API
  • 커밋 재시도
  • 트랜잭션 중첩

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_transactionstart_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 블록 내의 오류를 조용히 처리하지 않아야 합니다. 애플리케이션이 차단 내의 오류를 처리해야 하는 경우 오류를 다시 발생시켜야 합니다.

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 를 사용해야 합니다.

트랜잭션은 세션에서 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 을(를) 호출하려고 하면 오류가 발생합니다.

돌아가기

세션