Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

나의 마지막상태, 예를 들면 내가 마지막에 본 페이지를 서비스가 기록을 하고 다른기기를 옮겨가도 

그 상태를 복구하여 그대로 보여줄수 있는 서비스가 있다고 가정해봅시다. 사용자가 페이지를 이동할때마다

전통적인 개발방법은 메인 DB에 기록을 할것이며, 메인 DB에 부하를 주게될것입니다. 어쨋든 어플리케이션은

종료가 되거나 재시작이 될수 있으니까요, 액터의 스냅샷을 이용하면 자신이 원하는 타이밍에 원하는

로컬 스토리지에 저장을하고 마지막 상태를 완벽하게 복원할수가 있습니다. 여기서 원하는 타이밍은

페이지 이동에따른 비용이아닌, 만약 비용보다 훨씬 큰 비용이 사용되게될시,예를들어 초당 100건이상의 메시지가 발생한다라고 가정해봅시다.

이 모든타이밍에 스냅샷에 기록할수도 있겠지만 낭비적입니다. 그렇게 저장할만큼 IO를 희생해야할 중요한 기능은 아니기때문에아니며

이러한 경우 스냅샷 시기를 느슨하게 하여 어쨋든 장애로인한 재시작 이후 마지막에 저장한 스냅샷을 복구해줄것입니다.

마지막 상태의 자료형을 String으로 가져갈것인지? EventList로 가져갈것인지에따라 이벤트 소싱을 복원할수도 있고

마지막 상태만 복원할수도 있습니다. 스냅샷은 단순하게 무엇을 복원할것인가에 대해 포커싱을 맞춥니다.

액터설계

Code Block
languagec#
themeEmacs
linenumberstrue
public class MySnapShotActor : UntypedPersistentActor
{
    private ILoggingAdapter log = Context.GetLogger();
    public override string PersistenceId => "my-stable-persistence-id";
    private const int SnapShotInterval = 2; //2의 배수에 스냅샷을 저장합니다.
    private object state = new object();

    //실시간 복구수 전략을 선택합니다. (최근5개)
    public override Recovery Recovery => new Recovery(fromSnapshot: new SnapshotSelectionCriteria(minSequenceNr:0, maxSequenceNr: 5, maxTimeStamp: DateTime.UtcNow));
    //public override Recovery Recovery => new Recovery(fromSnapshot: SnapshotSelectionCriteria.Latest);

    protected override void OnRecover(object message)
    {
        // handle recovery here
        if (message is SnapshotOffer offeredSnapshot)
        {
            log.Debug("마지막 스냅샷 복구");
            state = offeredSnapshot.Snapshot;
            log.Debug("스냅샷복구완료:" + state);
        }
        else if (message is RecoveryCompleted)
        {
            log.Debug("스냅샷복구완료:");
        }
        else
        {
            // event
            log.Debug("스냅샷e:" + message);
        }
    }

    protected override void OnCommand(object message)
    {
        if (message is SaveSnapshotSuccess s)
        {
            log.Debug("스냅샷 성공");
        }
        else if (message is SaveSnapshotFailure f)
        {
            log.Debug("스냅샷 실패");
        }
        else if (message is string cmd)
        {
            log.Debug("cmd:" + cmd);
            Persist($"evt-{cmd}", e =>
            {
                UpdateState(e);
                if (LastSequenceNr % SnapShotInterval == 0 && LastSequenceNr != 0)
                {
                    log.Debug("스냅샷 시도");
                    SaveSnapshot(state);
                }
            });
        }
    }

    private void UpdateState(string evt)
    {
        state = evt;
    }
}

...

스냅샷은 다양한 저장소에 저장이 가능하며,  akka.config에서 지정가능합니다.

여기서 주로 설명하는 방법이, 메인DB를 혹사시키지 않는 방법에 대해서이지만

저장 플러그인 설정에따라 로컬 DB로할지? 메인 DB로 할지? 그냥 DISK로할지?

데이터의 중요도에따라 전략화가 가능합니다.


Panel
title설정예

akka.persistence.journal.inmem {
class = "Akka.Persistence.Journal.MemoryJournal, Akka.Persistence"
# Dispatcher for the plugin actor.
plugin-dispatcher = "akka.actor.default-dispatcher"
}

akka.persistence.snapshot-store.inmem {
# Class name of the plugin.
class = "Akka.Persistence.Snapshot.MemorySnapshotStore, Akka.Persistence"
# Dispatcher for the plugin actor.
plugin-dispatcher = "akka.actor.default-dispatcher"
}

akka.persistence.snapshot-store.local {
# Class name of the plugin.
}


별다른설정이없으면, 파일에 저장이 됩니다. 

...