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

com.avsystem.commons.redis.actor.ConnectionPoolActor.scala Maven / Gradle / Ivy

package com.avsystem.commons
package redis.actor

import akka.actor.{Actor, ActorRef, Props}
import com.avsystem.commons.redis.NodeAddress
import com.avsystem.commons.redis.actor.ConnectionPoolActor._
import com.avsystem.commons.redis.config.{ConnectionConfig, NodeConfig}
import com.avsystem.commons.redis.exception.RedisException
import com.typesafe.scalalogging.LazyLogging

import scala.annotation.tailrec

class ConnectionPoolActor(address: NodeAddress, config: NodeConfig, queue: JDeque[QueuedConn])
  extends Actor with LazyLogging {

  import context._

  private val connections = new MHashSet[ActorRef]

  if (config.maxBlockingIdleTime.isFinite) {
    val interval = config.blockingCleanupInterval
    system.scheduler.scheduleWithFixedDelay(interval, interval, self, Cleanup)
  }

  def receive: Receive = {
    case CreateNewConnection if connections.size < config.maxBlockingPoolSize =>
      logger.info(s"Creating new blocking connection to $address")
      val connConfig: ConnectionConfig = config.blockingConnectionConfigs(connections.size)
      val props = Props(new RedisConnectionActor(address, connConfig))
      val connection = connConfig.actorName.fold(actorOf(props))(actorOf(props, _))
      connections += connection
      connection ! RedisConnectionActor.Open(mustInitiallyConnect = false, Promise[Unit]())
      sender() ! NewConnection(connection)
    case CreateNewConnection =>
      sender() ! Full
    case Cleanup =>
      cleanup(System.nanoTime(), config.maxBlockingIdleTime.toNanos)
    case Close(cause, stopSelf) =>
      connections.foreach(_ ! RedisConnectionActor.Close(cause, stopSelf))
      if (stopSelf) {
        stop(self)
      }
  }

  private def cleanup(nowNanos: Long, maxIdleNanos: Long): Unit = {
    @tailrec def loop(dequeue: Boolean): Unit = {
      val last = if (dequeue) queue.pollLast() else queue.peekLast()
      last match {
        case QueuedConn(conn, enqueuedAt) =>
          val stale = (nowNanos - enqueuedAt) > maxIdleNanos
          if (!dequeue && stale) {
            loop(dequeue = true)
          } else if (dequeue && stale) {
            conn ! RedisConnectionActor.Close(new RedisException("Idle blocking connection closed"), stop = true)
            context.stop(conn)
            connections.remove(conn)
            loop(dequeue = false)
          } else if (dequeue && !stale) {
            // unlikely situation where we dequeued something else than we peeked before
            queue.addLast(last)
          }
        case null =>
      }
    }
    loop(dequeue = false)
  }
}
object ConnectionPoolActor {
  final case class QueuedConn(conn: ActorRef, enqueuedAt: Long)

  object CreateNewConnection
  final case class Close(cause: Throwable, stop: Boolean)
  object Cleanup

  final case class NewConnection(connection: ActorRef)
  object Full
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy