O
O
Orbit
Search…
addressables
In Orbit, an addressable is an object that interacts with the world through asynchronous messages. Simply, it has an address and can receive messages, even remotely.
Orbit will activate an addressable when it receives a message, and deactivate it when after a configurable period of inactivity. This patterns allows developers to speed up interactions and reduce load on databases and external services.
Orbit guarantees that only one addressable with a given identity can be active at any time in the cluster. As such, developers do not need to be concerned about keeping multiple activations/instances of an addressable synchronized with one another.
Orbit also guarantees that calls to addressables can never be processed in parallel, meaning developers do not need to worry about concurrent access to an addressable. Two calls to an addressable can not be processed in parallel.

Addressable Interfaces

Before you can implement an addressable you must create an interface for it.

Using a suspend method

1
package sample.addressables
2
3
import orbit.client.addressable.Addresable
4
5
interface Greeter : Addressable {
6
suspend fun hello(message: String): String
7
}
Copied!

Using a Kotlin future

1
package sample.addressables
2
3
import kotlinx.coroutines.Deferred
4
import orbit.client.addressable.Addresable
5
6
interface Greeter : Addressable {
7
fun hello(message: String): Deferred<String>
8
}
Copied!

Asynchronous Return Types

Addressables must only contain methods which return asynchronous types, like futures, or be Kotlin suspending methods. The following return types (and their subtypes) are currently supported.
Main Type
Common Subtypes
For
Deferred
Kotlin
Suspend
N/A
Kotlin

Concrete Implementation

Once you have created an Addressable interface, you must offer an implementation for Orbit to use. For each addressable interface, exactly one addressable class must implement it.

Using suspending methods

1
package sample.addressables
2
3
import orbit.client.addressables.AbstractAddressable
4
import sample.addressables.Greeter
5
6
class GreeterImpl() : AbstractAddressable(), Greeter {
7
{
8
override suspend fun hello(message: String): String {
9
return "Message: ${message}"
10
}
11
}
Copied!

Using a Kotlin future

1
package sample.addressables
2
3
import kotlinx.coroutines.Deferred
4
import kotlinx.coroutines.CompletableDeferred
5
import orbit.client.addressables.AbstractAddressable
6
import sample.addressables.Greeter
7
8
class GreeterImpl() : AbstractAddressable(), Greeter {
9
{
10
override fun hello(message: String): Deferred<String> {
11
return CompletableDeferred("Message: ${message}")
12
}
13
}
Copied!

Lifetime

When a message is sent to a given addressable reference, the framework will perform the following actions:
  1. 1.
    Check if the addressable is already activated in the cluster
    • If so, forward the message to the client
    • If not, proceed to step 2
  2. 2.
    Activate the addressable for the addressable type and place on a connected client
  3. 3.
    Call the OnActivate hook to initialize any state, such as restoring from persistence
  4. 4.
    Forward the message to the addressable
Orbit does not provide any default persistence functionality, but the @OnActivate hook is available to restore state from a store before it receives the message.
1
class GreeterImpl() : AbstractAddressable(), Game {
2
{
3
@OnActivate
4
suspend fun onActivate() {
5
loadFromStore()
6
}
7
8
@OnDeactivate
9
suspend fun onDeactivate(deactivationReason: DeactivationReason) {
10
saveToStore()
11
}
12
}
Copied!
Addressables can be persisted at any time, but the @OnDeactivate hook can help assure the latest state is saved, except in the event of an hard shutdown. During graceful shutdown, every actor is deactivated giving it a final chance to persist. Activate and Deactivate methods can similarly utilize a future by returning a Deferred<Unit>.

Execution Model

Addressables in Orbit can have multiple different execution modes. By default, addressables will use Safe Execution Mode.

Safe Execution Mode

In safe execution mode Orbit guarantees that calls to addressables can never be processed in parallel. This means that if two clients call an addressable at the same time, they are guaranteed to be processed serially (one after the other) where one call completes before the next one starts.
Orbit guarantees "at most once" delivery, meaning that best attempts will be made to deliver a message one time, but will deliver an error upon failure to deliver.

Context

If an Addressable extends the AbstractAddressable or AbstractActor class, it will have access to an Orbit-managed context object. The AddressableContext provides access to the AddressableReference (identifier) and the OrbitClient instance. The AddressableReference can be used during the @OnActivate hook to identify which addressable needs to be restored from persistence.
1
abstract class AbstractAddressable {
2
lateinit var context: AddressableContext
3
}
4
5
data class AddressableContext(
6
val reference: AddressableReference,
7
val client: OrbitClient
8
)
Copied!
For example, if the addressable is an Actor with a String-type key,
1
interface IdentityActor : ActorWithStringKey {
2
suspend fun identity(): String
3
}
4
5
class IdentityActorImpl() : IdentityActor, AbstractActor() {
6
override suspend fun identity(): String {
7
val id = context.reference
8
return context.actorFactory.createProxy<IdentityResolver>().resolve(id)
9
}
10
}
Copied!

Keys

Every addressable in Orbit has a unique key of one of the following types which identifies it.
Orbit Type
JDK Type
NoKey
N/A
StringKey
String
Int32Key
Integer
Int64Key
Long
Last modified 1yr ago