Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Azure Table Storage 기반 이벤트 저장소 동시성 문제 해결 #28

Open
gyuwon opened this issue Jan 4, 2018 · 0 comments

Comments

@gyuwon
Copy link
Member

gyuwon commented Jan 4, 2018

79f45f 커밋 기준 AzureEventSourcedRepository_specs.SaveAndPublish_is_concurrency_safe 테스트 케이스는 실패한다. 영속된 후 이벤트가 발행되지 않는 경우가 발생한다. 이 문제는 메시지 분할(partitioning)이 적용된 시스템에서는 발생하지 않는다. 하지만 프레임워크가 메시지 분할을 강제하지 않기 때문에 이 문제는 해결되어야 한다.

우선 조치

CQRS Journey에서 소개하는 방식대로 영속 이벤트와 발행 대기 이벤트를 같은 파티션에 기록하고 복원 프로세스는 테이블 전체 검색(full scan)을 수행하도록 한다. 테이블 전체 검색 비용을 벤치마킹한 후 해결되어야 할 수준으로 판단되면 다음 방안을 검토한다.

테이블 구조

Type Partition Key Row Key Transaction Id Version
Persistent Event {AggregateType}-{AggregateId} {Version} {TransactionId}
Pending Event {AggregateType}-{AggregateId} Pending-{Version} {TransactionId} {Version}
Publish Queue Item {AggregateType}-Publish-{AggregateId} {Version}-{TransactionId} {TransactionId} {Version}

Persistent Events와 Pending Events가 같은 파티션에 존재하면 명령 원자성을 보장받으므로 SQL 구현체와 유사한 매커니즘을 적용할 수 있다. 이것은 CQRS Journey에서 소개하는 방식이다. 하지만 이 방식은 영속 후 발행되지 못한 이벤트를 찾기 위해 테이블 전체 검색(full scan)이 수행되므로 큰 비용이 소비된다. 이 문제를 해결하기 위해 발행 작업 대기열을 나타내는 별도의 파티션(Publish Queue Item)을 만든다. 이 때 해결해야 할 문제는 작업 대기열을 나타내는 파티션은 이벤트 파티션과 다르기 때문에 명령의 원자성이 보장되지 않아 동시성 상황에 대해 각종 불일치가 발생할 수 있다는 점이다.

기본 I/O 절차

  1. 마지막 버전 기준 Publish Queue Item 추가
  2. Persistent Events & Pending Events 추가
  3. Pending Events 순차적 삭제
  4. Publish Queue Item 삭제

복원 프로세스

  • Publish Queue Item과 동일한 버전의 Persistent Event 엔터티가 존재하지 않으면 해당 대기열 항목을 죽은 작업으로 판단한다.(이 경우 아직 진행중인지 죽은 작업인지 불명확한 상태로 분류해야 하는 것 아닌가?)
  • Publish Queue Item과 동일한 버전의 Persistent Event 엔터티의 TransactionId가 일치하지 않으면 해당 대겨얼 항목을 죽은 작업으로 판단한다.

예외 사례

AggregateId와 TransactionId에는 GUID가 사용되지만 편의상 짧게 적는다.

Case 1

AggregateType AggregateId
User 1
  1. Process 1 Transaction A 시작
  2. Process 1 10 버전 기준 Publish Queue Item 추가
  3. Process 1 CRASH!
Type Partition Key Row Key Transaction Id Version
Publish Queue Item User-Publish-1 10-A A 10

복원 프로세스는 대기열 항목은 동일 버전의 Persistent Event 엔터티가 존재하지 않기 때문에 죽은 작업임을 판단할 수 있다.

Case 2

AggregateType AggregateId
User 1
  1. Process 1 Transaction A 시작
  2. Process 1 10 버전 기준 Publish Queue Item 추가
  3. Process 2 복원 프로세스 시작
  4. Process 2 복원 프로세스가 Publish Queue Item를 죽은 작업으로 오판해 삭제
  5. Process 1 Persistent Events & Pending Events 추가
  6. Process 1 CRASH!
Type Partition Key Row Key Transaction Id Version
Persistent Event User-1 10 A
Pending Event User-1 Pending-10 A 10

복원 프로세스는 대기열 항목이 없기 때문에 대기 이벤트를 발행할 수 없다.

Case 3

AggregateType AggregateId
User 1
  1. Process 1 Transaction A 시작
  2. Process 1 10 버전 기준 Publish Queue Item 추가
  3. Process 1 CRASH!
  4. Process 1 Transaction B 시작
  5. Process 1 10 버전 기준 Publish Queue Item 추가
  6. Process 1 Persistent Events & Pending Events 추가
  7. Process 1 Pending Events 순차적 삭제
  8. Process 1 Publish Queue Item 삭제
  9. Process 1 Transaction B 종료
Type Partition Key Row Key Transaction Id Version
Persistent Event User-1 10 B
Publish Queue Item User-Publish-1 10-A A 10

복원 프로세스는 대기열 항목과 동일 버전의 Persistent Event 엔터티의 TransactionId가 일치하지 않기 때문에 해당 대기열 항목이 죽은 작업임을 판단할 수 있다.

gyuwon added a commit that referenced this issue Jan 7, 2018
Azure 테이블 저장소 기반 이벤트 저장소 구현체가 영속 이벤트와 발행 대기
이벤트를 하나의 파티션에 기록해 두 이벤으의 기록 작업이 원자성을 가지도록
변경한다.

Issue: #28
gyuwon added a commit that referenced this issue Jan 7, 2018
Azure 테이블 저장소 기반 이벤트 저장소 구현체가 영속 이벤트와 발행 대기
이벤트를 하나의 파티션에 기록해 두 이벤트의 기록 작업이 원자성을 가지도록
변경한다.

Issue: #28
gyuwon added a commit that referenced this issue Jan 7, 2018
PendingEvent 엔터티 클래스를 기반으로 동작하도록 AzureEventPublisher
클래스를 수정한다.

Issue: #28
gyuwon added a commit that referenced this issue Jan 7, 2018
기존 이벤트 저장소 파티션 구성이 가진 동시성 문제를 해결하고 일부 성능을
개선한다.

Issue: #28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant