All Downloads are FREE. Search and download functionalities are using the official Maven repository.

orbit.client.mesh.NodeLeaser.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0a19
Show newest version
/*
 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