경쟁 조건 처리: 실제 예

PHPz
풀어 주다: 2024-07-18 14:47:22
원래의
960명이 탐색했습니다.

당신의 경력에서 슈뢰딩거의 고양이 문제, 때로는 효과가 있고 때로는 효과가 없는 상황에 직면하게 될 것입니다. 경쟁 조건은 이러한 과제 중 하나입니다(예, 딱 하나입니다!).

이 블로그 게시물 전반에 걸쳐 실제 사례를 제시하고, 문제를 재현하는 방법을 시연하고, PostgreSQL의 직렬화 가능 트랜잭션 격리 및 권고 잠금을 사용하여 경합 조건을 처리하기 위한 전략을 논의하겠습니다.

"데이터 집약적 애플리케이션 설계", 7장 - 트랜잭션 "약한 격리 수준"에서 영감을 얻음

실제 예제가 포함된 Github 저장소

응용 프로그램

병원 의사의 당직 근무를 관리하는 애플리케이션입니다. 경쟁 조건 문제에 초점을 맞추기 위해 시나리오를 단순화해 보겠습니다. 우리 앱은 다음 단일 테이블을 중심으로 해결됩니다.

으아아아

우리에게는 중요한 비즈니스 규칙이 있습니다:

  • 각 교대 근무에는 항상최소 한 명의담당 의사가 있어야 합니다.

짐작하셨겠지만 순진한 API를 구현하면 경쟁 조건 시나리오가 발생할 수 있습니다. 다음과 같은 가상의 상황을 생각해 보세요.

Jack과 John은 같은 근무 시간 동안 병원에 당직 중입니다. 거의 동시에 그들은 휴가를 요청하기로 결정했습니다. 하나는 성공했지만 다른 하나는 교대근무 중인 의사 수에 대한 오래된 정보에 의존합니다. 그 결과 두 사람 모두 교대를 그만두고 비즈니스 규칙을 어기고 의사가 없는 특정 교대를 떠나게 되었습니다.

으아아아

문제 재현

이 애플리케이션은 Golang에서 구현된 간단한 API입니다. 이 경쟁 조건 시나리오를 재현하기 위해 스크립트를 실행하고 실행하는 방법에 대한 지침은 GitHub 저장소를 확인하세요. 요약하면 다음이 필요합니다.

  1. 서버 시작: Yarn nx는 병원 교대 근무를 제공합니다.
  2. k6 테스트를 실행하여 경쟁 조건 시나리오 재현: Yarn nx 테스트 병원 교대

이 테스트는 두 명의 의사를 동시에 호출하여 서로 다른 접근 방식으로 엔드포인트에 도달하려고 시도합니다.shiftId=1advisory lock을 사용하고,shiftId=2직렬화 가능한 트랜잭션 격리를 사용하며,shiftId=3순진합니다. 동시성 제어 없이 구현.

k6 결과는 비즈니스 규칙을 위반한 ShiftId를 나타내는 사용자 정의 측정항목을 출력합니다.

으아아아

Yarn, Go, K6, Docker와 같은 도구가 필요하거나 DevBox를 사용하여 저장소 종속성을 더 쉽게 설정할 수 있습니다.

경쟁 상황 해결

우리 애플리케이션이 오래된 데이터를 기반으로 결정을 내릴 때 문제가 발생합니다. 두 거래가 거의 동시에 실행되고 둘 다 의사의 교대 근무를 취소하려고 시도하는 경우 이런 일이 발생할 수 있습니다. 한 트랜잭션은 예상대로 성공하지만 오래된 정보에 의존하는 다른 트랜잭션도 잘못 성공합니다. 이러한 바람직하지 않은 동작을 어떻게 방지할 수 있습니까? 이를 달성하는 방법에는 몇 가지가 있습니다. PostgreSQL이 지원하는 두 가지 옵션을 살펴보겠습니다. 하지만 다른 데이터베이스 관리 시스템에서도 유사한 솔루션을 찾을 수 있습니다.

직렬화 가능한 트랜잭션 격리

직렬화 가능한 스냅샷 격리는 애플리케이션에서 나타나는 쓰기 왜곡과 같은 이상 현상을 자동으로 감지하고 방지합니다.

트랜잭션 격리에 대한 이론을 자세히 다루지는 않겠지만 이는 널리 사용되는 많은 데이터베이스 관리 시스템에서 공통적으로 사용되는 주제입니다. 트랜잭션 격리에 관한 PostgreSQL 공식 문서에서 이와 같은 스냅샷 격리를 검색하면 좋은 자료를 찾을 수 있습니다. 또한 몇 년 전에 이 솔루션을 제안한 논문이 있습니다. Talk는 저렴하므로 코드를 살펴보겠습니다:

먼저 트랜잭션을 시작하고 격리 수준을 직렬화 가능으로 설정합니다.

으아아아

그런 다음 작업 실행을 진행합니다. 우리의 경우에는 다음 기능을 실행합니다:

으아아아

동시 실행으로 인해 일관되지 않은 시나리오가 발생할 때마다 직렬화 가능 격리 수준을 통해 하나의 트랜잭션이 성공하도록 허용하고 이 메시지와 함께 다른 트랜잭션을 자동으로 롤백하므로 안전하게 재시도할 수 있습니다.

으아아아
  • updateWithSerializedIsolation 함수에서 전체 예제를 찾을 수 있습니다.

권고 잠금

비즈니스 규칙을 시행하는 또 다른 방법은 특정 근무조에 대해 리소스를 명시적으로 잠그는 것입니다.트랜잭션 수준에서 Advisory Lock을 사용하여 이를 달성할 수 있습니다. 이 유형의 잠금은 애플리케이션에 의해 완전히 제어됩니다. 자세한 내용은 여기에서 확인하세요.

잠금은 세션 수준과 트랜잭션 수준 모두에 적용될 수 있다는 점에 유의하는 것이 중요합니다. 여기에서 사용 가능한 다양한 기능을 탐색할 수 있습니다. 우리의 경우 pg_try_advisory_xact_lock(key bigint) → boolean을 사용하여 커밋 또는 롤백 후에 자동으로 잠금을 해제합니다.

으아아아

우리 애플리케이션에 사용된 전체 기능은 다음과 같습니다.

-- Function to Manage On Call Status with Advisory Locks, automatic release when the trx commits CREATE OR REPLACE FUNCTION update_on_call_status_with_advisory_lock(shift_id_to_update INT, doctor_name_to_update TEXT, on_call_to_update BOOLEAN) RETURNS VOID AS $$ DECLARE on_call_count INT; BEGIN -- Attempt to acquire advisory lock and handle failure with NOTICE IF NOT pg_try_advisory_xact_lock(shift_id_to_update) THEN RAISE EXCEPTION '[AdvisoryLock] Could not acquire advisory lock for shift_id: %', shift_id_to_update; END IF; -- Check the current number of doctors on call for this shift SELECT COUNT(*) INTO on_call_count FROM shifts s WHERE s.shift_id = shift_id_to_update AND s.on_call = TRUE; IF on_call_to_update = FALSE AND on_call_count = 1 THEN RAISE EXCEPTION '[AdvisoryLock] Cannot set on_call to FALSE. At least one doctor must be on call for this shiftId: %', shift_id_to_update; ELSE UPDATE shifts s SET on_call = on_call_to_update WHERE s.shift_id = shift_id_to_update AND s.doctor_name = doctor_name_to_update; END IF; END; $$ LANGUAGE plpgsql;
로그인 후 복사
  • You can find the complete example in the function updateWithAdvisoryLock.

Conclusion

Dealing with race conditions, like thewrite skewscenario we talked about, can be pretty tricky. There's a ton of research and different ways to solve these problems, so definitely check out some papers and articles if you're curious.

These issues can pop up in real-life situations, like when multiple people try to book the same seat at an event or buy the same spot in a theater. They tend to appear randomly and can be hard to figure out, especially if it's your first time dealing with them.

When you run into race conditions, it's important to look into what solution works best for your specific situation. I might do a benchmark in the future to compare different approaches and give you more insights.

I hope this post has been helpful. Remember, there are tools out there to help with these problems, and you're not alone in facing them!


Dealing with Race Conditions: A Practical Exampleiamseki / dev-to

Implementations of dev.to blog posts

위 내용은 경쟁 조건 처리: 실제 예의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!