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

orbit.server.mesh.AddressableManager.kt Maven / Gradle / Ivy

/*
 Copyright (C) 2015 - 2019 Electronic Arts Inc.  All rights reserved.
 This file is part of the Orbit Project .
 See license in LICENSE.
 */

package orbit.server.mesh

import orbit.server.OrbitServerConfig
import orbit.shared.addressable.AddressableLease
import orbit.shared.addressable.AddressableReference
import orbit.shared.exception.PlacementFailedException
import orbit.shared.mesh.Namespace
import orbit.shared.mesh.NodeId
import orbit.shared.mesh.NodeStatus
import orbit.util.misc.attempt
import orbit.util.time.Timestamp
import orbit.util.time.toTimestamp
import java.time.Instant

class AddressableManager(
    private val addressableDirectory: AddressableDirectory,
    private val clusterManager: ClusterManager,
    config: OrbitServerConfig
) {
    private val leaseExpiration = config.addressableLeaseDuration

    suspend fun locateOrPlace(namespace: Namespace, addressableReference: AddressableReference): NodeId =
        addressableDirectory.getOrPut(addressableReference) {
            createNewEntry(namespace, addressableReference)
        }.let {
            val invalidNode = clusterManager.getNode(it.nodeId) == null
            val expired = Timestamp.now() > it.expiresAt
            if (invalidNode || expired) {
                val newEntry = createNewEntry(namespace, addressableReference)
                if (addressableDirectory.compareAndSet(it.reference, it, newEntry)) {
                    newEntry.nodeId
                } else {
                    locateOrPlace(namespace, addressableReference)
                }
            } else {
                it.nodeId
            }
        }

    // TODO: We need to take the expiry time
    suspend fun renewLease(addressableReference: AddressableReference, nodeId: NodeId): AddressableLease =
        addressableDirectory.manipulate(addressableReference) { initialValue ->
            if (initialValue == null || initialValue.nodeId != nodeId || Timestamp.now() > initialValue.expiresAt) {
                throw PlacementFailedException("Could not renew lease for $addressableReference")
            }

            initialValue.copy(
                expiresAt = Instant.now().plus(leaseExpiration.expiresIn).toTimestamp(),
                renewAt = Instant.now().plus(leaseExpiration.renewIn).toTimestamp()
            )
        }!!

    suspend fun abandonLease(key: AddressableReference, nodeId: NodeId): Boolean {
        val currentLease = addressableDirectory.get(key)
        if (currentLease != null && currentLease.nodeId == nodeId && Timestamp.now() <= currentLease.expiresAt) {
            return addressableDirectory.compareAndSet(key, currentLease, null)
        }
        return false
    }

    private suspend fun createNewEntry(namespace: Namespace, addressableReference: AddressableReference) =
        AddressableLease(
            nodeId = place(namespace, addressableReference),
            reference = addressableReference,
            expiresAt = Instant.now().plus(leaseExpiration.expiresIn).toTimestamp(),
            renewAt = Instant.now().plus(leaseExpiration.renewIn).toTimestamp()
        )

    private suspend fun place(namespace: Namespace, addressableReference: AddressableReference): NodeId =
        runCatching {
            attempt(
                maxAttempts = 5,
                initialDelay = 1000
            ) {
                val allNodes = clusterManager.getAllNodes()
                val potentialNodes = allNodes
                    .filter { it.id.namespace == namespace }
                    .filter { it.nodeStatus == NodeStatus.ACTIVE }
                    .filter { it.capabilities.addressableTypes.contains(addressableReference.type) }

                potentialNodes.random().id
            }
        }.fold(
            { it },
            { throw PlacementFailedException("Could not find node capable of hosting $addressableReference") })
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy