Skip to content

Kotlin Actor Public Interface

Interface table of contents · Java Actor · Common Actor Contract

Session–Actor binding §8.2 owns relocation route updates for a bound Session.

Kotlin uses Java's global Actor identity and fluent operation unchanged. ActorId is unique across the whole Location Store transaction domain, UTF-8 encoded size 1..255 bytes. It's case-sensitive and not normalized. A regular send/request only takes ActorId and resolves current authority. ActorRef(actorId, objectGeneration, meshName, nodeRid) is only used to destroy an specified incarnation or bind to a session. objectGeneration is 1..Long.MAX_VALUE, and a decimal string in JSON.

ZLinkKotlinActorManager.create(actorId, actorType) and getOrCreate(actorId, actorType) return ZLinkKotlinActorCreateCall. Actor Model §6.2 owns the single-use rules, duplicate-option handling, and terminal re-invocation errors.

If inMesh is omitted and there's one object-role Mesh, it's auto-selected; with 0, NotConfigured; with two or more, InvalidOperation. If the specified Mesh doesn't exist, NotFound. A placement API taking a target RID or predicate callback isn't provided.

Both await() and yield() of Create and GetOrCreate return ZLinkActorCreateResult. yield() only returns the current Spot gate in a SPOT_WIDE User Spot and Instance Spot application callback. In a different context, it ends with InvalidOperation before reservation, factory execution, and queue change. Actor send only provides a one-way await(): Unit and doesn't provide yield().

Actor type is a stable value of UTF-8 1..255 bytes. If Create finds a Ready object, it's AlreadyExists, and a new attempt returns Java ZLinkActorCreateResult's Created or Rejected. GetOrCreate returns a Ready object of the same type as Existing, without a callback. If Creating, it waits for the authority change, and a CAS loser doesn't start a separate factory or callback. A different operation receives Existing after Ready, competes for a new reservation after cleanup, and doesn't share an earlier application reply. Only a resend of the same source Node RID/lifecycle generation/OperationId reads the correlation-free creation-operation-terminal-v1 envelope and re-encodes the reply with the current correlation/reply route. The terminal is kept for 5 minutes after the original deadline. A callback exception isn't Rejected — it's a typed creation failure. A different type is TypeMismatch. Kotlin doesn't add a local Actor create, directory, resolver, or hidden remote retry.

Kotlin uses Java's ZLinkActorRelocationAdapter<TActor> and factory builder unchanged. The opaque Java byte[] appears as Kotlin ByteArray, and capture and restore's asynchronous completion is CompletionStage. A separate suspending adapter, TState, stateContractId, state class, or ZLinkMessage-based relocation API isn't created. The state-preservation policy is configured with preserveStateWith(ActorAdapter::class.java), and the match between factory and adapter target is validated before socket bind. A policy passing a null adapter class through Java interop is also rejected as a startup configuration error before bind.

An Actor adapter registered with preserveStateWith(...) is used for maintenance cross-node materialization, remote User/Entry Spot join, and each Actor participant of a whole User Spot relocation. It isn't called on a same-node join or on a factory that selected disableRelocation() or recreateOnRelocation(). The ByteArray capture returns has no relocation-adapter-specific size cap. The Java runtime copies it at completion and splits the payload into chunks no larger than relocationPayloadChunkLimitBytes, transferring them directly over the source–target ordered mesh connection. Source memory is the restore origin, and the handoff payload isn't stored in the Relocation Store. The adapter owns the array until completion. Restore receives a fresh defensive copy per call and doesn't keep it after completion. An empty ByteArray is also a valid preserved state. The factory creates a fresh Actor instance per target attempt and doesn't reuse the source or a previous attempt's instance. Restore of the same attempt can be repeated. A capture exception keeps source authority and admission, and a restore exception keeps the target sealed while allowing a retry with the same payload on the same target process. A different target isn't automatically selected. A null stage and null capture payload are contract violations. Host relocation's precommit adapter exception/ contract violation is Blocked/StateIncompatible if a deadline hasn't been fixed yet, and Blocked/DeadlineExceeded once the deadline is fixed. Stale attempt cancellation can't commit a terminal result. Both callbacks are at-least-once and can overlap with a stale attempt, so they must be retry-safe. Inside a Kotlin coroutine, an exception isn't turned into normal completion, and an empty ByteArray isn't returned as a failure fallback.

Kotlin Source Signature

interface ZLinkSuspendingEntrySpotActorSendHandler<
 TEntrySpot : ZLinkEntrySpot<*>, TActor : ZLinkActor, TMessage,
> {
 suspend fun handle(
 entrySpot: TEntrySpot,
 actor: TActor,
 context: ZLinkMessageContext,
 message: TMessage,
 )
}

interface ZLinkSuspendingEntrySpotActorRequestHandler<
 TEntrySpot : ZLinkEntrySpot<*>, TActor : ZLinkActor, TRequest, TReply,
> {
 suspend fun handle(
 entrySpot: TEntrySpot,
 actor: TActor,
 context: ZLinkMessageContext,
 request: TRequest,
 ): TReply
}

interface ZLinkSuspendingSpotActorSendHandler<
 TSpot : ZLinkSpot<*>, TActor : ZLinkActor, TMessage,
> {
 suspend fun handle(
 spot: TSpot,
 actor: TActor,
 context: ZLinkMessageContext,
 message: TMessage,
 )
}

interface ZLinkSuspendingSpotActorRequestHandler<
 TSpot : ZLinkSpot<*>, TActor : ZLinkActor, TRequest, TReply,
> {
 suspend fun handle(
 spot: TSpot,
 actor: TActor,
 context: ZLinkMessageContext,
 request: TRequest,
 ): TReply
}

abstract class ZLinkSuspendingActor : ZLinkActor {
 abstract val context: ZLinkActorContext

 // connects the Java accessor to the same same Context property.
 final override fun context(): ZLinkActorContext = context

 // a final bridge connecting the Java callback to a coroutine.
 final override fun onJoinCompleted(
 completion: ZLinkActorJoinCompletion,
 ): CompletionStage<Void>

 abstract suspend fun onJoinCompletedSuspending(
 completion: ZLinkActorJoinCompletion,
 )
}

abstract class ZLinkSuspendingActorFactory : ZLinkActorFactory {
 protected abstract suspend fun createActor(
 context: ZLinkActorContext,
 ): ZLinkActor
}

interface ZLinkKotlinActorCreateCall {
 fun inMesh(meshName: String): ZLinkKotlinActorCreateCall
 fun request(request: Any): ZLinkKotlinActorCreateCall
 fun timeout(timeout: Duration): ZLinkKotlinActorCreateCall
 suspend fun await(): ZLinkActorCreateResult
 suspend fun yield(): ZLinkActorCreateResult
}

interface ZLinkKotlinActorManager {
 fun create(actorId: String, actorType: String): ZLinkKotlinActorCreateCall
 fun getOrCreate(
 actorId: String,
 actorType: String,
 ): ZLinkKotlinActorCreateCall
 suspend fun destroy(actor: ActorRef): Boolean
}

interface ZLinkKotlinActorClient {
 fun sendToActor(
 actorId: String,
 message: Any,
 ): ZLinkKotlinMessageSendCall

 fun <TReply : Any> requestToActor(
 actorId: String,
 request: Any,
 replyType: KClass<TReply>,
 ): ZLinkKotlinRequestCall<TReply>
}

inline fun <reified TReply : Any> ZLinkKotlinActorClient.requestToActor(
 actorId: String,
 request: Any,
): ZLinkKotlinRequestCall<TReply> =
 requestToActor(actorId, request, TReply::class)

interface ZLinkKotlinWorkerCall<T> {
 suspend fun await(): T
 suspend fun yield(): T
}

Generated JVM Signature

public abstract class systems.zlink.framework.kotlin.ZLinkSuspendingActorFactory implements systems.zlink.framework.actors.ZLinkActorFactory {
 public systems.zlink.framework.kotlin.ZLinkSuspendingActorFactory();
 public final java.util.concurrent.CompletionStage<systems.zlink.framework.actors.ZLinkActor> create(systems.zlink.framework.actors.ZLinkActorContext);
}
public abstract class systems.zlink.framework.kotlin.ZLinkSuspendingActor implements systems.zlink.framework.actors.ZLinkActor {
 public abstract systems.zlink.framework.actors.ZLinkActorContext getContext();
 public final systems.zlink.framework.actors.ZLinkActorContext context();
 public final java.util.concurrent.CompletionStage<java.lang.Void> onJoinCompleted(systems.zlink.framework.actors.ZLinkActorJoinCompletion);
 public abstract java.lang.Object onJoinCompletedSuspending(systems.zlink.framework.actors.ZLinkActorJoinCompletion, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
public interface systems.zlink.framework.kotlin.ZLinkSuspendingEntrySpotActorSendHandler<TEntrySpot extends systems.zlink.framework.spots.ZLinkEntrySpot<?>, TActor extends systems.zlink.framework.actors.ZLinkActor, TMessage> {
 public abstract java.lang.Object handle(TEntrySpot, TActor, systems.zlink.framework.ZLinkMessageContext, TMessage, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
public interface systems.zlink.framework.kotlin.ZLinkSuspendingEntrySpotActorRequestHandler<TEntrySpot extends systems.zlink.framework.spots.ZLinkEntrySpot<?>, TActor extends systems.zlink.framework.actors.ZLinkActor, TRequest, TReply> {
 public abstract java.lang.Object handle(TEntrySpot, TActor, systems.zlink.framework.ZLinkMessageContext, TRequest, kotlin.coroutines.Continuation<? super TReply>);
}
public interface systems.zlink.framework.kotlin.ZLinkSuspendingSpotActorSendHandler<TSpot extends systems.zlink.framework.spots.ZLinkSpot<?>, TActor extends systems.zlink.framework.actors.ZLinkActor, TMessage> {
 public abstract java.lang.Object handle(TSpot, TActor, systems.zlink.framework.ZLinkMessageContext, TMessage, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
public interface systems.zlink.framework.kotlin.ZLinkSuspendingSpotActorRequestHandler<TSpot extends systems.zlink.framework.spots.ZLinkSpot<?>, TActor extends systems.zlink.framework.actors.ZLinkActor, TRequest, TReply> {
 public abstract java.lang.Object handle(TSpot, TActor, systems.zlink.framework.ZLinkMessageContext, TRequest, kotlin.coroutines.Continuation<? super TReply>);
}
public interface systems.zlink.framework.kotlin.ZLinkKotlinActorCreateCall {
 public abstract systems.zlink.framework.kotlin.ZLinkKotlinActorCreateCall inMesh(java.lang.String);
 public abstract systems.zlink.framework.kotlin.ZLinkKotlinActorCreateCall request(java.lang.Object);
 public abstract systems.zlink.framework.kotlin.ZLinkKotlinActorCreateCall timeout-LRDsOJo(long);
 public abstract java.lang.Object await(kotlin.coroutines.Continuation<? super systems.zlink.framework.actors.ZLinkActorCreateResult>);
 public abstract java.lang.Object yield(kotlin.coroutines.Continuation<? super systems.zlink.framework.actors.ZLinkActorCreateResult>);
}
public interface systems.zlink.framework.kotlin.ZLinkKotlinWorkerCall<T> {
 public abstract java.lang.Object await(kotlin.coroutines.Continuation<? super T>);
 public abstract java.lang.Object yield(kotlin.coroutines.Continuation<? super T>);
}
public interface systems.zlink.framework.kotlin.ZLinkKotlinActorManager {
 public abstract systems.zlink.framework.kotlin.ZLinkKotlinActorCreateCall create(java.lang.String, java.lang.String);
 public abstract systems.zlink.framework.kotlin.ZLinkKotlinActorCreateCall getOrCreate(java.lang.String, java.lang.String);
}
public interface systems.zlink.framework.kotlin.ZLinkKotlinActorClient {
 public abstract systems.zlink.framework.kotlin.ZLinkKotlinMessageSendCall sendToActor(java.lang.String, java.lang.Object);
 public abstract <TReply> systems.zlink.framework.kotlin.ZLinkKotlinRequestCall<TReply> requestToActor(java.lang.String, java.lang.Object, kotlin.reflect.KClass<TReply>);
}

The factory callback must call one of disableRelocation(), recreateOnRelocation(), preserveStateWith(...). Kotlin doesn't generate a reified helper for state-preservation configuration and adapter registration, or an overload/default argument that omits the policy. The only public operations taking an ActorRef are destroy and session bind. A missing specified ref is false, a generation mismatch is InvalidOperation, and a sealed handoff window is Unavailable.

A coroutine terminal isn't added to Actor Join. The Java interface's synchronous defer() is called once during handler execution, and doesn't return the Spot gate or Actor FIFO claim. The request/worker/create wrapper's yield() keeps the Actor FIFO claim on a SPOT_WIDE User Spot member Actor and only returns the User Spot gate. On an Entry Actor and a PER_ACTOR Actor, it completes with InvalidOperation before the underlying Java operation submission. An awaited request the same Actor sends to itself is also rejected before suspending the coroutine or changing the queue. A SPOT_WIDE member Actor's Join leaving the current User Spot is also registered with defer() and runs after the handler's last continuation. The callback isn't called inline or in a re-entrant way.

defer() only registers an immutable Join intent and an inactive barrier, without a target lookup or Store I/O. If the handler fails, the barrier is discarded, and the result after a normal finish is received in onJoinCompletedSuspending(...). The overload with no request fixes an empty ZLinkMessage. The default timeout is 5 seconds, and an explicit value is a finite 1..Int.MAX_VALUE ms rounded up to milliseconds. The monotonic absolute deadline is fixed at the moment defer() is called.

The completion operation ID is an idempotency ID distinct from RelocationId, reservation ID, or aggregate commit ID. Same-node and cross-node completion retry are limited to the current source and target process lifetime. After the process ends, a different runtime doesn't automatically replay completion.