콘텐츠로 이동

Kotlin 구성과 host 공개 인터페이스

인터페이스 목차 · Java 구성 · MeshNode 공통 계약

Kotlin application은 Java builder를 직접 사용한다. Kotlin DSL은 receiver와 reified type으로 실제 중복을 줄이는 경우에만 제공하며 Java contract에 없는 역할, factory default, allocation provider를 만들지 않는다. 따라서 ClientServer의 Client-only connect와 Server RID·lifecycle generation별 intent 통합, fanout의 Subscriber-only connect와 automatic·manual subscriber 혼합 금지는 Java 구성의 같은 계약을 그대로 적용한다. 같은 ClientServer ChannelName에는 Java builder의 client()server()를 각각 한 번 등록할 수 있으며 별도 Kotlin DSL이나 public API를 추가하지 않는다. 두 역할은 (ChannelName, Role) key의 별도 registration으로 하나의 topology를 공유하고 같은 역할의 중복은 startup 오류다. Local Server도 remote Server와 같은 readiness·weight·drain 조건으로 선택하며 local 우선순위나 handler 직접 호출을 사용하지 않는다.

Automatic RouteMesh는 RID를 canonical byte order로 비교하고 더 작은 RID의 MeshNode만 상대 endpoint로 connect한다. Manual topology는 application endpoint 구성에 따라 한쪽 또는 양쪽에서 connect할 수 있다. 양쪽 연결이나 automatic discovery 경합·오래된 snapshot으로 중복 후보가 생기면 handshake와 admission이 같은 RID와 lifecycle generation을 확인해 하나만 ready 상태로 유지한다.

두 MeshNode가 모두 Object Client이고 양쪽 모두 RouteMesh Channel Server membership이 없을 때만 peer connection이 필요하지 않다. Channel Client membership만 등록한 경우도 같다. 어느 한쪽에라도 weight 0을 포함한 Channel Server membership이 있으면 연결이 필요하다. ClientServer와 classic fanout은 별도 물리 topology이므로 이 판정에 포함하지 않는다.

MeshNode의 object role은 None, Client, Server 중 하나다. objects()를 호출하지 않으면 None, client()는 outbound manager와 resolve를 제공하고 server()는 Client 기능과 factory·Entry registration을 함께 제공한다. Client와 Server는 Location Store가 필요하다. None에는 object manager나 factory가 없다. 한 node에서 role을 중복 선택하면 startup configuration error다. Object Client에도 RouteMesh Channel Server를 등록할 수 있지만 application Node direct handler는 등록할 수 없다. Object Client RID를 Node direct target으로 지정하면 다른 RID로 바꾸지 않고 not-found로 끝낸다.

ZLinkFrameworkOptions.addLocationStore(...)addRelocationStore(...)는 Java public member를 그대로 사용한다. recreateOnRelocation() 또는 preserveStateWith(...)를 선택한 factory가 하나라도 있거나 Instance Spot factory가 하나라도 있으면 Relocation Store를 정확히 하나 등록해야 한다. Missing·duplicate registration은 socket bind 전에 configuration error다. Instance Spot factory가 없고 disableRelocation()만 선택한 same-node 구성에는 Relocation Store가 필수가 아니다. 두 capability를 묶는 Kotlin DSL이나 Redis 전용 registration helper는 제공하지 않는다. Cross-node Actor·Spot 이동의 application state·queue·timer handoff payload는 Relocation Store에 저장하지 않는다. Source가 payload를 memory에 유지한 채 source–target ordered mesh 연결로 직접 chunk 전송하며, source memory가 복원 원본이다. Relocation Store는 Instance Spot cold activation 기록과 relocation 뒤 완료되는 pending request의 terminal 기록을 계속 소유하므로 위 등록 요구는 유지된다. Same-node Actor join은 relocation payload를 만들지 않고, disableRelocation()을 선택한 cross-node 이동은 capture 전에 거부한다.

다음 Java builder member는 Kotlin에서 property 변환 없이 같은 JVM signature로 직접 호출한다.

public enum systems.zlink.framework.configuration.ZLinkCoreHwmProfile {
 COMPACT,
 LOW_LATENCY,
 BALANCED,
 THROUGHPUT
}
public enum systems.zlink.framework.configuration.ZLinkApplicationJobQueueProfile {
 COMPACT,
 LOW_LATENCY,
 BALANCED,
 THROUGHPUT
}
public interface systems.zlink.framework.configuration.ZLinkDispatchOptions {
 public abstract systems.zlink.framework.configuration.ZLinkUnhandledDispatchOptions unhandled();
 public abstract systems.zlink.framework.configuration.ZLinkDiagnosticsOptions diagnostics();
 public abstract systems.zlink.framework.configuration.ZLinkDispatchOptions messageFlow(systems.zlink.framework.configuration.ZLinkMessageFlowLogMode);
 public abstract systems.zlink.framework.configuration.ZLinkDispatchOptions traceSampleRate(double);
 public abstract systems.zlink.framework.configuration.ZLinkDispatchOptions includeMessageSizes(boolean);
}
public interface systems.zlink.framework.configuration.ZLinkInboundDispatchOptions {
 public abstract java.util.OptionalLong coreHwmMemoryLimitBytes();
 public abstract void setCoreHwmMemoryLimitBytes(long);
 public abstract java.util.OptionalLong coreHwmBudgetBytes();
 public abstract void setCoreHwmBudgetBytes(long);
 public abstract systems.zlink.framework.configuration.ZLinkCoreHwmProfile coreHwmProfile();
 public abstract void setCoreHwmProfile(systems.zlink.framework.configuration.ZLinkCoreHwmProfile);
 public abstract systems.zlink.framework.configuration.ZLinkApplicationJobQueueProfile applicationJobQueueProfile();
 public abstract void setApplicationJobQueueProfile(systems.zlink.framework.configuration.ZLinkApplicationJobQueueProfile);
 public abstract java.util.OptionalLong maxQueuedApplicationJobs();
 public abstract void setMaxQueuedApplicationJobs(long);
 public abstract int applicationJobQueuePauseThresholdPercent();
 public abstract void setApplicationJobQueuePauseThresholdPercent(int);
 public abstract int applicationJobQueueResumeThresholdPercent();
 public abstract void setApplicationJobQueueResumeThresholdPercent(int);
}
public interface systems.zlink.framework.locations.ZLinkLocationOptions {
 public abstract java.time.Duration ownerLeaseRenewInterval();
 public abstract void setOwnerLeaseRenewInterval(java.time.Duration);
 public abstract java.time.Duration ownerLeaseTtl();
 public abstract void setOwnerLeaseTtl(java.time.Duration);
 public abstract java.time.Duration pollingInterval();
 public abstract void setPollingInterval(java.time.Duration);
 public abstract java.time.Duration storeFailureGrace();
 public abstract void setStoreFailureGrace(java.time.Duration);
 public abstract java.time.Duration ownerLeaseFencingMargin();
 public abstract void setOwnerLeaseFencingMargin(java.time.Duration);
 public abstract java.time.Duration ownerLeaseRenewTimeout();
 public abstract void setOwnerLeaseRenewTimeout(java.time.Duration);
 public abstract java.time.Duration routeCacheMaxAge();
 public abstract void setRouteCacheMaxAge(java.time.Duration);
 public abstract java.time.Duration messageFollowDuration();
 public abstract void setMessageFollowDuration(java.time.Duration);
 public abstract java.time.Duration sessionRelocationSealTimeout();
 public abstract void setSessionRelocationSealTimeout(java.time.Duration);
 public abstract long relocationPayloadChunkLimitBytes();
 public abstract void setRelocationPayloadChunkLimitBytes(long);
 public abstract long relocationInFlightPayloadBudgetBytes();
 public abstract void setRelocationInFlightPayloadBudgetBytes(long);
 public abstract long relocationNodeInFlightPayloadBudgetBytes();
 public abstract void setRelocationNodeInFlightPayloadBudgetBytes(long);
 public abstract java.time.Duration relocationCutoverWaitTimeout();
 public abstract void setRelocationCutoverWaitTimeout(java.time.Duration);
}
public interface systems.zlink.framework.configuration.ZLinkMeshNodeBuilder {
 public abstract systems.zlink.framework.configuration.ZLinkMeshNodeBuilder setRoutingIdPrefix(java.lang.String);
 public abstract systems.zlink.framework.configuration.ZLinkMeshNodeBuilder setPlacementWeight(int);
 public abstract systems.zlink.framework.configuration.ZLinkMeshNodeBuilder setActorCapacity(int);
 public abstract systems.zlink.framework.configuration.ZLinkMeshNodeBuilder setSpotCapacity(int);
 public abstract systems.zlink.framework.configuration.ZLinkMeshNodeBuilder setActivationConcurrency(int);
 public abstract systems.zlink.framework.configuration.ZLinkMeshObjectRoleBuilder objects();
}
public interface systems.zlink.framework.configuration.ZLinkMeshObjectRoleBuilder {
 public abstract systems.zlink.framework.configuration.ZLinkMeshObjectClientBuilder client();
 public abstract systems.zlink.framework.configuration.ZLinkMeshObjectServerBuilder server();
}
public interface systems.zlink.framework.configuration.ZLinkStreamNodeBuilder {
 public abstract systems.zlink.framework.configuration.ZLinkStreamSocketConfig configureSocket();
 public abstract systems.zlink.framework.configuration.ZLinkStreamNodeBuilder enableActorDispatch();
}
public interface systems.zlink.framework.configuration.ZLinkStreamSocketConfig {
 public abstract long maxMessageSize();
 public abstract void setMaxMessageSize(long);
}

Core HWM과 application job queue 구성은 options.configureInboundDispatch()가 반환하는 ZLinkInboundDispatchOptions에서 직접 설정한다. configureDispatch { ... }는 diagnostics DSL이며 HWM이나 job queue 설정을 전달하지 않는다.

sessionRelocationSealTimeout()은 Java와 같은 startup-only 양수 Duration이고 기본값은 3초다. Millisecond 변환 불가, 0, 음수와 무한대는 socket bind 전에 configuration error다.

relocationPayloadChunkLimitBytes(), relocationInFlightPayloadBudgetBytes(), relocationNodeInFlightPayloadBudgetBytes()relocationCutoverWaitTimeout()도 Java 공개 계약을 그대로 사용한다. Chunk limit은 relocation payload를 나눈 encoded chunk 하나의 최대 크기(byte)로 기본값 256 KiB이며 transport가 협상한 frame 한도를 넘게 설정하면 socket bind 전에 startup configuration error다. In-flight budget은 peer 연결당 동시 전송 chunk byte 합계 상한으로 기본값 16 MiB, 0은 미적용이다. Node in-flight budget은 같은 규칙의 node 전체 합계이고 기본값 0은 미적용이다. Cutover wait timeout은 target의 cutover 대기이자 source의 재전송용 boundary batch 사본 유지 시간으로 기본값 1초다. 네 값 모두 startup-only이며 음수는 socket bind 전에 configuration error다.

Kotlin은 Java ZLinkStreamNodeBuilder.configureSocket()ZLinkStreamSocketConfig.setMaxMessageSize(...)를 그대로 사용한다. 기본값은 64 KiB이며, StreamNode의 Core STREAM inbound에서 client→server complete message에만 적용한다. 크기는 6-byte prefix를 제외한 header와 payload의 합이다. 0은 Core -1로 변환되어 Framework 상한을 사용하지 않고, 음수는 startup configuration error다. 상한을 넘은 message는 handler에 일부도 전달하지 않으며 server는 EMSGSIZE와 진단 trace를 남기고 연결을 종료한다. raw client는 별도 wire error code가 아니라 연결 종료를 관찰한다. server→client outbound에는 상한을 적용하지 않으며 ClientServer와 RouteMesh SS에는 이 설정을 추가하지 않는다.

Kotlin binding은 Java runtime의 양수 유한 Runtime.maxMemory()를 Core runtime memory hint로 전달한다. Core profile과 Application job queue profile은 Java 공개 계약의 독립된 enum과 계산을 그대로 사용한다. 두 profile의 기본값은 BALANCED이고 pressure threshold 기본값은 pause 80, resume 60이다. Pause는 1..100, resume은 0..99의 정수이며 resume은 pause보다 작아야 한다. Manual job cap, startup CPU snapshot과 bind 전 범위·순서·overflow 검증도 Java 공개 계약과 같다.

Kotlin source signature

fun ZLinkFrameworkOptions.useCoroutineHandlers(dispatcher: CoroutineDispatcher)
fun ZLinkFrameworkOptions.useCoroutineHandlers(
 scope: CoroutineScope,
 dispatcher: CoroutineDispatcher,
)

inline fun ZLinkFrameworkOptions.configureDispatch(
 block: ZLinkDispatchOptions.() -> Unit,
): ZLinkDispatchOptions

fun ZLinkFrameworkOptions.configureStreamCompression(
 configure: ZLinkStreamCompressionBuilder.() -> Unit,
): ZLinkFrameworkOptions

inline fun <reified TActor, reified TFactory>
 ZLinkMeshObjectServerBuilder.actorFactory(
 actorType: String,
 noinline configure: ZLinkActorFactoryBuilder<TActor>.() -> Unit,
 ): ZLinkMeshObjectServerBuilder
 where TActor : ZLinkActor,
 TFactory : ZLinkActorFactory

Factory configure callback에는 default가 없다. Actor factory builder에는 relocation 동작 선택 외의 설정이 없다. Node placement weight는 0..10000이고 기본값은 100이다. 범위 밖 값은 startup 설정과 runtime 변경에서 configuration error다. Channel weight와 별개이며 runtime update와 descriptor snapshot에 같은 값을 사용한다. RouteMesh Channel Server와 ClientServer Server weight도 같은 범위와 기본값을 사용한다. Weighted selection은 후보 weight 합계를 최소 64-bit 정수로 계산한다.

MeshNode와 Store-backed fanout publisher의 automatic RID는 prefix-<lowercase-canonical-uuid-v4> 형식이다. UUID v4는 8-4-4-4-12 자리의 lowercase canonical 문자열로 표현한다. Prefix는 ASCII [A-Za-z0-9._-] 1..64자이고 full RID는 UTF-8 255 bytes 이하다. Active owner와 충돌하면 새 UUID로 다시 시도하지 않고 즉시 RoutingIdConflict로 실패한다. Fixed RID는 automatic discovery topology에서도, object role이 있는 MeshNode에서도 허용한다. 구현·시험 시나리오가 peer를 이름으로 지목해야 할 때가 있고, 자동 UUID로는 그럴 수 없기 때문이다. Fixed RID를 쓴 node가 재시작해 이전 active owner claim과 충돌하면 자동 RID와 같은 규칙으로 처리한다 — 새 값을 만들어 재시도하지 않고 즉시 conflict로 실패하며, 이전 owner lease가 만료된 뒤 재시작이 성공한다. Slot count, allocation group과 public allocation provider는 없다.

Object Server의 Entry Spot ID는 같은 prefix의 <prefix>-entry-<lowercase-canonical-uuid-v4> 형식이며 MeshNode와 별도로 생성한 UUID v4를 사용한다. Java ZLinkMeshNodeDescriptor.entrySpotId()가 같은 lifecycle의 mapping을 제공한다. Global Spot ID가 active owner와 충돌하면 새 UUID로 다시 시도하지 않고 즉시 SpotIdConflict로 startup을 실패시킨다. Caller가 지정한 User·Instance Spot ID가 예약 형식과 일치하면 Store와 factory 전에 startup configuration error로 거부한다.

모든 factory는 Java builder를 Kotlin receiver callback으로 구성한다. Callback은 disableRelocation(), recreateOnRelocation(), preserveStateWith(...) 중 정확히 하나를 호출한다. 누락하거나 둘 이상 호출하면 socket bind 전에 startup configuration error다. Kotlin 전용 policy value와 suspending adapter는 추가하지 않는다. 별도 등록 API는 없다.

Framework는 receiver callback을 등록 호출 안에서 동기적으로 한 번만 실행한다. Callback이 반환된 뒤 보관한 builder를 다시 호출하면 configuration error다. Callback이 예외를 던지면 해당 factory를 등록하지 않고 같은 예외를 호출자에게 전달한다. stableTypeLimit(...)을 생략하면 node limit을 공유하며 명시한 값은 1..Int.MAX_VALUE여야 한다. 0과 음수는 callback 실행 중 configuration error다.

recreateOnRelocation() 또는 preserveStateWith(...)를 선택한 factory가 하나라도 있거나 Instance Spot factory가 하나라도 등록된 Object Server는 Java root의 addRelocationStore(...)로 Relocation Store를 정확히 하나 등록한다. Instance Spot factory가 없고 모든 factory가 disableRelocation()을 선택한 same-node 구성만 이를 생략할 수 있다.

generated JVM signature

public final class systems.zlink.framework.kotlin.ZLinkCoroutineHandlerOptionsKt {
 public static final void useCoroutineHandlers(systems.zlink.framework.configuration.ZLinkFrameworkOptions, kotlinx.coroutines.CoroutineDispatcher);
 public static final void useCoroutineHandlers(systems.zlink.framework.configuration.ZLinkFrameworkOptions, kotlinx.coroutines.CoroutineScope, kotlinx.coroutines.CoroutineDispatcher);
}
public final class systems.zlink.framework.kotlin.ZLinkDispatchOptionsExtensionsKt {
 public static final systems.zlink.framework.configuration.ZLinkDispatchOptions configureDispatch(systems.zlink.framework.configuration.ZLinkFrameworkOptions, kotlin.jvm.functions.Function1<? super systems.zlink.framework.configuration.ZLinkDispatchOptions, kotlin.Unit>);
}
public final class systems.zlink.framework.kotlin.ZLinkFrameworkExtensionsKt {
 public static final <TActor extends systems.zlink.framework.actors.ZLinkActor, TFactory extends systems.zlink.framework.actors.ZLinkActorFactory> systems.zlink.framework.configuration.ZLinkMeshObjectServerBuilder actorFactory(systems.zlink.framework.configuration.ZLinkMeshObjectServerBuilder, java.lang.String, kotlin.jvm.functions.Function1<? super systems.zlink.framework.configuration.ZLinkActorFactoryBuilder<TActor>, kotlin.Unit>);
 public static final systems.zlink.framework.configuration.ZLinkFrameworkOptions configureStreamCompression(systems.zlink.framework.configuration.ZLinkFrameworkOptions, kotlin.jvm.functions.Function1<? super systems.zlink.framework.configuration.ZLinkStreamCompressionBuilder, kotlin.Unit>);
}