
orbit.client.mesh.NodeLeaser.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orbit-client Show documentation
Show all versions of orbit-client Show documentation
Orbit is a system to make building highly scalable realtime services easier.
/*
Copyright (C) 2015 - 2019 Electronic Arts Inc. All rights reserved.
This file is part of the Orbit Project .
See license in LICENSE.
*/
package orbit.client.mesh
import io.grpc.Deadline
import kotlinx.coroutines.guava.await
import mu.KotlinLogging
import orbit.client.OrbitClientConfig
import orbit.client.net.GrpcClient
import orbit.client.net.LocalNode
import orbit.shared.proto.NodeManagementGrpc
import orbit.shared.proto.NodeManagementOuterClass
import orbit.shared.proto.toCapabilitiesProto
import orbit.shared.proto.toNodeInfo
import orbit.util.time.Clock
import java.util.concurrent.TimeUnit
internal class NodeLeaser(
private val localNode: LocalNode,
grpcClient: GrpcClient,
config: OrbitClientConfig,
private val clock: Clock
) {
private val logger = KotlinLogging.logger { }
private val joinTimeout = config.joinClusterTimeout
private val leaveTimeout = config.leaveClusterTimeout
private val nodeManagementStub = NodeManagementGrpc.newFutureStub(grpcClient.channel)
suspend fun joinCluster() {
logger.info("Joining namespace '${localNode.status.namespace}' in the '${localNode.status.grpcEndpoint}' cluster ...")
nodeManagementStub
.withWaitForReady()
.withDeadline(Deadline.after(joinTimeout.toMillis(), TimeUnit.MILLISECONDS))
.joinCluster(
NodeManagementOuterClass.JoinClusterRequestProto.newBuilder()
.setCapabilities(localNode.status.capabilities?.toCapabilitiesProto())
.build()
).await()
.also { responseProto ->
if (responseProto.status != NodeManagementOuterClass.NodeLeaseResponseProto.Status.OK) {
throw NodeLeaseRenewalFailed("Joining cluster failed")
}
responseProto.info.toNodeInfo().also { nodeInfo ->
localNode.manipulate {
it.copy(nodeInfo = nodeInfo)
}
logger.info("Joined cluster as node '${nodeInfo.id}'.")
}
}
}
suspend fun renewLease(force: Boolean) {
localNode.status.nodeInfo?.let { existingInfo ->
val existingLease = existingInfo.lease
if (force || clock.inPast(existingLease.renewAt)) {
logger.debug("Renewing lease...")
val renewalResult = nodeManagementStub.renewLease(
NodeManagementOuterClass.RenewNodeLeaseRequestProto.newBuilder()
.setChallengeToken(existingLease.challengeToken)
.setCapabilities(localNode.status.capabilities?.toCapabilitiesProto())
.build()
).await()
if (renewalResult.status != NodeManagementOuterClass.NodeLeaseResponseProto.Status.OK) {
throw NodeLeaseRenewalFailed("Node renewal failed")
}
localNode.manipulate {
it.copy(nodeInfo = renewalResult.info.toNodeInfo())
}
logger.debug("Lease renewed.")
}
}
}
suspend fun leaveCluster() {
logger.info("Leaving namespace '${localNode.status.namespace}' cluster ...")
nodeManagementStub
.withWaitForReady()
.withDeadline(Deadline.after(leaveTimeout.toMillis(), TimeUnit.MILLISECONDS))
.leaveCluster(NodeManagementOuterClass.LeaveClusterRequestProto.newBuilder().build()).await()
.also { responseProto ->
responseProto.info.toNodeInfo().also { nodeInfo ->
localNode.manipulate {
it.copy(nodeInfo = nodeInfo)
}
logger.info("Left cluster")
}
}
}
suspend fun tick() {
renewLease(false)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy