Page History
Info |
---|
스냅샷은 앞장 이벤트소싱에서도 부분적으로 이벤트소싱과 함께 사용이되었습니다. 스냅샷은 다양한곳에서 사용될수 있으며 그 컨셉은 간단합니다. 무수히 발생하는 이벤트로 인해 우리가 설계한 어떠한 객체의 상태는 지속적으로 변경된다는 점이며 복원관점에서 연속된 이벤트 재생이 필요하며 이벤트자체를 모두 기록하는것과 필요한 순간의 청사진만 찍어서 그것을 적절하게 활용할수 있다란 것입니다. 상태를 기록하는 범위와 주기에따라 아래와 같은 차이가 있으며, 그 차이를통해 스냅샷을 이해할수가 있습니다.
|
주로 게임 리플리와같이 게임시작부터 종료까지 게임플레이를 재생하려면 모든 상태보다 발생 이벤트를 기록해야하며
장바구니기능과 같이넣고빼고한 모든것을 기록할 필요없이 마지막 정보인 특정시점의 상태를 기록하는것이 이점입니다.
게임의 경우 고용량의 영상소스없이 재생이 가능하게되며, 언제든 원하면 고해상도의 동영상 제작도 가능해집니다.
장바구니의 경우 마지막 장바구니는 구매시 활용하고 특정시점의 장바구니는 다양한 상품 추천의 근거자료로 사용될수 있습니다.
모두를 기록해야하나? 특정시점을 기록해야하냐? 저장소(DB)를 포함하여 IT전반적으로 고민하고 선택하는 주제이며
Redo와 Snapshot의 차이로 일반적으로 많이 언급되며 여기서 오라클을 붙인다던지, 이전작업을 복원하는 redo task 를 붙인다던지
다양한 연관 주제에대해 접근이 가능합니다.
연관 키워드 : difference between redo and snapshot
AKKA에서 스냅샷의 의미
액터를 통해 수많은 실시간 메시지(이벤트)가 발생하며, 그 메시지는 상태를 수없이 변경을 합니다.
어떠한 기능을위해 그 상태 변경을 우리가 의도한 만큼 저장을 하고 복원을 할수가 있습니다.
...
최근 이벤트 100개를 유지하는것과 ( 순수 Persist기능), 1000번째마다 스냅샷을 찍어서 100개를 유지하는것은 각각 요구하는 목적이 다르며 필요로하는 성능이 다를수 있으나 함께 작동을 하여 상호보완하는 컨셉이며 VMWare를 포함하여 데이터베이스 복구등에서 보편적으로 사용되는 장치입니다. |
스냅샷 구현
Code Block | ||||
---|---|---|---|---|
| ||||
@Component @Scope("prototype") public class SnapShotActor extends AbstractPersistentActor { private final LoggingAdapter log = Logging.getLogger(getContext().system(), "AbstractPersistentActor"); final case class SnapshotMetadata(persistenceId: String, sequenceNr: Long, timestamp: Long = 0L) private Object state; private int snapShotInterval = 5; private int msgCnt = 1000; 0; @Override public String persistenceId() { return "ExamplePersistentActor-id-1"; } @Override public Receive createReceiveRecover() { return receiveBuilder(). match(SnapshotOffer.class, s -> { state = s.snapshot(); //상태복원 log.info("상태복원"); // ... }). match(String.class, s -> {/* ...*/}).build(); } @Override //복구전략(스냅샷을 무시할수도 있음) public Recovery recovery() { //return Recovery.create(SnapshotSelectionCriteria.none()); return Recovery.create( SnapshotSelectionCriteria .create(457L, System.currentTimeMillis())); } // 스냅샷을 지원하는 메시지 정의 @Override public Receive createReceive() { return receiveBuilder(). match(SaveSnapshotSuccess.class, ss -> { SnapshotMetadata metadata = ss.metadata(); // ... }). match(SaveSnapshotFailure.class, sf -> { SnapshotMetadata metadata = sf.metadata(); // ... }). match(String.class, cmd -> { log.info("EventFired:"+cmd); persist( "evt-" + cmd, e -> { updateState(e); if (lastSequenceNr() if(cmd.indexOf("print") ==0 ) { //로그를 통해 확인하거나? log.info("상태확인:"+state); // 전송자에게 메시지를 통해 상태를 알려줌 sender().tell(state, ActorRef.noSender()); }else { msgCnt++; state = cmd + "을 먹은 상태"; //커멘드에따른 상태변화 log.info("ChangeStated" + state); if (msgCnt % snapShotInterval == 0 && lastSequenceNr() != 0) ) { //이벤트가 ?회 발생할때만,상태를 변경하고 스냅샷을 찍음 ( 순수스냅샷 테스트 ) log.info("SaveSnapShot:" + state); saveSnapshot(state); } }); }) .build(); } } |
이벤트를 받게되면, 상태가 변경되게되며 스냅샷 저장에 필요한 메타정의(SnapshotMetadata) 후 자신이 원하는 타이밍에
카메라 셔터(saveSnapshot) 를 누르기만 하면됩니다.
...
여기서는 수많은 이벤트를 모두 저장하는것은 비효율적이니, 매 5번째 상태의 스냅샷정보를 유지하기로 하였다고 가정하였습니다.
실제, 앞장 샘플에서는 persist기능과 연동되어 스냅샷과 상관없이 최근 X개 유지기능을 통해 , 마지막 상태복구가가능합니다.
스냅샷 복원전략
Code Block | ||||
---|---|---|---|---|
| ||||
private Object state; @Override @Override public ReceiveRecovery createReceiveRecoverrecovery() { return receiveBuilder().Recovery.create( SnapshotSelectionCriteria match(SnapshotOffer.class, s -> { state = s.snapshot(); .create(457L, System.currentTimeMillis())); } |
마지막 상태를 복원(SnapshotOffer - 추천복원 )할수도 있고,
스냡샷은 히스토리 관리가 되기때문에
특정 시간의 스냅샷(SnapshotSelectionCriteria)을 복원할수도 있습니다.
스냅샷 테스트
Code Block | ||||
---|---|---|---|---|
| ||||
protected void persistenceSnapShot() { new TestKit(system) {{ ActorRef probe = getRef(); Props snapShotActorProp //= ext.props("snapShotActor"); System.out.println("===== snapShotActor 액터생성"); ActorRef snapShotActor = }). match(String.class, s -> {/* ...*/}).build(); } 또는 @Override public Recovery recovery() { return Recovery.create( SnapshotSelectionCriteria .create(457L, System.currentTimeMillis())); }system.actorOf(snapShotActorProp, "snapShotActor"); System.out.println("===== event 생성"); snapShotActor.tell("커피", ActorRef.noSender()); snapShotActor.tell("사탕", ActorRef.noSender()); snapShotActor.tell("커피", ActorRef.noSender()); snapShotActor.tell("스테이크", ActorRef.noSender()); snapShotActor.tell("라면", ActorRef.noSender()); // <-- 복구기대 상태 snapShotActor.tell("사탕", ActorRef.noSender()); snapShotActor.tell("커피", ActorRef.noSender()); System.out.println("===== 상태확인"); snapShotActor.tell( "print" , ActorRef.noSender()); expectNoMessage(java.time.Duration.ofSeconds(1)); System.out.println("===== snapShotActor 종료또는 비정상종료"); snapShotActor.tell( akka.actor.PoisonPill.getInstance() , ActorRef.noSender()); expectNoMessage(java.time.Duration.ofSeconds(1)); System.out.println("===== snapShotActor 재생성"); ActorRef snapShotActo2 = system.actorOf(snapShotActorProp, "eventActor"); System.out.println("===== 상태 복원확인을 위해 probe를 액터 참조자로 지정"); snapShotActo2.tell( "print" , probe ); // === 기대결과 1초이내에 '라면을 먹은 상태'임을 확인 : 비동기 메시지를 검사하는방법중하나 expectMsgEquals(java.time.Duration.ofSeconds(1), "라면을 먹은 상태" ); }}; } |
Expand | ||
---|---|---|
| ||
===== snapShotActor 액터생성 |
샘플은 모든 이용자가 먹을때마다 이벤트가 발생하고,그 시식 결과에의해 어떠한 상태로 변경이 됩니다.
이벤트드리븐 의 예를 간단하게 설명하는것으로 대단한 기능이 있는것은 아닙니다.
단지 이벤트는 무수히 발생하며 모든것을 기록하고 이용하기 어려우니, 스냅샷은 특정한 이벤트 조건에따라(여기서는 단순하게 5번째)
사진을 찍게되며 , 모든 사용자의 청사진을 최근 몇개까지 운영할것인가를 성능과 복원을 고려하여 설계에 반영할수 있다란것입니다..
최근 메시지를 유지하는것과 변경된 상태의 청사진을 오랫동안 유지하는것은 각각 다른 성능비용이 발생합니다
마지막 상태를 복원(SnapshotOffer)할수도 있고,
각 시간대별 스냅샷은 고유 아이디가 존재함으로
특정 스냅샷(SnapshotSelectionCriteria)을 복원할수도 있습니다.
스냅샷 시도에따른 반응 메시지
Method | Success | Failure message |
---|---|---|
saveSnapshot(Any) | SaveSnapshotSuccess | SaveSnapshotFailure |
deleteSnapshot(Long) | DeleteSnapshotSuccess | DeleteSnapshotFailure |
deleteSnapshots(SnapshotSelectionCriteria) | DeleteSnapshotsSuccess | DeleteSnapshotsFailure |
스냅샷은 다양한 이유로(디스크풀및 메모리풀등) 실패가 있을수 있으며 그에 대응하는 코드작성도 가능합니다.
AKKA의 에러처리는 Exception을 발생하여 하위 컴포넌트에게 전가하기보다
주로 상위 컴포넌트에게 메시지를통해 알려주는 방식을 선택합니다.
참고Link :
- 스냅샷 :
...
- https://doc.akka.io/docs/akka/2.5/persistence.html#snapshots
- 상태객체를 DB 스키마와 일치 시키는방법 : https://doc.akka.io/docs/akka/current/persistence-schema-evolution.html
다양한 활용 샘플 검색: