낙관적 잠금이 작동하지 않으면 어떻게해야합니까?


11

이 시나리오는 다음과 같습니다.

  1. 사용자는 GET 요청을 /projects/1하고 ETag를 받습니다 .
  2. 사용자는 1 단계부터 ETag 로 PUT 요청을 /projects/1합니다.
  3. 사용자는 /projects/11 단계에서 ETag 로 다른 PUT 요청을 합니다.

일반적으로 ETag가 오래되었으므로 두 번째 PUT 요청은 412 응답을 수신합니다. 첫 번째 PUT 요청이 자원을 수정 했으므로 ETag가 더 이상 일치하지 않습니다.

그러나 두 PUT 요청이 동시에 전송되거나 정확히 하나씩 전송되면 어떻게됩니까? 첫 번째 PUT 요청에는 PUT # 2가 도착하기 전에 리소스를 처리하고 업데이트 할 시간이 없으므로 PUT # 2가 PUT # 1을 덮어 씁니다. 낙관적 잠금의 요점은 그것이 일어나지 않는 것입니다 ...


3
Esben이 아래에 설명하는 것처럼 비즈니스 레벨 트랜잭션에서 조작을 원자화하십시오.
Robert Harvey

트랜잭션을 사용하여 작업을 원자화하면 어떻게됩니까? PUT # 1이 완전히 처리 될 때까지 PUT # 2가 처리되지 않습니까?
Maximedupre

7
비관 주의자가 되십니까?
jpmc26

글쎄, 이것이 잠금의 목적입니다.
Fattie

물론 Put # 2는 처리되지 않아야합니다. 고유해야합니다.
Fattie

답변:


21

ETag 메커니즘은 낙관적 잠금을위한 통신 프로토콜 만 지정합니다. 낙관적 잠금을 적용하기 위해 동시 업데이트를 감지하는 메커니즘을 구현하는 것은 애플리케이션 서비스의 책임입니다.

데이터베이스를 사용하는 일반적인 응용 프로그램에서는 일반적으로 PUT 요청을 처리 할 때 트랜잭션을 열어서이 작업을 수행합니다. 일반적으로 해당 트랜잭션 내에서 데이터베이스의 기존 상태를 읽고 (읽기 잠금을 얻기 위해), Etag 유효성을 확인하고, 데이터를 덮어 씁니다 (호환되지 않는 동시 트랜잭션이있을 때 쓰기 충돌을 일으키는 방식으로). 그런 다음 커밋하십시오. 트랜잭션을 올바르게 설정하면 동일한 데이터를 동시에 업데이트하려고하기 때문에 커밋 중 하나가 실패해야합니다. 그런 다음이 트랜잭션 실패를 사용하여 응용 프로그램에 적합한 경우 412를 반환하거나 요청을 다시 시도 할 수 있습니다.


서버가 현재 동시 업데이트를 감지하는 메커니즘을 구현하는 방법은 리소스의 해시를 비교하는 것입니다. 서버는 모든 작업에 트랜잭션을 사용하지만 잠금을 얻지 못했습니다. 이로 인해 문제가 발생할 수 있습니다. 그러나 귀하의 예에서 트랜잭션이 잠금을 사용하는 경우 커밋 중 하나에서 어떻게 오류가 발생할 수 있습니까? 두 번째 트랜잭션은 상태를 읽을 때 첫 번째 트랜잭션이 해결 될 때까지 보류 중이어야합니다.
Maximedupre

1
@maximedupre : 트랜잭션을 사용하는 경우 암시 적 잠금 일 수 있지만 일종의 잠금이 있습니다 (명시 적으로 요청하지 않고 필드를 읽거나 업데이트 할 때 잠금이 자동으로 획득 됨). 위에서 설명한 메커니즘은 암시 적 잠금 만 사용하여 구현할 수 있습니다. 다른 질문으로, 그것은 사용중인 데이터베이스에 달려 있지만 많은 현대 데이터베이스는 MVCC (Multi version Concurrency Control)를 사용하여 여러 독자와 작가가 불필요하게 서로를 차단하지 않고 동일한 필드에서 작업 할 수 있습니다.
Lie Ryan

1
경고 : 많은 DBMS (PostgreSQL, Oracle, SQL Server 등)에서 기본 트랜잭션 격리 수준은 "읽기 커밋"입니다. 여기서 접근 방식은 OP의 경쟁 조건을 방지하기에 충분 하지 않습니다 . 이러한 DMBSes, 당신은을 포함하여 문제를 해결할 수 있습니다 AND ETag = ...당신에 UPDATE문의 WHERE절과 이후 업데이트 된 행 카운트를 검사합니다. (또는 더 엄격한 트랜잭션 격리 수준을 사용하지만 실제로 권장하지는 않습니다.)
ruakh

1
@ruakh : 쿼리 작성 방법에 따라 다릅니다. 그렇습니다. 기본 격리 수준은 모든 쿼리에 대해 자동으로 이러한 동작을 제공하지 않지만 낙관적 잠금을 구현하기에 충분한 방식으로 트랜잭션을 구성하는 것이 종종 가능합니다. 대부분의 경우 응용 프로그램에서 트랜잭션 일관성이 중요한 경우 어쨌든 기본 격리 수준으로 반복 가능한 읽기를 권장합니다. MVCC를 사용하는 데이터베이스에서 반복 가능한 읽기의 오버 헤드는 매우 적으며 응용 프로그램을 크게 단순화합니다.
Lie Ryan

1
@ruakh : 반복 가능한 읽기의 주요 단점은 동시 트랜잭션이있는 경우 다시 시도하거나 실패 할 수 있도록 준비해야한다는 것입니다. 이것은 일반적으로 문제이지만 동시성 전략으로 낙관적 잠금을 제공하는 응용 프로그램은 이미이 처리가 필요하므로 반복 가능한 읽기 오류는 자연스럽게 낙관적 잠금 실패에 매핑되므로 실제로 새로운 단점을 추가하지는 않습니다.
거짓말 라이언

13

다음 쌍을 원자 적으로 실행해야합니다.

  • 태그의 유효성 검사 (즉, 최신)
  • 리소스 업데이트 (태그 업데이트 포함)

다른 사람들은 이것을 트랜잭션이라고 부릅니다. 그러나 기본적으로이 두 작업의 원자 실행은 타이밍 실수로 다른 하나를 덮어 쓰는 것을 방지합니다. 이것이 없으면 경쟁 조건이 있습니다.

큰 그림을 보더라도 여전히 낙관적 잠금으로 간주됩니다. 업데이트 여부에 관계없이 모든 사용자 또는 데이터를보고있는 사용자가 리소스 자체를 초기 읽기 (GET)로 잠그지 않습니다.

약간의 원자 적 동작이 필요하지만 여러 네트워크 상호 작용에 대한 잠금을 유지하려고 시도하지 않고 단일 요청 (PUT) 내에서 발생합니다. 이것은 낙관적 잠금입니다. 객체는 GET에 의해 잠기지 않았지만 여전히 PUT에 의해 안전하게 업데이트 될 수 있습니다.

이 두 가지 작업을 원자 적으로 실행하는 방법은 여러 가지가 있습니다. 리소스를 잠그는 것이 유일한 옵션은 아닙니다. 예를 들어, 가벼운 스레드 또는 객체 잠금으로 충분할 수 있으며 응용 프로그램의 아키텍처 및 실행 컨텍스트에 따라 다릅니다.


4
원자가 중요하다는 사실을 지적하여 +1. 업데이트되는 기본 리소스에 따라 트랜잭션이나 잠금없이 수행 할 수 있습니다. 예를 들어, 인 메모리 리소스의 원자 비교 및 ​​스왑 또는 지속 데이터의 이벤트 소싱.
Aaron M. Eshbach

@ AaronM.Eshbach는 이에 대해 동의했으며 전화 해 주셔서 감사합니다.
Erik Eidt

1

실제로 E-Tag를 확인하고 해당 논리를 제공하는 것은 응용 프로그램 개발자에게 있습니다. E-Tag정적 콘텐츠의 헤더 를 계산하는 방법 만 알고 있기 때문에 웹 서버가 당신을 위해하는 것은 마술이 아닙니다 . 위의 시나리오를 살펴보고 상호 작용이 발생하는 방식을 세분화하십시오.

GET /projects/1

서버는 요청을 수신하고이 버전의 레코드에 대한 E- 태그를 판별하여 실제 컨텐츠와 함께 리턴합니다.

200 - OK
E-Tag: "412"
Content-Type: application/json
{modified: false}

클라이언트는 이제 E-Tag 값을 가지므로 PUT요청에 이를 포함 할 수 있습니다 .

PUT /projects/1
If-Match: "412"
Content-Type: application/json
{modified: true}

이 시점에서 응용 프로그램은 다음을 수행해야합니다.

  • E-Tag가 여전히 올바른지 확인하십시오. "412"== "412"?
  • 그렇다면 업데이트하고 새 E- 태그를 계산하십시오.

성공 응답을 보냅니다.

204 No Content
E-Tag: "543"

다른 요청이 와서 PUT위의 요청과 유사한 작업을 수행하려고하면 서버 코드에서 두 번째로 요청을 평가하면 오류 메시지를 제공해야합니다.

  • E-Tag가 여전히 올바른지 확인하십시오. "412"! = "543"

실패하면 실패 응답을 보내십시오.

412 Precondition Failed

이것은 실제로 작성해야하는 코드입니다. E- 태그는 실제로 모든 텍스트 (HTTP 사양에 정의 된 제한 내) 일 수 있습니다. 숫자 일 필요는 없습니다. 해시 값일 수도 있습니다.


이것은 여기서 사용하는 표준 HTTP 표기법이 아닙니다. 표준 호환 HTTP에서는 응답 헤더에서만 ETag를 사용합니다. 요청 헤더에 ETag를 보내지 않고 요청 헤더의 If-Match 또는 If-None-Match 헤더에서 이전에 획득 한 ETag 값을 사용하십시오.
Lie Ryan

-2

다른 답변을 보완하기 위해 기본 문제를 충실하게 설명하는 ZeroMQ 설명서 에 최고의 인용문 중 하나를 게시합니다 .

완전히 완벽한 MT 프로그램을 만들기 위해 (그리고 말 그대로), 뮤텍스, 잠금 또는 ZeroMQ 소켓을 통해 전송되는 메시지를 제외한 다른 형태의 스레드 간 통신이 필요하지 않습니다.

"완벽한 MT 프로그램"이란 말은 작성하기 쉽고 이해하기 쉽고 모든 프로그래밍 언어와 운영 체제에서 동일한 설계 방식으로 작동하며 대기 상태가없고 포인트가없는 여러 CPU에 걸쳐 확장되는 코드를 의미합니다. 반품 감소.

잠금 및 세마포어 및 중요한 섹션을 사용하여 MT 코드가 전혀 작동하지 않도록 트릭을 배우는 데 몇 년을 소비했다면, 그것이 아무것도 아니라는 사실을 깨닫게되면 역겨워 할 것입니다. 30 년 이상의 동시 프로그래밍에서 배운 교훈이 있다면 상태를 공유하지 마십시오. 맥주를 마시려고하는 두 명의 술꾼과 같습니다. 그들이 좋은 친구인지는 중요하지 않습니다. 조만간 그들은 싸울 것입니다. 그리고 술에 취한 사람이 많을수록 맥주 위에서 서로 더 많이 싸 웁니다. MT 응용 프로그램의 비극적 인 대다수는 술 취한 바 싸움처럼 보입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.