EdgeAIG's picture
download
raw
5.07 kB
/**
* Redis support shared by persistence modules.
*
* This module defines a `Redis` service that can send Redis commands and run
* Lua scripts. It does not create a Redis client itself; callers provide a
* `send` function from their client or connection pool. The module also
* provides helpers for describing Lua scripts, loading them once, and running
* them later by their cached Redis id.
*
* @since 4.0.0
*/
import * as Cache from "../../Cache.ts"
import * as Context from "../../Context.ts"
import * as Effect from "../../Effect.ts"
import * as Equal from "../../Equal.ts"
import { constant, identity } from "../../Function.ts"
import * as Hash from "../../Hash.ts"
import * as Schema from "../../Schema.ts"
/**
* Service for sending Redis commands and evaluating cached Lua scripts.
*
* @category services
* @since 4.0.0
*/
export class Redis extends Context.Service<Redis, {
readonly send: <A = unknown>(command: string, ...args: ReadonlyArray<string>) => Effect.Effect<A, RedisError>
readonly eval: <
Config extends {
readonly params: ReadonlyArray<unknown>
readonly result: unknown
}
>(script: Script<Config>) => (...params: Config["params"]) => Effect.Effect<Config["result"], RedisError>
}>()("effect/persistence/Redis") {}
/**
* Creates a `Redis` service from a raw command sender.
*
* **Details**
*
* Lua scripts are loaded through `SCRIPT LOAD`, cached, and then invoked with
* `EVALSHA`.
*
* @category constructors
* @since 4.0.0
*/
export const make = Effect.fnUntraced(function*(
options: {
readonly send: <A = unknown>(command: string, ...args: ReadonlyArray<string>) => Effect.Effect<A, RedisError>
}
) {
const scriptCache = yield* Cache.make({
lookup: (script: Script<any>) => options.send<string>("SCRIPT", "LOAD", script.lua),
capacity: Number.POSITIVE_INFINITY
})
const eval_ = <
Config extends {
readonly params: ReadonlyArray<unknown>
readonly result: unknown
}
>(
script: Script<Config>
) =>
(...params: Config["params"]): Effect.Effect<Config["result"], RedisError> => {
const evalSha = (sha: string) =>
options.send<Config["result"]>(
"EVALSHA",
sha,
script.numberOfKeys(...params).toString(),
...script.params(...params).map((param) => String(param))
)
return Cache.get(scriptCache, script).pipe(
Effect.flatMap(evalSha),
Effect.catchIf(
(error) => String(error.cause).includes("NOSCRIPT"),
() => Cache.refresh(scriptCache, script).pipe(Effect.flatMap(evalSha))
)
)
}
return identity<Redis["Service"]>({
send: options.send,
eval: eval_
})
})
type ErrorTypeId = "~effect/persistence/Redis/RedisError"
const ErrorTypeId: ErrorTypeId = "~effect/persistence/Redis/RedisError"
/**
* Error raised by Redis command or script execution.
*
* @category errors
* @since 4.0.0
*/
export class RedisError extends Schema.ErrorClass<RedisError>(ErrorTypeId)({
_tag: Schema.tag("RedisError"),
cause: Schema.Defect()
}) {
/**
* Marks this value as a Redis persistence error for runtime guards.
*
* @since 4.0.0
*/
readonly [ErrorTypeId]: ErrorTypeId = ErrorTypeId
}
type ScriptTypeId = "~effect/persistence/Redis/Script"
const ScriptTypeId: ScriptTypeId = "~effect/persistence/Redis/Script"
/**
* Typed descriptor for a Redis Lua script.
*
* **Details**
*
* It defines the Lua source, parameter-to-argument mapping, Redis key count,
* and result type used by `Redis.eval`.
*
* @category Scripting
* @since 4.0.0
*/
export interface Script<
Config extends {
readonly params: ReadonlyArray<unknown>
readonly result: unknown
}
> {
readonly [ScriptTypeId]: {
readonly params: Config["params"]
readonly result: Config["result"]
}
readonly lua: string
readonly params: (...params: Config["params"]) => ReadonlyArray<unknown>
readonly numberOfKeys: (...params: Config["params"]) => number
/**
* Set the return type of the script.
*/
withReturnType<Result>(): Script<{
params: Config["params"]
result: Result
}>
}
const ScriptProto = {
[ScriptTypeId]: {
params: identity,
result: identity
},
withReturnType() {
return this
},
[Equal.symbol](that: unknown): boolean {
return this === that
},
[Hash.symbol](): number {
return Hash.random(this)
}
}
/**
* Constructs a typed Redis Lua script descriptor.
*
* **Details**
*
* The result type defaults to `void` and can be refined with
* `withReturnType`.
*
* @category Scripting
* @since 4.0.0
*/
export const script = <Params extends ReadonlyArray<any>>(
f: (...params: Params) => ReadonlyArray<unknown>,
options: {
readonly lua: string
readonly numberOfKeys: number | ((...params: Params) => number)
}
): Script<{
params: Params
result: void
}> =>
Object.assign(Object.create(ScriptProto), {
...options,
params: f,
numberOfKeys: typeof options.numberOfKeys === "number" ? constant(options.numberOfKeys) : options.numberOfKeys
})

Xet Storage Details

Size:
5.07 kB
·
Xet hash:
778656dc931a7ced3957a4b1e1c38f990f24b4e70129e1b7a41a09fa72ae9abf

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.