콘텐츠로 이동

05. Actor relocation

레퍼런스 목차

이 category는 ZLinkActorManager·ZLinkActorClient가 제공하는 외부 진입점과, Actor 코드 안에서 ZLinkActorContext로 Spot에 참여하는 진입점, 그리고 relocation 정책 선택을 다룬다. 정확한 signature는 Java Actor exact interface가 소유한다.


ZLinkActorManager.create

새 Actor를 항상 새로 만든다.

ZLinkActorCreateResult created = actorManager.create("player-1", "player")
    .inMesh("play")
    .request(new SpawnPlayer("player-1"))
    .submit()
    .toCompletableFuture().get();

옵션. 이 호출에는 다음 modifier가 붙는다.

Modifier 기본값 의미
.inMesh(meshName) Object Client·Server role의 Mesh가 하나면 생략 가능 Actor를 생성할 Mesh. 후보가 둘 이상인데 생략하면 INVALID_OPERATION, 없으면 NOT_CONFIGURED, 지정한 Mesh가 없으면 NOT_FOUND
.request(Object) / .request(ZLinkMessage) 없음(빈 요청) Actor factory 생성 시점에 전달할 요청
.timeout(Duration) 5초 resolve·reservation·factory·Ready barrier 전체의 deadline
.submit() terminal(택 1) 생성 완료까지 기다린다
.yield() terminal(택 1) SpotWide handler 안에서만 유효

완료 결과. ZLinkActorCreateResult(sealed interface)는 Created(새로 생성) 또는 Rejected(factory가 거부) 중 하나로 완료한다. 같은 ActorId의 Ready incarnation이 이미 있으면 두 대안이 아니라 ALREADY_EXISTS 오류로 완료한다 — ExistinggetOrCreate에만 있다. Ready incarnation이 있는데 stable type이 다르면 TYPE_MISMATCH다.

선택 기준. 항상 새 Actor가 필요할 때 쓴다. 있으면 재사용하고 없을 때만 만들려면 getOrCreate를 쓴다.


ZLinkActorManager.getOrCreate

같은 ActorId의 Ready Actor가 있으면 그것을 반환하고, 없으면 새로 만든다.

ZLinkActorCreateResult existingOrCreated = actorManager.getOrCreate("player-1", "player")
    .inMesh("play")
    .request(new SpawnPlayer("player-1"))
    .submit()
    .toCompletableFuture().get();

옵션. create와 동일하다 — .inMesh(...), .request(...), .timeout(...), terminal .submit() 또는 .yield().

완료 결과. Existing이면 이미 있던 Actor를 반환하고 request는 무시한다. Creating attempt와 경합하면 그 결과를 기다렸다가 합류하며, 서로 다른 operation은 Ready 뒤 Existing을 받고 이전 reply를 공유하지 않는다.

선택 기준. ActorId로 멱등하게 "있으면 쓰고 없으면 만들기"가 필요할 때 쓴다.


find / findSpot / destroy (manager)

기존 Actor를 조회하거나, 참여 중인 Spot을 조회하거나, 정확한 incarnation을 종료한다.

Optional<ActorRef> actor = actorManager.find("player-1").toCompletableFuture().get();
Optional<SpotRef> spot = actorManager.findSpot("player-1").toCompletableFuture().get();

if (actor.isPresent()) {
    boolean destroyed = actorManager.destroy(actor.get()).toCompletableFuture().get();
}

옵션. 세 호출 모두 modifier가 없다 — 대상 식별자만 받는다.

완료 결과. find는 Ready Actor가 없으면 Optional.empty()를 반환한다. findSpot은 current User Spot membership이 없으면 Optional.empty()를 반환한다. destroy는 해당 incarnation이 없으면 false, generation이 다르면 INVALID_OPERATION, pre-commit seal 중이면 UNAVAILABLE이다.

선택 기준. 지금 시점의 존재·소속 확인이나 명시적 종료가 필요할 때 쓴다.


sendToActor / requestToActor (ZLinkActorClient)

Global ActorId 하나로 one-way message를 보내거나 typed request/reply를 주고받는다. 외부 client에서 쓴다.

actorClient.sendToActor("player-1", new GrantItem("sword")).submit();

CompletionStage<Inventory> reply = actorClient
    .requestToActor("player-1", new GetInventory())
    .timeout(Duration.ofSeconds(3))
    .submit(Inventory.class);

옵션. sendToActor.metadata(...)와 terminal .submit()만 있다. requestToActor는 다음이 더 있다.

Modifier 기본값 의미
.timeout(Duration) MeshNode의 request 기본 timeout reply를 기다리는 상한
.submit(TReply.class) terminal(택 1) reply 수신까지 기다린다
.yield(TReply.class) terminal(택 1) SpotWide handler 안에서만 유효

완료 결과. ActorId가 없으면 NOT_FOUND. 나머지 완료 kind는 messaging-execution category의 공통 규칙과 같다.

선택 기준. Reply가 필요 없으면 sendToActor, 필요하면 requestToActor를 쓴다.


joinSpot / joinEntrySpot (Actor 코드 안)

현재 Actor를 User Spot 또는 Entry Spot에 참여시킨다. ZLinkActorContext.joinSpot(...)/ joinEntrySpot(...)로 호출하며, 다른 항목과 달리 terminal이 submit/yield가 아니라 defer() 하나뿐이다.

context.joinSpot("room-42", new JoinRoomRequest("player-1"))
    .timeout(Duration.ofSeconds(5))
    .defer();

옵션. 이 호출에는 다음 modifier가 붙는다.

Modifier 기본값 의미
.timeout(Duration) 5초 monotonic absolute deadline
.defer() 필수 terminal 결과 없는 동기 호출. Join intent와 비활성 barrier만 등록하고 target 조회를 바로 시작하지 않는다

완료 결과. defer() 자체는 반환값이 없다. 현재 handler가 정상적으로 끝나면 barrier가 활성화되어 Join을 실행하고, handler가 실패하면 barrier를 폐기한다. 실제 결과(수락·거부·실패)는 같은 128-bit ZLinkActorJoinOperationId를 담은 ZLinkActor.onJoinCompleted(...) callback으로 비동기 전달된다 — Accepted/Rejected/Failed(sealed interface ZLinkActorJoinCompletion) 중 하나다.

선택 기준. Actor를 다른 Spot으로 옮기거나 Entry Spot으로 되돌릴 때 쓴다. Entry Spot과 PER_ACTOR User Spot의 Actor에서 호출하면 INVALID_OPERATION으로 완료한다.


Relocation 정책 선택 (Actor factory 등록 시점)

addActorFactory(actorType, actorClass, factoryClass, configure)(topology-discovery category)의 configure callback에서 정확히 하나를 선택한다.

정책 cross-node 이동 시 동작 선택 기준
disableRelocation() Capture 전에 이동 자체를 거부한다 이 Actor가 다른 node로 옮겨지면 안 될 때
recreateOnRelocation() Target factory로 같은 logical identity를 다시 만든다. Application state는 복구하지 않는다 State 없이 다시 만들어도 되는 Actor일 때
preserveStateWith(adapterClass) ZLinkActorRelocationAdapter<TActor>.capture/restore로 opaque byte[]를 옮긴다 State를 유지한 채 옮겨야 할 때

완료 결과. preserveStateWithcapture(...) 결과는 최대 64 MiB다. Capture·Restore는 같은 relocation에서 여러 번 호출될 수 있으므로 두 callback 모두 retry-safe해야 한다 — 외부 side effect의 exactly-once 실행에 의존하면 안 된다. adapterClass가 해당 Actor type의 ZLinkActorRelocationAdapter<TActor>를 구현하지 않으면 startup configuration error다.

선택 기준. 세 정책 중 무엇을 고르느냐가 이 Actor 타입의 relocation 동작 전체를 결정한다 — factory 등록 시점에 한 번만 정하고 나중에 호출별로 바꿀 수 없다.


전체 근거는 Java Actor exact interface를 참고한다.