storage.drivers.rediscala.rediscala.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otoroshi_2.12 Show documentation
Show all versions of otoroshi_2.12 Show documentation
Lightweight api management on top of a modern http reverse proxy
The newest version!
package otoroshi.storage.drivers.rediscala
import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.scaladsl.util.FastFuture
import akka.stream.Materializer
import akka.stream.scaladsl.{Sink, Source}
import akka.util.ByteString
import com.typesafe.config.ConfigFactory
import otoroshi.auth.AuthConfigsDataStore
import otoroshi.cluster.{Cluster, ClusterStateDataStore, KvClusterStateDataStore}
import otoroshi.env.Env
import otoroshi.events.{AlertDataStore, AuditDataStore, HealthCheckDataStore}
import otoroshi.gateway.{InMemoryRequestsDataStore, RequestsDataStore}
import otoroshi.models._
import otoroshi.next.models._
import otoroshi.script.{KvScriptDataStore, ScriptDataStore}
import otoroshi.ssl.{CertificateDataStore, ClientCertificateValidationDataStore, KvClientCertificateValidationDataStore}
import otoroshi.storage._
import otoroshi.storage.stores._
import otoroshi.tcp.{KvTcpServiceDataStoreDataStore, TcpServiceDataStore}
import otoroshi.utils.syntax.implicits._
import play.api.inject.ApplicationLifecycle
import play.api.libs.json._
import play.api.{Configuration, Environment, Logger}
import redis._
import redis.util.CRC16
import scala.concurrent.{ExecutionContext, Future}
case class RedisMember(host: String, port: Int, password: Option[String]) {
def toRedisServer =
RedisServer(
host = host,
port = port,
password = password
)
}
object RedisMember {
def fromString(value: String): Option[RedisMember] = {
value.trim match {
case str if str.contains("@") && str.contains(":") =>
str.split("@").toList match {
case password :: rest :: Nil =>
rest.split(":").toList match {
case host :: port :: Nil => Some(RedisMember(host, port.toInt, Some(password)))
case _ => None
}
case _ => None
}
case str if str.contains(":") =>
str.split(":").toList match {
case host :: port :: Nil => Some(RedisMember(host, port.toInt, None))
case _ => None
}
case _ =>
None
}
}
def fromList(value: String): Seq[RedisMember] = {
value.split(",").map(_.trim).flatMap(fromString)
}
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisCPStore(redis: RedisClientPool, env: Env, ec: ExecutionContext) extends RedisCommandsStore(redis, env, ec)
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisMCPStore(redis: RedisClientMutablePool, env: Env, ec: ExecutionContext)
extends RedisCommandsStore(redis, env, ec)
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisClusterStore(redis: RedisCluster, env: Env, ec: ExecutionContext)
extends RedisCommandsStore(redis, env, ec, true)
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisLFStore(redis: RedisClientMasterSlaves, env: Env, ec: ExecutionContext)
extends RedisCommandsStore(redis, env, ec)
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisSentinelStore(redis: SentinelMonitoredRedisClient, env: Env, ec: ExecutionContext)
extends RedisCommandsStore(redis, env, ec)
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisSentinelLFStore(redis: SentinelMonitoredRedisClientMasterSlaves, env: Env, ec: ExecutionContext)
extends RedisCommandsStore(redis, env, ec)
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisCPDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends AbstractRedisDataStores(configuration, environment, lifecycle, env) {
lazy val redisCli: RedisClientPool = {
implicit val ec = redisDispatcher
val members = configuration
.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.pool.members")
.map(_.map { config =>
RedisServer(
host = config.getOptionalWithFileSupport[String]("host").getOrElse("localhost"),
port = config.getOptionalWithFileSupport[Int]("port").getOrElse(6379),
password = config.getOptionalWithFileSupport[String]("password")
)
})
.filter(_.nonEmpty)
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.pool.membersStr")
.map(RedisMember.fromList)
.map(_.map(_.toRedisServer))
}
.getOrElse(Seq.empty[RedisServer])
val cli: RedisClientPool = RedisClientPool(
members
)(redisActorSystem)
cli
}
lazy val _redis: RedisLike = new RedisCPStore(redisCli, env, redisActorSystem.dispatcher)
override def loggerName: String = "otoroshi-redis-pool-datastores"
override def name: String = "Redis Pool"
override def redis: RedisLike = _redis
override def info(): Future[String] = redisCli.info()
override def typeOfKey(key: String): Future[String] = redisCli.`type`(key)
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisMCPDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends AbstractRedisDataStores(configuration, environment, lifecycle, env) {
lazy val redisCli: RedisClientMutablePool = {
implicit val ec = redisDispatcher
val members = configuration
.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.mpool.members")
.map(_.map { config =>
RedisServer(
host = config.getOptionalWithFileSupport[String]("host").getOrElse("localhost"),
port = config.getOptionalWithFileSupport[Int]("port").getOrElse(6379),
password = config.getOptionalWithFileSupport[String]("password")
)
})
.filter(_.nonEmpty)
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.mpool.membersStr")
.map(RedisMember.fromList)
.map(_.map(_.toRedisServer))
}
.getOrElse(Seq.empty[RedisServer])
val cli: RedisClientMutablePool = RedisClientMutablePool(
members
)(redisActorSystem)
cli
}
lazy val _redis: RedisLike = new RedisMCPStore(redisCli, env, redisActorSystem.dispatcher)
override def loggerName: String = "otoroshi-redis-mpool-datastores"
override def name: String = "Redis Mutable Pool"
override def redis: RedisLike = _redis
override def info(): Future[String] = redisCli.info()
override def typeOfKey(key: String): Future[String] = redisCli.`type`(key)
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisLFDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends AbstractRedisDataStores(configuration, environment, lifecycle, env) {
lazy val redisCli: RedisClientMasterSlaves = {
implicit val ec = redisDispatcher
val master = RedisServer(
host = configuration
.getOptionalWithFileSupport[String]("app.redis.host")
.orElse(configuration.getOptionalWithFileSupport[String]("app.redis.lf.master.host"))
.getOrElse("localhost"),
port = configuration
.getOptionalWithFileSupport[Int]("app.redis.port")
.orElse(configuration.getOptionalWithFileSupport[Int]("app.redis.lf.master.port"))
.getOrElse(6379),
password = configuration
.getOptionalWithFileSupport[String]("app.redis.password")
.orElse(configuration.getOptionalWithFileSupport[String]("app.redis.lf.master.password"))
)
val slaves = configuration
.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.slaves")
.orElse(configuration.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.lf.slaves"))
.map(_.map { config =>
// val config = Configuration(cfgobj.toConfig)
RedisServer(
host = config.getOptionalWithFileSupport[String]("host").getOrElse("localhost"),
port = config.getOptionalWithFileSupport[Int]("port").getOrElse(6379),
password = config.getOptionalWithFileSupport[String]("password")
)
})
.filter(_.nonEmpty)
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.slavesStr")
.map(RedisMember.fromList)
.map(_.map(_.toRedisServer))
}
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.lf.slavesStr")
.map(RedisMember.fromList)
.map(_.map(_.toRedisServer))
}
.getOrElse(Seq.empty[RedisServer])
val cli: RedisClientMasterSlaves = RedisClientMasterSlaves(
master,
slaves
)(redisActorSystem)
cli
}
lazy val _redis: RedisLike = new RedisLFStore(redisCli, env, redisActorSystem.dispatcher)
override def loggerName: String = "otoroshi-redis-lf-datastores"
override def name: String = "Redis Leader/Followers"
override def redis: RedisLike = _redis
override def info(): Future[String] = redisCli.info()
override def typeOfKey(key: String): Future[String] = redisCli.`type`(key)
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisSentinelDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends AbstractRedisDataStores(configuration, environment, lifecycle, env) {
lazy val redisCli: SentinelMonitoredRedisClient = {
implicit val ec = redisDispatcher
val members: Seq[(String, Int)] = configuration
.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.sentinels.members")
.map(_.map { config =>
(
config.getOptionalWithFileSupport[String]("host").getOrElse("localhost"),
config.getOptionalWithFileSupport[Int]("port").getOrElse(6379)
)
})
.filter(_.nonEmpty)
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.sentinels.membersStr")
.map(RedisMember.fromList)
.map(_.map(m => (m.host, m.port)))
}
.getOrElse(Seq.empty[(String, Int)])
val master = configuration.getOptionalWithFileSupport[String]("app.redis.sentinels.master").get
val password = configuration.getOptionalWithFileSupport[String]("app.redis.sentinels.password")
val db = configuration.getOptionalWithFileSupport[Int]("app.redis.sentinels.db")
val name = configuration.getOptionalWithFileSupport[String]("app.redis.sentinels.name").getOrElse("SMRedisClient")
val cli: SentinelMonitoredRedisClient = SentinelMonitoredRedisClient(
members,
master,
password,
db,
name
)(redisActorSystem)
cli
}
lazy val _redis: RedisLike = new RedisSentinelStore(redisCli, env, redisActorSystem.dispatcher)
override def loggerName: String = "otoroshi-redis-sentinel-datastores"
override def name: String = "Redis Sentinels"
override def redis: RedisLike = _redis
override def info(): Future[String] = redisCli.info()
override def typeOfKey(key: String): Future[String] = redisCli.`type`(key)
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisSentinelLFDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends AbstractRedisDataStores(configuration, environment, lifecycle, env) {
lazy val redisCli: SentinelMonitoredRedisClientMasterSlaves = {
implicit val ec = redisDispatcher
val members: Seq[(String, Int)] = configuration
.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.sentinels.lf.members")
.map(_.map { config =>
(
config.getOptionalWithFileSupport[String]("host").getOrElse("localhost"),
config.getOptionalWithFileSupport[Int]("port").getOrElse(6379)
)
})
.filter(_.nonEmpty)
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.sentinels.lf.membersStr")
.map(RedisMember.fromList)
.map(_.map(m => (m.host, m.port)))
}
.getOrElse(Seq.empty[(String, Int)])
val master = configuration.getOptionalWithFileSupport[String]("app.redis.sentinels.lf.master").get
val cli: SentinelMonitoredRedisClientMasterSlaves = SentinelMonitoredRedisClientMasterSlaves(
members,
master
)(redisActorSystem)
cli
}
lazy val _redis: RedisLike = new RedisSentinelLFStore(redisCli, env, redisActorSystem.dispatcher)
override def loggerName: String = "otoroshi-redis-sentinel-lf-datastores"
override def name: String = "Redis Sentinel Leader/Followers"
override def redis: RedisLike = _redis
override def info(): Future[String] = redisCli.info()
override def typeOfKey(key: String): Future[String] = redisCli.`type`(key)
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisClusterDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends AbstractRedisDataStores(configuration, environment, lifecycle, env) {
lazy val redisCluster: RedisCluster = {
implicit val ec = redisDispatcher
val members = configuration
.getOptionalWithFileSupport[Seq[Configuration]]("app.redis.cluster.members")
.map(_.map { config =>
RedisServer(
host = config.getOptionalWithFileSupport[String]("host").getOrElse("localhost"),
port = config.getOptionalWithFileSupport[Int]("port").getOrElse(6379),
password = config.getOptionalWithFileSupport[String]("password")
)
})
.filter(_.nonEmpty)
.orElse {
configuration
.getOptionalWithFileSupport[String]("app.redis.sentinels.membersStr")
.map(RedisMember.fromList)
.map(_.map(_.toRedisServer))
}
.getOrElse(Seq.empty[RedisServer])
val cli: RedisCluster = RedisCluster(
members
)(redisActorSystem)
cli
}
lazy val _redis: RedisLike = new RedisClusterStore(redisCluster, env, redisActorSystem.dispatcher)
override def loggerName: String = "otoroshi-redis-cluster-datastores"
override def name: String = "Redis Cluster"
override def redis: RedisLike = _redis
override def info(): Future[String] = redisCluster.info()
override def typeOfKey(key: String): Future[String] = redisCluster.`type`(key)
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
abstract class AbstractRedisDataStores(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
env: Env
) extends DataStores {
def loggerName: String
def name: String
def redis: RedisLike
def info(): Future[String]
def typeOfKey(key: String): Future[String]
lazy val logger = Logger(loggerName)
lazy val redisStatsItems: Int = configuration.getOptionalWithFileSupport[Int]("app.redis.windowSize").getOrElse(99)
lazy val redisActorSystem =
ActorSystem(
"otoroshi-redis-system",
configuration
.getOptionalWithFileSupport[Configuration]("app.actorsystems.datastore")
.map(_.underlying)
.getOrElse(ConfigFactory.empty)
)
lazy val redisDispatcher = redisActorSystem.dispatcher
override def before(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle
): Future[Unit] = {
logger.info(s"Now using $name DataStores")
logger.warn(
s"You are using the rediscala datastore implementation that is now deprecated. It will eventually be replaced by the 'lettuce' implementation."
)
_serviceDescriptorDataStore.startCleanup(env)
_certificateDataStore.startSync()
FastFuture.successful(())
}
override def after(
configuration: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle
): Future[Unit] = {
_serviceDescriptorDataStore.stopCleanup()
_certificateDataStore.stopSync()
redisActorSystem.terminate()
FastFuture.successful(())
}
private lazy val _privateAppsUserDataStore = new KvPrivateAppsUserDataStore(redis, env)
private lazy val _backOfficeUserDataStore = new KvBackOfficeUserDataStore(redis, env)
private lazy val _serviceGroupDataStore = new KvServiceGroupDataStore(redis, env)
private lazy val _globalConfigDataStore = new KvGlobalConfigDataStore(redis, env)
private lazy val _apiKeyDataStore = new KvApiKeyDataStore(redis, env)
private lazy val _serviceDescriptorDataStore = new KvServiceDescriptorDataStore(redis, redisStatsItems, env)
private lazy val _simpleAdminDataStore = new KvSimpleAdminDataStore(redis, env)
private lazy val _alertDataStore = new KvAlertDataStore(redis)
private lazy val _auditDataStore = new KvAuditDataStore(redis)
private lazy val _healthCheckDataStore = new KvHealthCheckDataStore(redis, env)
private lazy val _errorTemplateDataStore = new KvErrorTemplateDataStore(redis, env)
private lazy val _requestsDataStore = new InMemoryRequestsDataStore()
private lazy val _canaryDataStore = new KvCanaryDataStore(redis, env)
private lazy val _chaosDataStore = new KvChaosDataStore(redis, env)
private lazy val _jwtVerifDataStore = new KvGlobalJwtVerifierDataStore(redis, env)
private lazy val _authConfigsDataStore = new KvAuthConfigsDataStore(redis, env)
private lazy val _certificateDataStore = new KvCertificateDataStore(redis, env)
private lazy val _clusterStateDataStore = new KvClusterStateDataStore(redis, env)
override def clusterStateDataStore: ClusterStateDataStore = _clusterStateDataStore
private lazy val _clientCertificateValidationDataStore = new KvClientCertificateValidationDataStore(redis, env)
override def clientCertificateValidationDataStore: ClientCertificateValidationDataStore =
_clientCertificateValidationDataStore
private lazy val _scriptDataStore = new KvScriptDataStore(redis, env)
override def scriptDataStore: ScriptDataStore = _scriptDataStore
private lazy val _tcpServiceDataStore = new KvTcpServiceDataStoreDataStore(redis, env)
override def tcpServiceDataStore: TcpServiceDataStore = _tcpServiceDataStore
private lazy val _rawDataStore = new KvRawDataStore(redis)
override def rawDataStore: RawDataStore = _rawDataStore
private lazy val _webAuthnAdminDataStore = new KvWebAuthnAdminDataStore()
override def webAuthnAdminDataStore: WebAuthnAdminDataStore = _webAuthnAdminDataStore
private lazy val _webAuthnRegistrationsDataStore = new WebAuthnRegistrationsDataStore()
override def webAuthnRegistrationsDataStore: WebAuthnRegistrationsDataStore = _webAuthnRegistrationsDataStore
private lazy val _tenantDataStore = new TenantDataStore(redis, env)
override def tenantDataStore: TenantDataStore = _tenantDataStore
private lazy val _teamDataStore = new TeamDataStore(redis, env)
override def teamDataStore: TeamDataStore = _teamDataStore
private lazy val _dataExporterConfigDataStore = new DataExporterConfigDataStore(redis, env)
override def dataExporterConfigDataStore: DataExporterConfigDataStore = _dataExporterConfigDataStore
private lazy val _routeDataStore = new KvNgRouteDataStore(redis, env)
override def routeDataStore: NgRouteDataStore = _routeDataStore
private lazy val _routesCompositionDataStore = new KvNgRouteCompositionDataStore(redis, env)
override def routeCompositionDataStore: NgRouteCompositionDataStore = _routesCompositionDataStore
private lazy val _backendsDataStore = new KvStoredNgBackendDataStore(redis, env)
override def backendsDataStore: StoredNgBackendDataStore = _backendsDataStore
private lazy val _wasmPluginDataStore = new KvWasmPluginDataStore(redis, env)
override def wasmPluginsDataStore: WasmPluginDataStore = _wasmPluginDataStore
private lazy val _draftDataStore = new KvDraftDataStore(redis, env)
override def draftsDataStore: DraftDataStore = _draftDataStore
private lazy val _adminPreferencesDatastore = new AdminPreferencesDatastore(env)
def adminPreferencesDatastore: AdminPreferencesDatastore = _adminPreferencesDatastore
override def privateAppsUserDataStore: PrivateAppsUserDataStore = _privateAppsUserDataStore
override def backOfficeUserDataStore: BackOfficeUserDataStore = _backOfficeUserDataStore
override def serviceGroupDataStore: ServiceGroupDataStore = _serviceGroupDataStore
override def globalConfigDataStore: GlobalConfigDataStore = _globalConfigDataStore
override def apiKeyDataStore: ApiKeyDataStore = _apiKeyDataStore
override def serviceDescriptorDataStore: ServiceDescriptorDataStore = _serviceDescriptorDataStore
override def simpleAdminDataStore: SimpleAdminDataStore = _simpleAdminDataStore
override def alertDataStore: AlertDataStore = _alertDataStore
override def auditDataStore: AuditDataStore = _auditDataStore
override def healthCheckDataStore: HealthCheckDataStore = _healthCheckDataStore
override def errorTemplateDataStore: ErrorTemplateDataStore = _errorTemplateDataStore
override def requestsDataStore: RequestsDataStore = _requestsDataStore
override def canaryDataStore: CanaryDataStore = _canaryDataStore
override def chaosDataStore: ChaosDataStore = _chaosDataStore
override def globalJwtVerifierDataStore: GlobalJwtVerifierDataStore = _jwtVerifDataStore
override def authConfigsDataStore: AuthConfigsDataStore = _authConfigsDataStore
override def certificatesDataStore: CertificateDataStore = _certificateDataStore
override def health()(implicit ec: ExecutionContext): Future[DataStoreHealth] = {
info().map(_ => Healthy).recover { case _ =>
Unreachable
}
}
override def rawExport(
group: Int
)(implicit ec: ExecutionContext, mat: Materializer, env: Env): Source[JsValue, NotUsed] = {
Source
.future(
redis.keys(s"${env.storageRoot}:*")
)
.mapConcat(_.toList)
.grouped(group)
.mapAsync(1) {
case keys if keys.isEmpty => FastFuture.successful(Seq.empty[JsValue])
case keys => {
Future.sequence(
keys
.filterNot { key =>
Cluster.filteredKey(key, env)
// key == s"${env.storageRoot}:cluster:" ||
// key == s"${env.storageRoot}:events:audit" ||
// key == s"${env.storageRoot}:events:alerts" ||
// key.startsWith(s"${env.storageRoot}:users:backoffice") ||
// key.startsWith(s"${env.storageRoot}:admins:") ||
// key.startsWith(s"${env.storageRoot}:u2f:users:") ||
// // key.startsWith(s"${env.storageRoot}:users:") ||
// key.startsWith(s"${env.storageRoot}:webauthn:admins:") ||
// key.startsWith(s"${env.storageRoot}:deschealthcheck:") ||
// key.startsWith(s"${env.storageRoot}:scall:stats:") ||
// key.startsWith(s"${env.storageRoot}:scalldur:stats:") ||
// key.startsWith(s"${env.storageRoot}:scallover:stats:") ||
// (key.startsWith(s"${env.storageRoot}:data:") && key.endsWith(":stats:in")) ||
// (key.startsWith(s"${env.storageRoot}:data:") && key.endsWith(":stats:out"))
}
.map { key =>
for {
w <- typeOfKey(key)
ttl <- redis.pttl(key)
value <- fetchValueForType(w, key)
} yield value match {
case JsNull => JsNull
case _ =>
Json.obj(
"k" -> key,
"v" -> value,
"t" -> (if (ttl == -1) -1 else (System.currentTimeMillis() + ttl)),
"w" -> w
)
}
}
)
}
}
.map(_.filterNot(_ == JsNull))
.mapConcat(_.toList)
}
override def fullNdJsonExport(group: Int, groupWorkers: Int, keyWorkers: Int): Future[Source[JsValue, _]] = {
implicit val ev = env
implicit val ecc = env.otoroshiExecutionContext
implicit val mat = env.otoroshiMaterializer
FastFuture.successful(
Source
.future(redis.keys(s"${env.storageRoot}:*"))
.mapConcat(_.toList)
.grouped(10)
.mapAsync(1) {
case keys if keys.isEmpty => FastFuture.successful(Seq.empty[JsValue])
case keys => {
Source(keys.toList)
.mapAsync(1) { key =>
for {
w <- typeOfKey(key)
ttl <- redis.pttl(key)
value <- fetchValueForType(w, key)
} yield value match {
case JsNull => JsNull
case _ =>
Json.obj(
"k" -> key,
"v" -> value,
"t" -> (if (ttl == -1) -1 else (System.currentTimeMillis() + ttl)),
"w" -> w
)
}
}
.runWith(Sink.seq)
.map(_.filterNot(_ == JsNull))
}
}
.mapConcat(_.toList)
)
}
override def fullNdJsonImport(exportSource: Source[JsValue, _]): Future[Unit] = {
implicit val ev = env
implicit val ecc = env.otoroshiExecutionContext
implicit val mat = env.otoroshiMaterializer
redis
.keys(s"${env.storageRoot}:*")
.flatMap(keys => if (keys.nonEmpty) redis.del(keys: _*) else FastFuture.successful(0L))
.flatMap { _ =>
exportSource
.mapAsync(1) { json =>
val key = (json \ "k").as[String]
val value = (json \ "v").as[JsValue]
val pttl = (json \ "t").as[Long]
val what = (json \ "w").as[String]
(what match {
case "counter" => redis.set(key, value.as[Long].toString)
case "string" => redis.set(key, value.as[String])
case "hash" =>
Source(value.as[JsObject].value.toList)
.mapAsync(1)(v => redis.hset(key, v._1, Json.stringify(v._2)))
.runWith(Sink.ignore)
case "list" => redis.lpush(key, value.as[JsArray].value.map(Json.stringify): _*)
case "set" => redis.sadd(key, value.as[JsArray].value.map(Json.stringify): _*)
case _ => FastFuture.successful(0L)
}).flatMap { _ =>
if (pttl > -1L) {
redis.pexpire(key, pttl)
} else {
FastFuture.successful(true)
}
}
}
.runWith(Sink.ignore)
.map(_ => ())
}
}
private def fetchValueForType(typ: String, key: String)(implicit ec: ExecutionContext): Future[JsValue] = {
typ match {
case "hash" => redis.hgetall(key).map(m => JsObject(m.map(t => (t._1, JsString(t._2.utf8String)))))
case "list" => redis.lrange(key, 0, Long.MaxValue).map(l => JsArray(l.map(s => JsString(s.utf8String))))
case "set" => redis.smembers(key).map(l => JsArray(l.map(s => JsString(s.utf8String))))
case "string" =>
redis.get(key).map {
case None => JsNull
case Some(a) => JsString(a.utf8String)
}
case _ => FastFuture.successful(JsNull)
}
}
}
@deprecated(message = "Use lettuce instead", since = "1.5.0")
class RedisCommandsStore(redis: RedisCommands, env: Env, executionContext: ExecutionContext, cluster: Boolean = false)
extends RedisLike {
implicit val ec = executionContext
override def health()(implicit ec: ExecutionContext): Future[DataStoreHealth] = {
redis.info().map(_ => Healthy).recover { case _ =>
Unreachable
}
}
override def stop(): Unit = ()
override def flushall(): Future[Boolean] = redis.flushall()
override def get(key: String): Future[Option[ByteString]] = redis.get(key)
override def mget(keys: String*): Future[Seq[Option[ByteString]]] = {
if (cluster) {
val keysAndHash: Seq[(Int, Seq[(String, Int)])] = keys.map(k => (k, CRC16.crc16(k))).groupBy(_._2).toSeq
Future
.sequence(keysAndHash.map { otherKeysTuple =>
val (_, seq) = otherKeysTuple
val keys: Seq[String] = seq.map(_._1)
redis.mget(keys: _*).map(_.zip(keys))
})
.map { res =>
val results: Map[String, Option[ByteString]] = res.flatten.map(t => (t._2, t._1)).toMap
keys.map(k => results.get(k).flatten)
}
} else {
redis.mget(keys: _*)
}
}
override def set(key: String, value: String, exSeconds: Option[Long], pxMilliseconds: Option[Long]): Future[Boolean] =
setBS(key, ByteString(value), exSeconds, pxMilliseconds)
override def setBS(
key: String,
value: ByteString,
exSeconds: Option[Long],
pxMilliseconds: Option[Long]
): Future[Boolean] = redis.set(key, value, exSeconds, pxMilliseconds)
override def del(keys: String*): Future[Long] = {
if (cluster) {
Future
.sequence(keys.toSeq.groupBy(CRC16.crc16).map { otherKeys =>
redis.del(otherKeys._2: _*)
})
.map(_.foldLeft(0L)(_ + _))
} else {
redis.del(keys: _*)
}
}
override def incr(key: String): Future[Long] = redis.incr(key)
override def incrby(key: String, increment: Long): Future[Long] = redis.incrby(key, increment)
override def exists(key: String): Future[Boolean] = redis.exists(key)
override def keys(pattern: String): Future[Seq[String]] = redis.keys(pattern)
override def hdel(key: String, fields: String*): Future[Long] = redis.hdel(key, fields: _*)
override def hgetall(key: String): Future[Map[String, ByteString]] = redis.hgetall(key)
override def hset(key: String, field: String, value: String): Future[Boolean] = hsetBS(key, field, ByteString(value))
override def hsetBS(key: String, field: String, value: ByteString): Future[Boolean] = redis.hset(key, field, value)
override def llen(key: String): Future[Long] = redis.llen(key)
override def lpush(key: String, values: String*): Future[Long] = lpushBS(key, values.map(ByteString.apply): _*)
override def lpushLong(key: String, values: Long*): Future[Long] =
lpushBS(key, values.map(v => ByteString(v.toString)): _*)
override def lpushBS(key: String, values: ByteString*): Future[Long] = redis.lpush(key, values: _*)
override def lrange(key: String, start: Long, stop: Long): Future[Seq[ByteString]] = redis.lrange(key, start, stop)
override def ltrim(key: String, start: Long, stop: Long): Future[Boolean] = redis.ltrim(key, start, stop)
override def pttl(key: String): Future[Long] = redis.pttl(key)
override def ttl(key: String): Future[Long] = redis.ttl(key)
override def expire(key: String, seconds: Int): Future[Boolean] = redis.expire(key, seconds)
override def pexpire(key: String, milliseconds: Long): Future[Boolean] = redis.pexpire(key, milliseconds)
override def sadd(key: String, members: String*): Future[Long] = saddBS(key, members.map(ByteString.apply): _*)
override def saddBS(key: String, members: ByteString*): Future[Long] = redis.sadd(key, members: _*)
override def sismember(key: String, member: String): Future[Boolean] = sismemberBS(key, ByteString(member))
override def sismemberBS(key: String, member: ByteString): Future[Boolean] = redis.sismember(key, member)
override def smembers(key: String): Future[Seq[ByteString]] = redis.smembers(key)
override def srem(key: String, members: String*): Future[Long] = sremBS(key, members.map(ByteString.apply): _*)
override def sremBS(key: String, members: ByteString*): Future[Long] = redis.srem(key, members: _*)
override def scard(key: String): Future[Long] = redis.scard(key)
override def rawGet(key: String): Future[Option[Any]] = redis.get(key)
override def setnxBS(key: String, value: ByteString, ttl: Option[Long])(implicit
ec: ExecutionContext,
env: Env
): Future[Boolean] =
redis.setnx(key, value).flatMap {
case false => FastFuture.successful(false)
case true =>
ttl match {
case None => FastFuture.successful(true)
case Some(v) => redis.pexpire(key, v).map(_ => true)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy