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

com.avsystem.commons.redis.commands.RedisInfo.scala Maven / Gradle / Ivy

package com.avsystem.commons
package redis.commands

import com.avsystem.commons.misc.{NamedEnum, NamedEnumCompanion}
import com.avsystem.commons.redis.NodeAddress

import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.Source

abstract class RedisInfoSection[T >: FullRedisInfo <: RedisInfo](val name: String)
trait RedisInfo {
  val infoStr: String

  protected def indexedPrefixes: List[String] = Nil

  lazy val rawValues: BMap[String, String] =
    mutable.LinkedHashMap() ++= Source.fromString(infoStr).getLines()
      .map(_.trim).filterNot(l => l.isEmpty || l.startsWith("#"))
      .map(l => l.split(":", 2)).map({ case Array(key, value) => (key, value) })

  protected lazy val keysByPrefix: BMap[String, BSeq[String]] = {
    val result = new MHashMap[String, ListBuffer[String]]
    rawValues.keys.foreach { key =>
      indexedPrefixes.filter(key.startsWith).foreach { prefix =>
        result.getOrElseUpdate(prefix, new ListBuffer) += key
      }
    }
    result
  }

  def get(name: String): Opt[String] =
    rawValues.get(name).toOpt

  override def toString: String = infoStr
}

abstract class DefaultRedisInfo extends RedisInfo
  with ServerInfo
  with ClientsInfo
  with MemoryInfo
  with PersistenceInfo
  with StatsInfo
  with ReplicationInfo
  with CpuInfo
  with ClusterInfo
  with KeyspaceInfo
object DefaultRedisInfo extends RedisInfoSection[DefaultRedisInfo]("default")

case class FullRedisInfo(infoStr: String) extends DefaultRedisInfo
  with CommandstatsInfo
object FullRedisInfo extends RedisInfoSection[FullRedisInfo]("all")

trait ServerInfo extends RedisInfo {
  def redisVersion: Opt[String] = get("redis_version")
  def redisGitSha1: Opt[String] = get("redis_git_sha1")
  def redisGitDirty: Opt[Boolean] = get("redis_git_dirty").map(_ == "1")
  def redisBuildId: Opt[String] = get("redis_build_id")
  def redisMode: Opt[RedisMode] = get("redis_mode").map(RedisMode.byName)
  def os: Opt[String] = get("os")
  def archBits: Opt[Int] = get("arch_bits").map(_.toInt)
  def multiplexingApi: Opt[String] = get("multiplexing_api")
  def gccVersion: Opt[String] = get("gcc_version")
  def processId: Opt[Int] = get("process_id").map(_.toInt)
  def runId: Opt[String] = get("run_id")
  def tcpPort: Opt[Int] = get("tcp_port").map(_.toInt)
  def uptimeInSeconds: Opt[Long] = get("uptime_in_seconds").map(_.toLong)
  def uptimeInDays: Opt[Long] = get("uptime_in_days").map(_.toLong)
  def hz: Opt[Int] = get("hz").map(_.toInt)
  def lruClock: Opt[Long] = get("lru_clock").map(_.toLong)
  def executable: Opt[String] = get("executable")
  def configFile: Opt[String] = get("config_file")
}
object ServerInfo extends RedisInfoSection[ServerInfo]("server")

sealed abstract class RedisMode(val name: String) extends NamedEnum
object RedisMode extends NamedEnumCompanion[RedisMode] {
  case object Cluster extends RedisMode("cluster")
  case object Sentinel extends RedisMode("sentinel")
  case object Standalone extends RedisMode("standalone")
  val values: List[RedisMode] = caseObjects
}
trait ClientsInfo extends RedisInfo {
  def connectedClients: Opt[Long] = get("connected_clients").map(_.toLong)
  def clientLongestOutputList: Opt[Long] = get("client_longest_output_list").map(_.toLong)
  def clientBiggestInputBuf: Opt[Long] = get("client_biggest_input_buf").map(_.toLong)
  def blockedClients: Opt[Int] = get("blocked_clients").map(_.toInt)
}
object ClientsInfo extends RedisInfoSection[ClientsInfo]("clients")

trait MemoryInfo extends RedisInfo {
  def usedMemory: Opt[Long] = get("used_memory").map(_.toLong)
  def usedMemoryHuman: Opt[String] = get("used_memory_human")
  def usedMemoryRss: Opt[Long] = get("used_memory_rss").map(_.toLong)
  def usedMemoryRssHuman: Opt[String] = get("used_memory_rss_human")
  def usedMemoryPeak: Opt[Long] = get("used_memory_peak").map(_.toLong)
  def usedMemoryPeakHuman: Opt[String] = get("used_memory_peak_human")
  def totalSystemMemory: Opt[Long] = get("total_system_memory").map(_.toLong)
  def totalSystemMemoryHuman: Opt[String] = get("total_system_memory_human")
  def usedMemoryLua: Opt[Long] = get("used_memory_lua").map(_.toLong)
  def usedMemoryLuaHuman: Opt[String] = get("used_memory_lua_human")
  def maxmemory: Opt[Long] = get("maxmemory").map(_.toLong)
  def maxmemoryHuman: Opt[String] = get("maxmemory_human")
  def maxmemoryPolicy: Opt[MaxmemoryPolicy] = get("maxmemory_policy").map(MaxmemoryPolicy.byName)
  def memFragmentationRatio: Opt[Double] = get("mem_fragmentation_ratio").map(_.toDouble)
  def memAllocator: Opt[String] = get("mem_allocator")
}
object MemoryInfo extends RedisInfoSection[MemoryInfo]("memory")

sealed abstract class MaxmemoryPolicy(val name: String) extends NamedEnum
object MaxmemoryPolicy extends NamedEnumCompanion[MaxmemoryPolicy] {
  case object VolatileLru extends MaxmemoryPolicy("volatile-lru")
  case object AllkeysLru extends MaxmemoryPolicy("allkeys-lru")
  case object VolatileRandom extends MaxmemoryPolicy("volatile-random")
  case object AllkeysRandom extends MaxmemoryPolicy("allkeys-random")
  case object VolatileTtl extends MaxmemoryPolicy("volatile-ttl")
  case object Noeviction extends MaxmemoryPolicy("noeviction")
  val values: List[MaxmemoryPolicy] = caseObjects
}

trait PersistenceInfo extends RedisInfo {
  def loading: Opt[Boolean] = get("loading").map(_ == "1")
  def rdbChangesSinceLastSave: Opt[Long] = get("rdb_changes_since_last_save").map(_.toLong)
  def rdbBgsaveInProgress: Opt[Boolean] = get("rdb_bgsave_in_progress").map(_ == "1")
  def rdbLastSaveTime: Opt[Long] = get("rdb_last_save_time").map(_.toLong)
  def rdbLastBgsaveStatusOk: Opt[Boolean] = get("rdb_last_bgsave_status").map(_ == "ok")
  def rdbLastBgsaveTimeSec: Opt[Long] = get("rdb_last_bgsave_time_sec").map(_.toLong)
  def rdbCurrentBgsaveTimeSec: Opt[Long] = get("rdb_current_bgsave_time_sec").map(_.toLong)
  def aofEnabled: Opt[Boolean] = get("aof_enabled").map(_ == "1")
  def aofRewriteInProgress: Opt[Boolean] = get("aof_rewrite_in_progress").map(_ == "1")
  def aofRewriteScheduled: Opt[Boolean] = get("aof_rewrite_scheduled").map(_ == "1")
  def aofLastRewriteTimeSec: Opt[Long] = get("aof_last_rewrite_time_sec").map(_.toLong)
  def aofCurrentRewriteTimeSec: Opt[Long] = get("aof_current_rewrite_time_sec").map(_.toLong)
  def aofLastBgrewriteStatusOk: Opt[Boolean] = get("aof_last_bgrewrite_status").map(_ == "ok")
  def aofLastWriteStatusOk: Opt[Boolean] = get("aof_last_write_status").map(_ == "ok")
  def aofCurrentSize: Opt[Long] = get("aof_current_size").map(_.toLong)
  def aofBaseSize: Opt[Long] = get("aof_base_size").map(_.toLong)
  def aofPendingRewrite: Opt[Boolean] = get("aof_pending_rewrite").map(_ == "1")
  def aofBufferLength: Opt[Long] = get("aof_buffer_length").map(_.toLong)
  def aofRewriteBufferLength: Opt[Long] = get("aof_rewrite_buffer_length").map(_.toLong)
  def aofPendingBioFsync: Opt[Long] = get("aof_pending_bio_fsync").map(_.toLong)
  def aofDelayedFsync: Opt[Long] = get("aof_delayed_fsync").map(_.toLong)
  def loadingStartTime: Opt[Long] = get("loading_start_time").map(_.toLong)
  def loadingTotalBytes: Opt[Long] = get("loading_total_bytes").map(_.toLong)
  def loadingLoadedBytes: Opt[Long] = get("loading_loaded_bytes").map(_.toLong)
  def loadingLoadedPerc: Opt[Double] = get("loading_loaded_perc").map(_.toDouble)
  def loadingEtaSeconds: Opt[Long] = get("loading_eta_seconds").map(_.toLong)
}
object PersistenceInfo extends RedisInfoSection[PersistenceInfo]("persistence")

trait StatsInfo extends RedisInfo {
  def totalConnectionsReceived: Opt[Long] = get("total_connections_received").map(_.toLong)
  def totalCommandsProcessed: Opt[Long] = get("total_commands_processed").map(_.toLong)
  def instantaneousOpsPerSec: Opt[Long] = get("instantaneous_ops_per_sec").map(_.toLong)
  def totalNetInputBytes: Opt[Long] = get("total_net_input_bytes").map(_.toLong)
  def totalNetOutputBytes: Opt[Long] = get("total_net_output_bytes").map(_.toLong)
  def instantaneousInputKbps: Opt[Double] = get("instantaneous_input_kbps").map(_.toDouble)
  def instantaneousOutputKbps: Opt[Double] = get("instantaneous_output_kbps").map(_.toDouble)
  def rejectedConnections: Opt[Long] = get("rejected_connections").map(_.toLong)
  def syncFull: Opt[Long] = get("sync_full").map(_.toLong)
  def syncPartialOk: Opt[Long] = get("sync_partial_ok").map(_.toLong)
  def syncPartialErr: Opt[Long] = get("sync_partial_err").map(_.toLong)
  def expiredKeys: Opt[Long] = get("expired_keys").map(_.toLong)
  def evictedKeys: Opt[Long] = get("evicted_keys").map(_.toLong)
  def keyspaceHits: Opt[Long] = get("keyspace_hits").map(_.toLong)
  def keyspaceMisses: Opt[Long] = get("keyspace_misses").map(_.toLong)
  def pubsubChannels: Opt[Long] = get("pubsub_channels").map(_.toLong)
  def pubsubPatterns: Opt[Long] = get("pubsub_patterns").map(_.toLong)
  def latestForkUsec: Opt[Long] = get("latest_fork_usec").map(_.toLong)
  def migrateCachedSockets: Opt[Long] = get("migrate_cached_sockets").map(_.toLong)
}
object StatsInfo extends RedisInfoSection[StatsInfo]("stats")

trait ReplicationInfo extends RedisInfo {
  def roleMaster: Opt[Boolean] = get("role").map(_ == "master")
  def masterAddr: Opt[NodeAddress] =
    for {
      masterHost <- get("master_host")
      masterPort <- get("master_port").map(_.toInt)
    } yield NodeAddress(masterHost, masterPort)
  def masterLinkStatusUp: Opt[Boolean] = get("master_link_status").map(_ == "up")
  def masterLastIoSecondsAgo: Opt[Int] = get("master_last_io_seconds_ago").map(_.toInt)
  def masterSyncInProgress: Opt[Boolean] = get("master_sync_in_progress").map(_ == "1")
  def slaveReplOffset: Opt[Long] = get("slave_repl_offset").map(_.toLong)
  def masterSyncLeftBytes: Opt[Long] = get("master_sync_left_bytes").map(_.toLong)
  def masterSyncLastIoSecondsAgo: Opt[Int] = get("master_sync_last_io_seconds_ago").map(_.toInt)
  def masterLinkDownSinceSeconds: Opt[Long] = get("master_link_down_since_seconds").map(_.toLong)
  def slavePriority: Opt[Int] = get("slave_priority").map(_.toInt)
  def slaveReadonly: Opt[Boolean] = get("slave_read_only").map(_ == "1")
  def connectedSlaves: Opt[Int] = get("connected_slaves").map(_.toInt)
  def minSlavesGoodSlaves: Opt[Int] = get("min_slaves_good_slaves").map(_.toInt)
  /**
    * @param slaveId ranges from 0 to [[connectedSlaves]]-1
    */
  def slaveInfo(slaveId: Int): Opt[SlaveInfo] = get(s"slave$slaveId").map(SlaveInfo)
  def masterReplOffset: Opt[Long] = get("master_repl_offset").map(_.toLong)
  def replBacklogActive: Opt[Boolean] = get("repl_backlog_active").map(_ == "1")
  def replBacklogSize: Opt[Long] = get("repl_backlog_size").map(_.toLong)
  def replBacklogFirstByteOffset: Opt[Long] = get("repl_backlog_first_byte_offset").map(_.toLong)
  def replBacklogHistlen: Opt[Long] = get("repl_backlog_histlen").map(_.toLong)
}
object ReplicationInfo extends RedisInfoSection[ReplicationInfo]("replication")

case class SlaveInfo(infoLine: String) extends ParsedInfo(infoLine, ",", "=") {
  def addr = NodeAddress(attrMap("ip"), attrMap("port").toInt)
  def state = SlaveState.byName(attrMap("state"))
  def offset: Long = attrMap("offset").toLong
  def lag: Long = attrMap("lag").toLong
}
sealed abstract class SlaveState(val name: String) extends NamedEnum
object SlaveState extends NamedEnumCompanion[SlaveState] {
  object WaitBgsave extends SlaveState("wait_bgsave")
  object SendBulk extends SlaveState("send_bulk")
  object Online extends SlaveState("online")
  val values: List[SlaveState] = caseObjects
}

trait CpuInfo extends RedisInfo {
  def usedCpuSys: Opt[Double] = get("used_cpu_sys").map(_.toDouble)
  def usedCpuUser: Opt[Double] = get("used_cpu_user").map(_.toDouble)
  def usedCpuSysChildren: Opt[Double] = get("used_cpu_sys_children").map(_.toDouble)
  def usedCpuUserChildren: Opt[Double] = get("used_cpu_user_children").map(_.toDouble)
}
object CpuInfo extends RedisInfoSection[CpuInfo]("cpu")

trait CommandstatsInfo extends RedisInfo {
  override protected def indexedPrefixes: List[String] = "cmdstat_" :: super.indexedPrefixes

  lazy val executedCommands: BSeq[String] = keysByPrefix.getOrElse("cmdstat_", Nil).map(_.stripPrefix("cmdstat_"))
  def commandStat(command: String): Opt[CommandStat] = get(s"cmdstat_$command").map(CommandStat)
}
object CommandstatsInfo extends RedisInfoSection[CommandstatsInfo]("commandstats")

case class CommandStat(infoLine: String) extends ParsedInfo(infoLine, ",", "=") {
  def calls: Long = attrMap("calls").toLong
  def usec: Long = attrMap("usec").toLong
  def usecPerCall: Double = attrMap("usec_per_call").toDouble
}

trait ClusterInfo extends RedisInfo {
  def clusterEnabled: Opt[Boolean] = get("cluster_enabled").map(_ == "1")
}
object ClusterInfo extends RedisInfoSection[ClusterInfo]("cluster")

trait KeyspaceInfo extends RedisInfo {
  override protected def indexedPrefixes: List[String] = "db" :: super.indexedPrefixes

  lazy val nonEmptyDbs: Seq[Int] = keysByPrefix.getOrElse("db", Nil).iterator.map(_.stripPrefix("db").toInt).toList
  def dbStat(dbId: Int): Opt[DbStat] = get(s"db$dbId").map(DbStat)
}
object KeyspaceInfo extends RedisInfoSection[KeyspaceInfo]("keyspace")

case class DbStat(infoLine: String) extends ParsedInfo(infoLine, ",", "=") {
  def keys: Long = attrMap("keys").toLong
  def expires: Long = attrMap("expires").toLong
  def avgTtl: Long = attrMap("avg_ttl").toLong
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy