Skip to content

Commit

Permalink
Add evalReadOnly & evalShaReadOnly (#880)
Browse files Browse the repository at this point in the history
* Add evalReadOnly and evalShaReadOnly

* Add evalReadOnly and evalShaReadOnly tests

* Fix 'write command on read-only script' exception test
  • Loading branch information
BalmungSan authored Jun 5, 2024
1 parent 340be81 commit 81c19b8
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ trait Scripting[F[_], K, V] {
def eval(script: String, output: ScriptOutputType[V]): F[output.R]
def eval(script: String, output: ScriptOutputType[V], keys: List[K]): F[output.R]
def eval(script: String, output: ScriptOutputType[V], keys: List[K], values: List[V]): F[output.R]
def evalReadOnly(script: String, output: ScriptOutputType[V]): F[output.R]
def evalReadOnly(script: String, output: ScriptOutputType[V], keys: List[K]): F[output.R]
def evalReadOnly(script: String, output: ScriptOutputType[V], keys: List[K], values: List[V]): F[output.R]
def evalSha(digest: String, output: ScriptOutputType[V]): F[output.R]
def evalSha(digest: String, output: ScriptOutputType[V], keys: List[K]): F[output.R]
def evalSha(digest: String, output: ScriptOutputType[V], keys: List[K], values: List[V]): F[output.R]
def evalShaReadOnly(digest: String, output: ScriptOutputType[V]): F[output.R]
def evalShaReadOnly(digest: String, output: ScriptOutputType[V], keys: List[K]): F[output.R]
def evalShaReadOnly(digest: String, output: ScriptOutputType[V], keys: List[K], values: List[V]): F[output.R]
def scriptLoad(script: String): F[String]
def scriptLoad(script: Array[Byte]): F[String]
def scriptExists(digests: String*): F[List[Boolean]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import io.lettuce.core.{
}
import org.typelevel.keypool.KeyPool

import java.nio.charset.StandardCharsets
import java.time.Instant
import java.util.concurrent.TimeUnit
import scala.concurrent.duration._
Expand Down Expand Up @@ -1340,7 +1341,40 @@ private[redis4cats] class BaseRedis[F[_]: FutureLift: MonadThrow: Log, K, V](
_.eval[output.Underlying](
script,
output.outputType,
// see comment in eval above
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]],
values: _*
).futureLift.map(output.convert(_))
)

override def evalReadOnly(script: String, output: ScriptOutputType[V]): F[output.R] =
async
.flatMap(
_.evalReadOnly[output.Underlying](
script.getBytes(StandardCharsets.UTF_8),
output.outputType,
// see comment in eval above.
Array.emptyObjectArray.asInstanceOf[Array[K with Object]]
).futureLift
)
.map(r => output.convert(r))

override def evalReadOnly(script: String, output: ScriptOutputType[V], keys: List[K]): F[output.R] =
async.flatMap(
_.evalReadOnly[output.Underlying](
script.getBytes(StandardCharsets.UTF_8),
output.outputType,
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]]
).futureLift.map(output.convert(_))
)

override def evalReadOnly(script: String, output: ScriptOutputType[V], keys: List[K], values: List[V]): F[output.R] =
async.flatMap(
_.evalReadOnly[output.Underlying](
script.getBytes(StandardCharsets.UTF_8),
output.outputType,
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]],
values: _*
).futureLift.map(output.convert(_))
Expand All @@ -1356,7 +1390,7 @@ private[redis4cats] class BaseRedis[F[_]: FutureLift: MonadThrow: Log, K, V](
_.evalsha[output.Underlying](
digest,
output.outputType,
// see comment in eval above
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]]
).futureLift.map(output.convert(_))
)
Expand All @@ -1366,7 +1400,45 @@ private[redis4cats] class BaseRedis[F[_]: FutureLift: MonadThrow: Log, K, V](
_.evalsha[output.Underlying](
digest,
output.outputType,
// see comment in eval above
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]],
values: _*
).futureLift.map(output.convert(_))
)

override def evalShaReadOnly(digest: String, output: ScriptOutputType[V]): F[output.R] =
async
.flatMap(
_.evalshaReadOnly[output.Underlying](
digest,
output.outputType,
// see comment in eval above.
Array.emptyObjectArray.asInstanceOf[Array[K with Object]]
).futureLift
)
.map(output.convert(_))

override def evalShaReadOnly(digest: String, output: ScriptOutputType[V], keys: List[K]): F[output.R] =
async.flatMap(
_.evalshaReadOnly[output.Underlying](
digest,
output.outputType,
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]]
).futureLift.map(output.convert(_))
)

override def evalShaReadOnly(
digest: String,
output: ScriptOutputType[V],
keys: List[K],
values: List[V]
): F[output.R] =
async.flatMap(
_.evalshaReadOnly[output.Underlying](
digest,
output.outputType,
// see comment in eval above.
keys.toArray[Any].asInstanceOf[Array[K with Object]],
values: _*
).futureLift.map(output.convert(_))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,10 +602,23 @@ trait TestScenarios { self: FunSuite =>
)
)
_ <- IO(assertEquals(list, List("Let", "us", "have", "fun")))
boolReadOnly <- redis.evalReadOnly("return true", ScriptOutputType.Boolean, List("Foo"))
_ <- IO(assert(boolReadOnly))
_ <- redis.eval(statusScript, ScriptOutputType.Status, List("test"), List("foo"))
either <- redis.evalReadOnly(statusScript, ScriptOutputType.Status, List("test"), List("foo")).attempt
_ <- IO(
assert(
either.left.exists { ex =>
ex.isInstanceOf[RedisCommandExecutionException] &&
ex.getMessage.startsWith("ERR Write commands are not allowed from read-only scripts")
}
)
)
sha42 <- redis.scriptLoad("return 42")
fortyTwoSha <- redis.evalSha(sha42, ScriptOutputType.Integer)
_ <- IO(assertEquals(fortyTwoSha, 42L))
fortyTwoShaReadOnly <- redis.evalShaReadOnly(sha42, ScriptOutputType.Integer)
_ <- IO(assertEquals(fortyTwoShaReadOnly, 42L))
shaStatusScript <- redis.scriptLoad(statusScript)
_ <- redis.evalSha(shaStatusScript, ScriptOutputType.Status, List("test"), List("foo", "bar"))
exists <- redis.scriptExists(sha42, "foobar")
Expand Down

0 comments on commit 81c19b8

Please sign in to comment.