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

ai.platon.pulsar.protocol.browser.driver.ConcurrentStatefulDriverPoolPool.kt Maven / Gradle / Ivy

The newest version!
package ai.platon.pulsar.protocol.browser.driver

import ai.platon.pulsar.common.getLogger
import ai.platon.pulsar.common.warnForClose
import ai.platon.pulsar.skeleton.crawl.fetch.driver.WebDriver
import ai.platon.pulsar.skeleton.crawl.fetch.privacy.BrowserId
import com.google.common.annotations.Beta
import java.util.concurrent.ConcurrentSkipListMap
import java.util.concurrent.ConcurrentSkipListSet

/**
 * A concurrent pool of loading driver pools whose states are tracked.
 * */
class ConcurrentStatefulDriverPoolPool {
    private val logger = getLogger(this)
    /**
     * Working driver pools
     * */
    private val _workingDriverPools = ConcurrentSkipListMap()

    /**
     * Retired but not closed driver pools
     * */
    private val _retiredDriverPools = ConcurrentSkipListMap()
    /**
     * Closed driver pool ids
     * */
    private val _closedDriverPools = ConcurrentSkipListSet()

    val workingDriverPools: Map get() = _workingDriverPools

    val retiredDriverPools: Map get() = _retiredDriverPools

    val closedDriverPools: Set get() = _closedDriverPools
    /**
     * Return the number of new drivers can offer by the pool at the calling time point.
     *
     * If the driver pool is retired or closed, no new driver can offer.
     * If the driver pool is not created yet, it can offer [capacity] drivers at top.
     *
     * @param browserId The id of the webdriver pool.
     * @param capacity The capacity of a driver pool.
     * @return The number of new drivers can offer by the pool at the calling time point.
     * */
    @Synchronized
    fun promisedDriverCount(browserId: BrowserId, capacity: Int): Int {
        if (browserId in closedDriverPools || browserId in retiredDriverPools) {
            return 0
        }

        // if the
        val pool = _workingDriverPools[browserId] ?: return capacity
        return pool.numAvailable
    }
    /**
     * Check if a webdriver pool is full capacity, so it can not provide a webdriver for new tasks.
     * Note that if a driver pool is retired or closed, it's not full capacity.
     *
     * @param browserId The id of the webdriver pool.
     * @return True if the webdriver pool is full capacity, false otherwise.
     * */
    @Synchronized
    fun isFullCapacity(browserId: BrowserId): Boolean {
        if (browserId in closedDriverPools || browserId in retiredDriverPools) {
            return false
        }

        val pool = _workingDriverPools[browserId] ?: return false
        return pool.numWorking + pool.numWaiting >= pool.capacity
    }

    @Synchronized
    fun isRetiredPool(browserId: BrowserId) = retiredDriverPools.contains(browserId)

    @Beta
    @Synchronized
    fun subscribeDriver(browserId: BrowserId): WebDriver? {
        if (browserId in closedDriverPools || browserId in retiredDriverPools) {
            return null
        }

        val pool = _workingDriverPools[browserId] ?: return null
        val driver = pool.poll()
        // _subscribedDrivers.add(driver)
        return driver
    }

    @Beta
    @Synchronized
    fun subscribeDriver(): WebDriver? {
        val driverPool = _workingDriverPools.values.firstOrNull { it.numAvailable > 0 } ?: return null
        val driver = driverPool.poll()
        // _subscribedDrivers.add(driver)
        return driver
    }

    @Synchronized
    fun computeIfAbsent(browserId: BrowserId, mappingFunction: (BrowserId) -> LoadingWebDriverPool): LoadingWebDriverPool {
        return _workingDriverPools.computeIfAbsent(browserId, mappingFunction)
    }

    @Synchronized
    fun retire(driverPool: LoadingWebDriverPool) {
        val browserId = driverPool.browserId
        _workingDriverPools.remove(browserId)

        if (browserId in _closedDriverPools) {
            logger.warn("Inconsistent driver pool state: retire pool who is already closed | {}", browserId)
        }

        driverPool.retire()
        _retiredDriverPools[browserId] = driverPool
    }

    @Synchronized
    fun retire(browserId: BrowserId): LoadingWebDriverPool? {
        val retiredDriverPool = _workingDriverPools.remove(browserId)
        if (retiredDriverPool != null) {
            retire(retiredDriverPool)
        }
        return retiredDriverPool
    }

    @Synchronized
    fun close(driverPool: LoadingWebDriverPool) {
        val browserId = driverPool.browserId

        _workingDriverPools.remove(browserId)
        _retiredDriverPools.remove(browserId)
        _closedDriverPools.add(browserId)

        kotlin.runCatching { driverPool.close() }.onFailure { warnForClose(this, it) }
    }

    @Synchronized
    fun close() {
        val pools = workingDriverPools.values + retiredDriverPools.values
        pools.forEach { close(it) }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy