마이크로 서비스 아키텍처(MSA) 프로젝트를 개발 및 운영을 하다 보면 도메인 모델은 복잡해지고 점점 설계 시점의 의도와는 다른 방향으로 변질되는 일이 빈번히 발생한다. 특히 요즘처럼 고차원적인 UX, 급변하는 IT 시장의 흐름으로 인해 시도 때도 없이 달라지는 기획팀/사업부의 요구사항을 충족하는 모델을 만드는 건 더욱 어려운 일이 되었다. 게다가 이렇게 복잡한 내용을 하나의 화면에서 다 보여달라고 하니.. 아무리 인덱스를 추가하고 쿼리를 튜닝하더라도 조회 속도가 나지 않고, n번의 api를 결과를 합치면서 생기는 실수, 더러운 소스코드 등은 결국 서비스의 질을 낮추기에 충분해진다.
이게 왜 어려워졌을까? 데이터의 변경과 조회 시 필요한 데이터가 관점에 따라 명백히 다른데, 이걸 하나의 모델/애플리케이션/디비에서 해결하려다 보니 (각 영역에서 필요하지 않은 속성들로 인해) 복잡도가 증가하고 변질되는 것은 아닐까?
그럼 어떻게 이 문제를 해결 할 수 있을까? 데이터의 변경과 조회를 나누면 되지 않을까? 해서 나온 게 CQRS이다.
CQRS란?
마이크로 서비스의 패턴이 무려 44가지나 있다고 하는데, 간단하게 관련 용어 몇 가지만 살펴본다.
DDD - Domain Driven Design 도메인 주도 개발(방법론)
비즈니스를 도메인 별로 나누어 설계하는 방식/접근법
EDA - Event Driven Architecture
분산된 시스템 간에 이벤트를 생성, 발행(publishing)하고 발행된 이벤트를 필요로 하는 수신자(subscriber)에게 전송되는 흐름으로 구성된 아키텍처로 이벤트를 수신한 수신자가 이벤트를 처리하는 형태임
Event-Sourced Aggregate: EventStore로부터 Event를 재생하면서 모델을 최신 상태로
State-Stored Aggregate: EventStore에 Event를 적재와 별개로 모델 자체에 최신 상태를 DB에 저장
Query Application
Point to Point Query: 하나의 QueryHandler를 찾아 진행
Scatter-Gather Query: 동일한 Query를 처리하는 Handler가 여러 App에 등록되어있을 때, 이를 처리하는 방법
Subscription Query: Point to Point Query를 요청하였을 때, 만약 Query를 수행하는 Read Model이 바뀌었다면, 화면에 출력되는 결과와 Read Model 사이 데이터 정합성 불일치 문제가 발생한다. 따라서 이를 해결하기 위해 주기적으로 Query를 재요청하는 방식
추가) Saga pattern in axon
axon framework에서도 분산 트랜젝션을 위해 saga 패턴을 지원한다(saga event 정보를 db에 저장).
Saga 트랜잭션에서 실패가 났을 경우 recovery 하는 방법에 대한 글이다. 딱히 신기술이나 신개념이 나오는 것은 아니고 exception을 try/catch 문으로 잡아서 catch문에서 새로운 이벤트를 전파시켜 롤백하는 과정이다. 즉, 로직적으로 해결하는 것이라서 흐름만 잘 따라가면 된다.
해당 글에서는 두 가지 케이스에 대해 다룬다.
타임아웃으로 인한 잔액 복구 케이스
타임아웃이 났는데 어차피 잔액이 모자라서 진행이 안 되는 케이스(하지만 진행을 했다가 취소하는 방식으로 진행한다; 이벤트는 삭제할 수 없기 때문에 취소 이벤트를 발생해줘야 한다)
각 흐름에 대해 정리한다. 소스를 보면서 따라가야 하며 로그를 같이 보면 훨씬 빠르게 접근할 수 있다.
중간에 요청 없어짐 표시는 무시해도 된다. 처음에 테스트할 때 잔액 차감 성공 이벤트가 로그에 찍혔는데, 잔액을 복구하는 로직이 없어서 이상하다 싶었는데 다시 확인해보니, 잔액 차감 성공(TransferApprovedCommand) 시 로직이 덜 짜져 있어서(if (!isExecutingCompensation && !isAbortingCompensation)이 부분이 없었다.) 잘못된 코드였고, 수정해서 안 나오는 게 맞다는 것을 확인했다. 즉, 잔액 차감 성공(TransferApprovedCommand) 을 받았지만 취소 중이면 차감을 아애 안 하는 로직인 것임.
괄호의 숫자는 로그의 시간이다. 시간순으로 나열한 것은 아니므로 주의해서 확인해야한다..
특이점은 jeju에서는 잔액이 감소(이체 성공)하고 다시 복구(이체 취소)하는 과정을 거치지만, command 쪽은 아예 잔액 변경이 없다.
--------------------command
commandGateway.sendAndWait(MoneyTransferCommand)
@CommandHandler transferMoney(MoneyTransferCommand)
AggregateLifecycle.apply(MoneyTransferEvent)
///사가 시작
@StartSaga @SagaEventHandler(associationProperty = "transferID") on(MoneyTransferEvent)
commandGateway.sendAndWait(JejuBankTransferCommand) (17:01:23.740)
10초 기다리다 익셉션 (17:01:33.744)
--------------------jeju
@CommandHandler on(JejuBankTransferCommand)
15초 홀딩하던도중 취소 요청을 받긴하지만 홀딩 후 성공처리 먼저함
AggregateLifecycle.apply(TransferApprovedEvent) (17:01:38.790)
@EventSourcingHandler on(TransferApprovedEvent)
잔액 차감 (17:01:38.791)
--------------------command
//요청 없어짐
//{익셉션 (17:01:33.744)시 isExecutingCompensation = true로 바뀌었고 그 후에 들어온 이체 성공(잔액 차감) 요청이므로 진행 안 됨}
//@SagaEventHandler(associationProperty = "srcAccountID") on(TransferApprovedEvent)
// 제주 잔액 차감 성공 이벤트 리슨 후 반영
// commandGateway.send(TransferApprovedCommand)
//@CommandHandler transferMoney(TransferApprovedCommand)
// 잔액 증가
// AggregateLifecycle.apply(DepositMoneyEvent) //for query app
// AggregateLifecycle.apply(DepositCompletedEvent)// 사가 종료;; 근데 왜 로그 없지?
//요청 없어짐
cancelTransfer(17:01:33.744)
취소 요청
commandGateway.send(JejuBankCancelTransferCommand)
--------------------jeju
@CommandHandler on(JejuBankCancelTransferCommand)
취소 요청
AggregateLifecycle.apply(CompletedCancelTransferEvent) (17:01:38.818)
@EventSourcingHandler on(CompletedCancelTransferEvent) (17:01:38.819)
잔액 복구 (17:01:38.819)
--------------------command
@SagaEventHandler(associationProperty = "srcAccountID") on(CompletedCancelTransferEvent)
계좌이체 취소완료 (17:01:38.854)
///사가 종료
타임아웃이 났는데 어차피 잔액이 모자라서 진행이 안 되는 케이스
여기서 특이점은 jeju에서는 잔액이 증가(복구)하고 다시 취소(회수)하는 과정을 거치지만, command쪽은 아애 잔액 변경이 없다. 타임아웃으로 인한 복구가 먼저 진행되고, 잔액이 없어서 이체를 취소하는 요청 시 복구 중인지 확인한 후 복구 중이면 다시 복구를 취소하는 것이다(복구 중이 아니면 그대로 종료).
jeju모듈을 state stored aggregate 방식으로 전환 시 추가로 설정해야 하는 부분이 있다.
1. postgres에 jeju 계정을 생성하고 권한을 부여한다.
# su - postgres
$ psql
postgres=# create user jeju with password 'jeju';
postgres=# create database jeju owner jeju;
postgres=# ALTER ROLE jeju WITH createdb;
postgres=# GRANT ALL PRIVILEGES ON DATABASE jeju TO jeju;
postgres=# \q
2. 혹시 진행 시 아래와 같은 에러를 만나면..
connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: 치명적오류: 사용자 "jeju"의 peer 인증을 실패했습니다.
아래와 같이 설정을 수정하자.
vi /var/lib/pgsql/14/data/pg_hba.conf vi /var/lib/pgsql/14/data/pg_hba.conf
//아래와 같이 수정(제주 추가)
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all command, query, jeju md5
...
설정 후 디비 재시작 해야한다.
systemctl restart postgresql-14
command 애플리케이션에 @Saga 어노테이션이 활성화되니까 아래와 같이 axon server에 계속 쿼리를 날린다.
Hibernate: update token_entry set timestamp=? where processor_name=? and segment=? and owner=?
나는 command의 잔액이 증가하면 command 테이블의 account 테이블에 증액이 될 것 같았는데 그렇지 않았다.
1번은 잘못된 코드가 들어 있었다. 예전에 실습에 event-sourced-aggregate방식 -> state-stored-aggregate방식으로 전환하는 부분이 있었는데 이걸 다시 event-sourced-aggregate방식으로 바꿔놓고 이 실습을 진행하는 것이었는데(정확히 말하면 그 부분은 번외라 실습 제외 코드였다), 나는 그걸 모르고 state-stored-aggregate방식에서 진행하다 보니 일부 로직이 빠져있었다. 어쩐지 그 후 강의에서도 몇 번 코드가 이상하다고 느꼈다. 우선 실습을 위해 다시 event-sourced-aggregate방식으로 롤백하여 진행했다.
2번도 사실 1번의 연장인데, 나는 최종 상태가 DB에 있을 것이라 생각했으나 event-sourced-aggregate방식이라 DB에 마지막 값을 저장하지 않고 file에 이벤트만 쌓아두는 방식이었기에 당연한 것이었다. query 쪽에서는 그 이벤트를 받아다가 mv_account에 저장하고 있었으니, 원본 마지막에 command를 확인하는 게 아니라 query.mv_account 테이블을 직접 조회해서 확인하는 것이었다..!
로그에서도 알 수 있지만, Command 모듈에서 MoneyTransferEvent Event가 발행되면 saga와 관련한 정보를 디비에 넣고(saga instance 생성) 수정하고 계속 불러오면서 확인하는 것을 알 수 있다. 또한 모든 작업이 끝나면 해당 데이터를 마지막에 삭제하는 것으로 마무리한다.
그래서!! 앞으로는 그 방식을 state-stored-aggregate방식으로 전환하는 작업을 해보도록 한다.
1. axonConfig.java 의 모든 빈을 주석처리(event-sourced-aggregate의 snapshot 관련 설정이기에)
2. accountAggregate.java를 aggregate/entity 방식으로 다시 바꾸고 아래 함수 작성
총 세 가지 방법이 있는데 두 가지는 지난 시간에 코딩하였고 오늘은 마지막 방법인 Scatter-Gather Query를 구현한다.
위 블로그를 따라 구현하다보면 마지막 서버 실행 부분에 아래와 같이 circular reference 관련 에러가 난다.
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'org.axonframework.config.Configurer': Requested bean is currently in creation: Is there an unresolvable circular reference?
예전 글에서도 본 적 있는 에런데 아래와 같은 설정을 추가하면 된다. springboot2.6부터 circular reference 가 기본 설정에서 제외되었기 때문이다.
spring:
main:
allow-circular-references: true
앱을 모두 실행하고 테스트 해봤을 때 로그를 확인해본다.
1. 쿼리 서비스 흐름
HolderAccountController.getAccountInfoScatterGather -> QueryServiceImpl.getAccountInfoScatterGather -> holderID / 잔액을 dto로 만들어서 제주/서울에게 넘긴 후 결과를 LoanLimitResult.class로 받아오라고 명령 -> 서울/제주의 QueryHandler 가 받아서 처리 -> Query Service에서는 두 곳에서의 응답이 다 오기를 기다리고 결과를 조합하여 리스트로 내려줌
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
///////서울 제주로 요청 보냄
//////응답1 서울
15:57:34.506 DEBUG 8092 --- [ault-executor-1] o.a.a.c.query.AxonServerQueryBus : Received query response [message_identifier: "204f7d62-8f12-4df7-881d-339387ab3fa2"
payload {
type: "com.cqrs.loan.LoanLimitResult"
data: "<com.cqrs.loan.LoanLimitResult><holderID>e5775054-4265-46ef-8116-297ac22f480d</holderID><bankName>SeoulBank</bankName><balance>7980</balance><loanLimit>11970</loanLimit></com.cqrs.loan.LoanLimitResult>"
}
meta_data {
key: "traceId"
value {
text_value: "b540735f-c8a3-48da-96a7-9e90aa752966"
}
}
meta_data {
key: "correlationId"
value {
text_value: "b540735f-c8a3-48da-96a7-9e90aa752966"
}
}
request_identifier: "b540735f-c8a3-48da-96a7-9e90aa752966"
]
//////응답2 제주
15:57:34.507 DEBUG 8092 --- [ault-executor-1] o.a.a.c.query.AxonServerQueryBus : Received query response [message_identifier: "16efaa5a-e221-4740-85b2-ff5478d8ed4e"
payload {
type: "com.cqrs.loan.LoanLimitResult"
data: "<com.cqrs.loan.LoanLimitResult><holderID>e5775054-4265-46ef-8116-297ac22f480d</holderID><bankName>JejuBank</bankName><balance>7980</balance><loanLimit>9576</loanLimit></com.cqrs.loan.LoanLimitResult>"
}
meta_data {
key: "traceId"
value {
text_value: "b540735f-c8a3-48da-96a7-9e90aa752966"
}
}
meta_data {
key: "correlationId"
value {
text_value: "b540735f-c8a3-48da-96a7-9e90aa752966"
}
}
request_identifier: "b540735f-c8a3-48da-96a7-9e90aa752966"
]
api를 실행했을 때의 흐름을 살펴보면 HolderAccountController.getAccountInfo -> QueryServiceImpl.getAccountInfo -> queryGateway -> queryHandler로 가는 것을 알 수 있다. 여기를 어떻게 찾아가는지 확인하기 위해 아래와 같이 같은 request/response를 가지는 핸들러를 하나 더 만들어서 테스트를 해봤는데..
////on3 작동
@QueryHandler
public HolderAccountSummary on3(AccountQuery query){
log.debug(">>> handling fake {}", query);
HolderAccountSummary res = new HolderAccountSummary();
res.setName("test");
return res;
}
@QueryHandler
public HolderAccountSummary one(AccountQuery query){
log.debug(">>> handling queryHandler {}", query);
return repository.findByHolderId(query.getHolderId()).orElse(null);
}
-----------------------------------
////on2 작동
@QueryHandler
public HolderAccountSummary on3(AccountQuery query){
log.debug(">>> handling fake {}", query);
HolderAccountSummary res = new HolderAccountSummary();
res.setName("test");
return res;
}
@QueryHandler
public HolderAccountSummary on2(AccountQuery query){
log.debug(">>> handling queryHandler {}", query);
return repository.findByHolderId(query.getHolderId()).orElse(null);
}
여러 번 이름을 바꿔서 실행해봤는데, 어째 이름의 alphabetical order.. 가 낮은 순(a-> b-> c..)으로 작동되는 것 같다.
찾아보니 axon에서도 query handler 의 순서에 대해 명시해놓긴 했으나 명확한 기준이라고 하긴 애매하다.
1. On the actual instance level of the class hierarchy (as returned by this.getClass()), all annotated methods are evaluated 2.If one or more methods are found of which all parameters can be resolved to a value, the method with the most specific type is chosen and invoked 3.If no methods are found on this level of the class hierarchy, the super type is evaluated the same way 4.When the top level of the hierarchy is reached, and no suitable query handler is found, this query handling instance is ignored.
내가 이해한 바로는 1. request/response 타입에 맞는 핸들러인지를 먼저 확인하고, 2. 해당 핸들러가 복수개이면 더 하위 레벨/자세한(상속을 받았다거나) 쪽을 따르는 듯하다. 내가 짠 위 코드는 같은 형태의 핸들러가 복수개이지만 뎁스가 같아서 다른 기준으로 순서를 정했을 터인데... 알파벳순이 왠지 맞는 것 같다..
2. Subscription Query
api를 실행했을 때의 흐름을 살펴보면 HolderAccountController.getAccountInfoSubscription -> QueryServiceImpl.getAccountInfoSubscription -> flux를 이용하여 subscribe 하고 있다는 것을 알 수 있다.
event handler 중 바로 노티 받을 곳에서 emit을 하면 subscribe에서 받는 구조.
화면은 SSE(Server Sent Event) 방식으로 구현되어 있으며 EventSource 객체를 사용하였다.
위 로직으로 인해 화면에 현 상태가 화면에 먼저 뿌려지며, 그 후에는 버튼을 누르지 않아도 emit 된 값이 listen 중이던 flux 쪽으로 와서 doOnNext를 타고 ui로 간다. ui에서도 비동기를 받을 수 있는 EventSource객체로 url을 호출한지라 eventSource.onmessage 함수에서 자동으로 데이터를 가져와 화면에 뿌려준다. 조회는 한 번만 눌렀을 뿐인데 화면에 변한 값이 자동으로 보인다.(신기하다..)
Subscription 방식은 한번 커넥션을 맺은 상태에서는 별다른 호출이 없어도 자동으로 변화를 감지하기 때문에 사용성이 좋을 것 같긴 하지만 webflux와 EventSource를 사용하면서 개발의 진입장벽이 있을 것으로 보인다. 또한 서버와 클라이언트의 Connection을 유지해야하기 때문에 Subscription Query가 증가할수록 그에 상응하는 Thread 수가 증가한다는 단점이 있다는 것을 확인해야 한다.
ui에서 별다른 유저 액션 없이도 어떻게 계속 동기화된 데이터를 받을 수 있을까를 고민했던 과거의 나에게 약간의 해답을 준 오늘의 실습이었다. 아직 모든 게 완벽히 와닿지는 않지만 여러 번 반복해서 보다 보면 조금씩 이해할 수 있지 않을까...
참고로 AbstractEventProcessor 구현체로 TrackingEventProcessor와 SubscribingEventProcessor가 있는데, 우리는 tracking모드로 구현하였으니 TrackingEventProcessor를 사용한다. 그리고 TrackingEventProcessor가 사용하는 설정 값은 TrackingEventProcessorConfiguration 에 있다.
기본값은 아래와 같다.
Thread 수 : 1개
Batch Size : 1
최대 Thread 수 : Segment 개수
TokenClaim 주기 : 5000ms
TrackingEventProcessorConfiguration.java
private static final int DEFAULT_BATCH_SIZE = 1;
private static final int DEFAULT_THREAD_COUNT = 1;
//단위 ms
private static final int DEFAULT_TOKEN_CLAIM_INTERVAL = 5000;
private int eventAvailabilityTimeout = 1000;
private TrackingEventProcessorConfiguration(int numberOfSegments) {
this.batchSize = DEFAULT_BATCH_SIZE;
this.initialSegmentCount = numberOfSegments;
this.maxThreadCount = numberOfSegments;
this.threadFactory = pn -> new AxonThreadFactory("EventProcessor[" + pn + "]");
this.tokenClaimInterval = DEFAULT_TOKEN_CLAIM_INTERVAL;
}
위 클래스에 정의된 함수를 사용하여 설정 값을 변경하면 된다.
2. 병렬처리
성능 향상을 고민할 때, 보통 생각하는 방법이 병렬 처리 기법이다. 허나 여기서 병렬로 처리 시 이벤트의 순서가 꼬이게 될 가능성이 있는데, axon에서는 이를 위해 sequencing 정책을 제공한다. 위 설정에서 단일 스레드를 병렬로 변경하고 스레드 갯수를 지정함다.
@Configuration
public class AxonConfig {
@Autowired
public void configure(EventProcessingConfigurer eventProcessingConfigurer){
eventProcessingConfigurer.registerTrackingEventProcessor(
"accounts",
org.axonframework.config.Configuration::eventStore,
configuration -> TrackingEventProcessorConfiguration
.forParallelProcessing(3) ////
.andBatchSize(100)
);
eventProcessingConfigurer.registerSequencingPolicy(
"accounts",
configuration -> SequentialPerAggregatePolicy.instance() ////
);
}
}
하지만 이렇게 해도 aggregate가 다르면 다른 스레드에서 실행될 수 있어 (예를 들어) 계좌 생성 이전에 잔액 조회가 실행되어 에러가 날 수 있다.
이를 위해 실패 시 자동으로 재시도하게 하는 spring-retry를 사용한다.
implementation 'org.springframework.retry:spring-retry'
//service @EnableRetry 추가
@EnableRetry
@Component
@RequiredArgsConstructor
@Slf4j
@ProcessingGroup("accounts")
public class HolderAccountProjection {
//method @Retryable 추가; 언제(에러발생 시) / 몇 번 시도(5회) / 언제 시도(1초 후)
@Retryable(value = {NoSuchElementException.class}, maxAttempts = 5, backoff = @Backoff(delay = 1000))
@EventHandler
@AllowReplay
protected void on(AccountCreationEvent event, @Timestamp Instant instant){
어플리케이션을 실행하니까, hibernate log가 난리난다.. 밀린 모든 이벤트에 대한 프로젝션을 진행해서인 듯
Hibernate: create table mv_account (holder_id varchar(255) not null, account_cnt int8 not null, address varchar(255) not null, name varchar(255) not null, tel varchar(255) not null, total_balance int8 not null, primary key (holder_id))
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select tokenentry0_.processor_name as processo1_3_0_, tokenentry0_.segment as segment2_3_0_, tokenentry0_.owner as owner3_3_0_, tokenentry0_.timestamp as timestam4_3_0_, tokenentry0_.token as token5_3_0_, tokenentry0_.token_type as token_ty6_3_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? and tokenentry0_.segment=? for update
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
... 반복
Hibernate: select tokenentry0_.processor_name as processo1_3_0_, tokenentry0_.segment as segment2_3_0_, tokenentry0_.owner as owner3_3_0_, tokenentry0_.timestamp as timestam4_3_0_, tokenentry0_.token as token5_3_0_, tokenentry0_.token_type as token_ty6_3_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? and tokenentry0_.segment=? for update
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: select holderacco0_.holder_id as holder_i1_1_0_, holderacco0_.account_cnt as account_2_1_0_, holderacco0_.address as address3_1_0_, holderacco0_.name as name4_1_0_, holderacco0_.tel as tel5_1_0_, holderacco0_.total_balance as total_ba6_1_0_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
그냥 띄우기만 해도 select for update 96번; update 96번; insert 7번
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 45
위 로그가 0~45 까지 두 바퀴 돈 것으로 보아 아마 액션이 45번이었을 것 같고, 그놈의 결과를 aggregateId별로 group by하니까 아래와 같이 7건이 나온 것 같다.
헉 그런데 더 놀라운건, 서버를 단지 띄우기만 했는데도 아래의 쿼리문이 매 초 2번씩 실행된다는 것이다..
Hibernate: update token_entry set timestamp=? where processor_name=? and segment=? and owner=?
replay 란 토큰 정보를 초기화하여 처음부터 혹은 일정 시점부터 토큰을 다 지우고 새롭게 넣는 방법이다.
이번 실습에서는 전체 초기화 후 다시 넣는 방법을 사용한다. 위 블로그대로 구현하고 실행하면 아래와 같은 로그가 지나간다(일부 발췌).
// service reset 실행
// 1. trackingEventProcessor.shutDown();
// 2. trackingEventProcessor.resetTokens(); // 토큰초기화
// 3. trackingEventProcessor.start();
o.a.e.TrackingEventProcessor : Shutdown state set for Processor 'accounts'.
o.a.e.TrackingEventProcessor : Processor 'accounts' awaiting termination...
o.a.a.c.u.FlowControllingStreamObserver : Observer stopped
o.a.e.TrackingEventProcessor : Released claim
o.a.e.TrackingEventProcessor : Worker for segment Segment[0/0] stopped.
c.c.q.p.HolderAccountProjection : reset triggered
Hibernate: delete from mv_account where holder_id=?
Hibernate: delete from mv_account where holder_id=?
Hibernate: delete from mv_account where holder_id=?
Hibernate: delete from mv_account where holder_id=?
Hibernate: delete from mv_account where holder_id=?
Hibernate: delete from mv_account where holder_id=?
Hibernate: delete from mv_account where holder_id=?
o.a.e.TrackingEventProcessor : Worker assigned to segment Segment[0/0] for processing
o.a.e.TrackingEventProcessor : Using current Thread for last segment worker: TrackingSegmentWorker{processor=accounts, segment=Segment[0/0]}
o.a.e.TrackingEventProcessor : Fetched token: ReplayToken{currentToken=IndexTrackingToken{globalIndex=-1}, tokenAtReset=IndexTrackingToken{globalIndex=45}} for segment: Segment[0/0]
o.a.a.c.event.axon.AxonServerEventStore : open stream: 0
o.a.a.c.u.FlowControllingStreamObserver : Sending response to AxonServer platform, remaining permits: 2500
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 0
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 1
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 2
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 3
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 4
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 5
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 6
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 7
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 8
o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1477/0x0000000800a2b040 for phase COMMIT
o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1479/0x0000000800a2a840 for phase ROLLBACK
o.a.m.unitofwork.AbstractUnitOfWork : Starting Unit Of Work
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 9
o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1482/0x0000000800a2a440 for phase ROLLBACK
o.a.m.u.MessageProcessingContext : Notifying handlers for phase STARTED
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 10
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 11
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 12
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 13
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 14
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 15
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 16
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 17
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 18
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 19
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 20
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 21
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 22
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 23
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 24
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 25
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 26
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 27
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 28
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 29
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 30
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 31
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 32
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 33
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 34
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 35
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 36
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 37
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 38
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 39
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 40
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 41
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 42
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 43
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 44
o.a.a.c.event.axon.AxonServerEventStore : Received event with token: 45
o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.TrackingEventProcessor$$Lambda$1488/0x0000000800a28c40 for phase PREPARE_COMMIT
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=9c53f4b2-9898-457f-a050-59dd012c8ddd, holderName=kevin, tel=02-1234-5678, address=OO시 OO구) , timestamp : 2022-01-13T06:45:47.273Z
o.a.m.unitofwork.AbstractUnitOfWork : Committing Unit Of Work
o.a.m.u.MessageProcessingContext : Notifying handlers for phase PREPARE_COMMIT
o.a.m.u.MessageProcessingContext : Notifying handlers for phase COMMIT
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
o.a.m.u.MessageProcessingContext : Notifying handlers for phase AFTER_COMMIT
o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLEANUP
o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLOSED
//1-cycle example
o.a.m.unitofwork.AbstractUnitOfWork : Starting Unit Of Work
o.a.m.u.MessageProcessingContext : Notifying handlers for phase STARTED
Hibernate: select tokenentry0_.processor_name as processo1_3_0_, tokenentry0_.segment as segment2_3_0_, tokenentry0_.owner as owner3_3_0_, tokenentry0_.timestamp as timestam4_3_0_, tokenentry0_.token as token5_3_0_, tokenentry0_.token_type as token_ty6_3_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? and tokenentry0_.segment=? for update
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=a00b1e82-252a-4d14-bc76-11a633514ece, holderName=kevin, tel=02-1234-5678, address=OO시 OO구) , timestamp : 2022-01-13T08:26:01.301Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_0_, holderacco0_.account_cnt as account_2_1_0_, holderacco0_.address as address3_1_0_, holderacco0_.name as name4_1_0_, holderacco0_.tel as tel5_1_0_, holderacco0_.total_balance as total_ba6_1_0_ from mv_account holderacco0_ where holderacco0_.holder_id=?
o.a.m.unitofwork.AbstractUnitOfWork : Committing Unit Of Work
o.a.m.u.MessageProcessingContext : Notifying handlers for phase PREPARE_COMMIT
o.a.m.u.MessageProcessingContext : Notifying handlers for phase COMMIT
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
o.a.m.u.MessageProcessingContext : Notifying handlers for phase AFTER_COMMIT
o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLEANUP
o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLOSED
//45 loop
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=5cfc5832-d1b8-4f73-bf4f-2afa3cf7ffa4, holderName=kevin, tel=02-1234-5678, address=OO시 OO구) , timestamp : 2022-01-13T08:37:00.461Z
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, holderName=kevin, tel=02-1234-5678, address=OO시 OO구) , timestamp : 2022-01-18T03:06:26.829Z
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
c.c.q.p.HolderAccountProjection : >>> projecting AccountCreationEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621) , timestamp : 2022-01-18T03:08:11.008Z
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting DepositMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=300) , timestamp : 2022-01-18T03:08:50.248Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.TrackingEventProcessor$$Lambda$1488/0x0000000800a28c40 for phase PREPARE_COMMIT
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T03:09:47.116Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T03:09:50.161Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T03:09:56.770Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T03:16:16.136Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=4eec3a20-efe0-4264-bbb5-ac3233c49960, holderName=kevin, tel=02-1234-5678, address=OO시 OO구) , timestamp : 2022-01-18T05:12:55.827Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_0_, holderacco0_.account_cnt as account_2_1_0_, holderacco0_.address as address3_1_0_, holderacco0_.name as name4_1_0_, holderacco0_.tel as tel5_1_0_, holderacco0_.total_balance as total_ba6_1_0_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
c.c.q.p.HolderAccountProjection : >>> projecting AccountCreationEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=d1726ff3-ee98-4262-8232-bae85ad26b27) , timestamp : 2022-01-18T05:13:07.173Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting DepositMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=300) , timestamp : 2022-01-18T05:13:18Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:13:53.745Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:14:34.937Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:18:44.814Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:24:47.280Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:25:33.178Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:55:17.833Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:55:30.381Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T05:57:29.647Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10) , timestamp : 2022-01-18T06:26:39.681Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, holderName=melvin, tel=02-1234-5678, address=OO시 OO구 hehe) , timestamp : 2022-01-18T06:54:22.558Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_0_, holderacco0_.account_cnt as account_2_1_0_, holderacco0_.address as address3_1_0_, holderacco0_.name as name4_1_0_, holderacco0_.tel as tel5_1_0_, holderacco0_.total_balance as total_ba6_1_0_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
c.c.q.p.HolderAccountProjection : >>> projecting AccountCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb) , timestamp : 2022-01-18T06:54:32.705Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400) , timestamp : 2022-01-18T06:54:57.181Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T06:55:24.383Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T06:55:36.025Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T06:55:47.363Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:02:42.397Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:06:09.120Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400) , timestamp : 2022-01-18T07:06:10.742Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:06:15.558Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:06:16.857Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting AccountCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=0041f839-4ea4-42bf-94ca-6ebcbdaaf2ee) , timestamp : 2022-01-18T07:15:32.990Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:16:07.264Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400) , timestamp : 2022-01-18T07:16:28.820Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:16:47.148Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:46:04.953Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T07:46:59.301Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10) , timestamp : 2022-01-18T08:11:22.699Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting HolderCreationEvent(holderID=65cc77b7-6820-472d-ba72-dd942f801f21, holderName=melvin, tel=02-1234-5678, address=OO시 OO구 hehe) , timestamp : 2022-01-19T04:23:43.806Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_0_, holderacco0_.account_cnt as account_2_1_0_, holderacco0_.address as address3_1_0_, holderacco0_.name as name4_1_0_, holderacco0_.tel as tel5_1_0_, holderacco0_.total_balance as total_ba6_1_0_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: insert into mv_account (account_cnt, address, name, tel, total_balance, holder_id) values (?, ?, ?, ?, ?, ?)
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
c.c.q.p.HolderAccountProjection : >>> projecting AccountCreationEvent(holderID=65cc77b7-6820-472d-ba72-dd942f801f21, accountID=fffb8fa2-94dd-4e84-b7fd-072d09d01d33) , timestamp : 2022-01-19T05:52:19.493Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting DepositMoneyEvent(holderID=65cc77b7-6820-472d-ba72-dd942f801f21, accountID=fffb8fa2-94dd-4e84-b7fd-072d09d01d33, amount=400) , timestamp : 2022-01-19T06:16:23.159Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting AccountCreationEvent(holderID=65cc77b7-6820-472d-ba72-dd942f801f21, accountID=38886d53-ad4c-4bfe-b61a-059948f4a2f5) , timestamp : 2022-01-19T06:25:34.896Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=65cc77b7-6820-472d-ba72-dd942f801f21, accountID=fffb8fa2-94dd-4e84-b7fd-072d09d01d33, amount=150) , timestamp : 2022-01-19T06:26:16.604Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
c.c.q.p.HolderAccountProjection : >>> projecting WithdrawMoneyEvent(holderID=65cc77b7-6820-472d-ba72-dd942f801f21, accountID=fffb8fa2-94dd-4e84-b7fd-072d09d01d33, amount=10) , timestamp : 2022-01-19T07:04:16.234Z
Hibernate: select holderacco0_.holder_id as holder_i1_1_, holderacco0_.account_cnt as account_2_1_, holderacco0_.address as address3_1_, holderacco0_.name as name4_1_, holderacco0_.tel as tel5_1_, holderacco0_.total_balance as total_ba6_1_ from mv_account holderacco0_ where holderacco0_.holder_id=?
Hibernate: update token_entry set owner=?, timestamp=?, token=?, token_type=? where processor_name=? and segment=?
Hibernate: update mv_account set account_cnt=?, address=?, name=?, tel=?, total_balance=? where holder_id=?
로그를 보니 디비 내용물을 다 지우고 처음부터 다시 넣는 것을 알 수 있으며, 디비 값을 조회했을 때 이전과 똑같은 결과가 나오는 것을 확인할 수 있다.
근데 왜 replay를 query 쪽에서 구현하는걸까.. 아마 이벤트에 대한 새로운 정보가 아닌(실시간으로 사용자의 이벤트에 기반한게 아닌), 과거의 행동의 재사용이라서 그러지 않을까 추측해본다.
그나저나 이벤트의 양이 많으면 replay에 대한 속도도 느릴 것 같은데, 다음 시간에 성능 개선을 한다고 한다..!
state store aggregate 으로 전환하고 실행, api 쏴보니 아래와 같은 jpa 에러가 난다.
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.cqrs.command.aggregate.HolderAggregate.accounts, could not initialize proxy - no Session
다시 하는데 아래와 같은 에러가 난다... 열심히 구글링했는데 별다른 힌트를 얻지 못해서.. axonserver 를 재시작했는데.. 세상에 잘 된다..
(설정을 변경해보려다 실패해서 얻어걸린 재시작 ㅎ) .. 내 2시간..ㅎ
2022-01-19 14:15:50.006 WARN 6587 --- [ault-executor-3] o.a.a.c.u.ResubscribableStreamObserver : A problem occurred in the stream.
io.grpc.StatusRuntimeException: CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream
..
Hibernate: insert into account (balance, holder_id, account_id) values (?, ?, ?)
2022-01-19 14:15:50.010 ERROR 6587 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
org.axonframework.axonserver.connector.command.AxonServerCommandDispatchException: CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream
...
2022-01-19 14:15:50.010 ERROR 6587 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is AxonServerCommandDispatchException{message=CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream, errorCode='AXONIQ-4003', server='6587@AL01590036.local'}] with root cause
org.axonframework.axonserver.connector.command.AxonServerCommandDispatchException: CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream
...
2022-01-19 14:35:34.782 WARN 6587 --- [ault-executor-7] o.a.a.c.u.ResubscribableStreamObserver : A problem occurred in the stream.
io.grpc.StatusRuntimeException: UNAVAILABLE: HTTP/2 error code: NO_ERROR
Received Goaway
app_requested
hibernate sql log가 지나간다.
Hibernate: insert into holder (address, holder_name, tel, holder_id) values (?, ?, ?, ?)
---
Hibernate: select holderaggr_.holder_id, holderaggr_.address as address2_2_, holderaggr_.holder_name as holder_n3_2_, holderaggr_.tel as tel4_2_ from holder holderaggr_ where holderaggr_.holder_id=?
Hibernate: insert into account (balance, holder_id, account_id) values (?, ?, ?)
---
Hibernate: select accountagg0_.account_id as account_1_0_0_, accountagg0_.balance as balance2_0_0_, accountagg0_.holder_id as holder_i3_0_0_ from account accountagg0_ where accountagg0_.account_id=? for update
Hibernate: select holderaggr0_.holder_id as holder_i1_2_0_, holderaggr0_.address as address2_2_0_, holderaggr0_.holder_name as holder_n3_2_0_, holderaggr0_.tel as tel4_2_0_, accounts1_.holder_id as holder_i3_0_1_, accounts1_.account_id as account_1_0_1_, accounts1_.account_id as account_1_0_2_, accounts1_.balance as balance2_0_2_, accounts1_.holder_id as holder_i3_0_2_ from holder holderaggr0_ left outer join account accounts1_ on holderaggr0_.holder_id=accounts1_.holder_id where holderaggr0_.holder_id=?
Hibernate: update account set balance=?, holder_id=? where account_id=?
---
db를 봐도 잘 들어가 있다.
event sourced aggregate 방식과 다르게 매 스탭의 결과를 디비에 저장하기 때문에 매번 작업을 새롭게 하지 않는다는 장점이 있으나, 디비 연결에 대한 네트워크 이슈(속도), 디비가 날아가면 히스토리를 알기 어려움 등의 단점도 알아둬야겠다.
디비에 저장하면서 이벤트 발생 히스토리도 별도로 저장하면 좋지 않을까?
추가)
다시 소스를 보니 @CommandHandler 어노테이션만 있으면 별도의 repository나 save 등의 액션이 없이도 디비에 수정사항이 생기고 변화하는 것을 알 수 있다.
원문을 보면 아래와 같은 문구를 확인할 수 있다. 없으면 자동 생성해서 해주는 듯 하다.
Axon will automatically register all the @CommandHandler annotated methods with the command bus and set up a repository if none is present.
POST http://localhost:8080/holder
POST http://localhost:8080/account
POST http://localhost:8080/deposit
POST http://localhost:8080/withdrawal
POST http://localhost:8080/withdrawal
POST http://localhost:8080/withdrawal
위 글에도 써있지만 아래와 같은 순서로 잔고를 인출했을 때의 로그는 아래와 같다(withdrawal을 10원씩 4번째 호출했을 때이다).
다시 말하지만 api 한번 호출 시의 로그이다.
[nio-8080-exec-3] o.a.commandhandling.SimpleCommandBus : Handling command [com.cqrs.command.commands.WithdrawMoneyCommand]
[nio-8080-exec-3] o.a.m.unitofwork.AbstractUnitOfWork : Starting Unit Of Work
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1197/0x00000008008b5040 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Notifying handlers for phase STARTED
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1201/0x00000008008b4040 for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1202/0x00000008008b4440 for phase ROLLBACK
[nio-8080-exec-3] o.a.a.c.event.axon.AxonServerEventStore : Reading events for aggregate id 2b158c88-b810-4643-9db4-4096edea5621
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : >> applying AccountCreationEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : applying DepositMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=300)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : balance 300
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : balance 290
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : balance 280
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : balance 270
[nio-8080-exec-3] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[ault-executor-3] o.a.a.c.e.AxonServerEventStoreClient : Done request for 2b158c88-b810-4643-9db4-4096edea5621: 22ms, 5 events
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.LockingRepository$$Lambda$1300/0x0000000800a0d440 for phase CLEANUP
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1301/0x0000000800a0d840 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1302/0x0000000800a0dc40 for phase PREPARE_COMMIT
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : handling WithdrawMoneyCommand(accountID=2b158c88-b810-4643-9db4-4096edea5621, holderID=76147ef5-280f-439e-b017-bcd04028f176, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate : balance 260
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1222/0x00000008008f3440 for phase AFTER_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1223/0x00000008008f3840 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1225/0x00000008008f4040 for phase PREPARE_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1226/0x00000008008f4440 for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1227/0x00000008008f4840 for phase AFTER_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1228/0x00000008008f4c40 for phase CLEANUP
[nio-8080-exec-3] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-3] o.a.m.unitofwork.AbstractUnitOfWork : Committing Unit Of Work
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Notifying handlers for phase PREPARE_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1245/0x000000080091f040 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1246/0x000000080091f440 for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Notifying handlers for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Notifying handlers for phase AFTER_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLEANUP
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLOSED
/withdraw api의 aggregateId 인 acoountId 에 대한 모든 이벤트를 다시 불러오는 듯 하다. 즉 위 로그처럼 acoountId에 대한 모든 이벤트를 롤백하고(마치 계좌가 없던 것 처럼) 다시 첨부터 하는 것을 반복한다. 즉 여기서는 계좌 생성(/account), 입금(/deposit), 출금 n번(/withdraw)이다.
신박하다.. 데이터의 정합성은 보장될 것 같은데 효율성은 떨어질 것 같다.
그래서 클론 코딩에서 알려준대로 스냅샷을 적용하고 새로운 마음으로 계정생성1, 입금2, 인출3, 인출4, 인출5 를 해본다.
아래는 인출5 api실행 시 로그다.
[nio-8080-exec-5] o.a.commandhandling.SimpleCommandBus : Handling command [com.cqrs.command.commands.WithdrawMoneyCommand]
[nio-8080-exec-5] o.a.m.unitofwork.AbstractUnitOfWork : Starting Unit Of Work
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1192/0x000000080087ac40 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Notifying handlers for phase STARTED
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1196/0x0000000800879c40 for phase COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1197/0x0000000800879040 for phase ROLLBACK
[nio-8080-exec-5] o.a.a.c.event.axon.AxonServerEventStore : Reading events for aggregate id f00088d7-c81a-432c-b010-28a88012f0eb
[ault-executor-4] o.a.a.c.e.AxonServerEventStoreClient : Done request for f00088d7-c81a-432c-b010-28a88012f0eb: 12ms, 4 events
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : >> applying AccountCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 400
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 390
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 380
[nio-8080-exec-5] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.LockingRepository$$Lambda$1246/0x00000008009c2c40 for phase CLEANUP
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1247/0x00000008009c3040 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1249/0x00000008009c3840 for phase PREPARE_COMMIT
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : handling WithdrawMoneyCommand(accountID=f00088d7-c81a-432c-b010-28a88012f0eb, holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 370
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1260/0x00000008009c6440 for phase AFTER_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1261/0x00000008009c6840 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1263/0x00000008009c7040 for phase PREPARE_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1264/0x00000008009c7440 for phase COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1265/0x00000008009c7840 for phase AFTER_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1266/0x00000008009c7c40 for phase CLEANUP
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventsourcing.EventCountSnapshotTriggerDefinition$EventCountSnapshotTrigger$$Lambda$1245/0x00000008009c2840 for phase PREPARE_COMMIT
[nio-8080-exec-5] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-5] o.a.m.unitofwork.AbstractUnitOfWork : Committing Unit Of Work
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Notifying handlers for phase PREPARE_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1276/0x00000008009ce840 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1277/0x00000008009cec40 for phase COMMIT
[nio-8080-exec-5] o.a.a.c.event.axon.AxonServerEventStore : Reading events for aggregate id f00088d7-c81a-432c-b010-28a88012f0eb
[ault-executor-4] o.a.a.c.e.AxonServerEventStoreClient : Done request for f00088d7-c81a-432c-b010-28a88012f0eb: 10ms, 4 events
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : >> applying AccountCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 400
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 390
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 380
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate : balance 370
[nio-8080-exec-5] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Notifying handlers for phase COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Notifying handlers for phase AFTER_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLEANUP
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLOSED
[ault-executor-4] o.a.a.c.event.axon.AxonServerEventStore : Snapshot created
왜 400 -> 370 까지 로그가 두 번 찍히는지는 모르겠지만 마지막에 snapshot created.
이 후 인출6 시 로그. 확실히 400 -> 370 까지 다시 하지 않고 바로 360으로 간다.
[nio-8080-exec-7] o.a.commandhandling.SimpleCommandBus : Handling command [com.cqrs.command.commands.WithdrawMoneyCommand]
[nio-8080-exec-7] o.a.m.unitofwork.AbstractUnitOfWork : Starting Unit Of Work
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1192/0x000000080087ac40 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Notifying handlers for phase STARTED
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1196/0x0000000800879c40 for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1197/0x0000000800879040 for phase ROLLBACK
[nio-8080-exec-7] o.a.a.c.event.axon.AxonServerEventStore : Reading events for aggregate id f00088d7-c81a-432c-b010-28a88012f0eb
[ault-executor-5] o.a.a.c.e.AxonServerEventStoreClient : Done request for f00088d7-c81a-432c-b010-28a88012f0eb: 15ms, 1 events
[nio-8080-exec-7] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.LockingRepository$$Lambda$1246/0x00000008009c2c40 for phase CLEANUP
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1247/0x00000008009c3040 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1249/0x00000008009c3840 for phase PREPARE_COMMIT
[nio-8080-exec-7] c.c.command.aggregate.AccountAggregate : handling WithdrawMoneyCommand(accountID=f00088d7-c81a-432c-b010-28a88012f0eb, holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, amount=10)
[nio-8080-exec-7] c.c.command.aggregate.AccountAggregate : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-7] c.c.command.aggregate.AccountAggregate : balance 360
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1260/0x00000008009c6440 for phase AFTER_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1261/0x00000008009c6840 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1263/0x00000008009c7040 for phase PREPARE_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1264/0x00000008009c7440 for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1265/0x00000008009c7840 for phase AFTER_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1266/0x00000008009c7c40 for phase CLEANUP
[nio-8080-exec-7] org.axonframework.messaging.Scope : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-7] o.a.m.unitofwork.AbstractUnitOfWork : Committing Unit Of Work
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Notifying handlers for phase PREPARE_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1276/0x00000008009ce840 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1277/0x00000008009cec40 for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Notifying handlers for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Notifying handlers for phase AFTER_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLEANUP
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext : Notifying handlers for phase CLOSED
그 이후로도 입금/출금 계속 했더니 위처럼 반복되다가 5번째마다 스냅샷이 잘 찍히는 것을 알 수 있다.
실패기록:
api를 몇 번 랜덤하게 쐈더니 아래와 같은 에러가 났다.
AxonServerException OUT_OF_RANGE: [AXONIQ-2000] Invalid sequence number while storing snapshot.
무슨 의미인지 찾아보던 중, 원문에 default가 tracking이라는데 저걸 또 명시하는게 무슨 의미인가 싶은데... 또 에러가 해결은 되고... ㅎ
Event Processors come in roughly two forms: Subscribing and Tracking. The Subscribing Event Processors subscribe themselves to a source of Events and are invoked by the thread managed by the publishing mechanism. Tracking Event Processors, on the other hand, pull their messages from a source using a thread that it manages itself. ... By default, Axon will use Tracking Event Processors.