Hyperlinkv0.8.0-beta.28

RequestResolver

RequestResolver.withCacheconsteffect/RequestResolver.ts:1077
<A extends Request.Any>(options: {
  readonly capacity: number
  readonly strategy?: "lru" | "fifo" | undefined
}): (self: RequestResolver<A>) => Effect.Effect<RequestResolver<A>>
<A extends Request.Any>(
  self: RequestResolver<A>,
  options: {
    readonly capacity: number
    readonly strategy?: "lru" | "fifo" | undefined
  }
): Effect.Effect<RequestResolver<A>>

Adds a bounded in-memory cache to a request resolver.

When to use

Use to reuse completed results for repeated equal request values while still passing a RequestResolver to Effect.request.

Details

Running the returned effect creates the cache and returns a wrapped resolver. The cache stores completed success or failure results by request equality up to capacity. The strategy option controls eviction order and defaults to "lru"; "fifo" keeps insertion order.

Gotchas

Entries do not expire by time, and completed failures are cached the same as successes. Request equality controls cache hits.

export const withCache: {
  <A extends Request.Any>(options: {
    readonly capacity: number
    readonly strategy?: "lru" | "fifo" | undefined
  }): (self: RequestResolver<A>) => Effect.Effect<RequestResolver<A>>
  <A extends Request.Any>(self: RequestResolver<A>, options: {
    readonly capacity: number
    readonly strategy?: "lru" | "fifo" | undefined
  }): Effect.Effect<RequestResolver<A>>
} = dual(2, <A extends Request.Any>(self: RequestResolver<A>, options: {
  readonly capacity: number
  readonly strategy?: "lru" | "fifo" | undefined
}): Effect.Effect<RequestResolver<A>> =>
  Effect.sync(() => {
    const strategy = options.strategy ?? "lru"
    const cache = MutableHashMap.empty<A, {
      readonly entry: Request.Entry<A>
      exit: Request.Result<A> | undefined
    }>()
    return makeWith({
      ...self,
      runAll(entries, key) {
        return Effect.onExit(self.runAll(entries, key), () => {
          let toRemove = MutableHashMap.size(cache) - options.capacity
          if (toRemove <= 0) return Effect.void
          for (const k of MutableHashMap.keys(cache)) {
            MutableHashMap.remove(cache, k)
            toRemove--
            if (toRemove <= 0) break
          }
          return Effect.void
        })
      },
      preCheck(entry) {
        const ocached = MutableHashMap.get(cache, entry.request)
        if (ocached._tag === "None") {
          const cached = { entry, exit: undefined as Request.Result<A> | undefined }
          MutableHashMap.set(cache, entry.request, cached)
          const prevComplete = entry.completeUnsafe
          entry.completeUnsafe = function(exit) {
            cached.exit = exit as any
            prevComplete(exit)
          }
          return true
        }
        const cached = ocached.value
        if (cached.exit) {
          if (strategy === "lru") {
            MutableHashMap.remove(cache, cached.entry.request)
            MutableHashMap.set(cache, cached.entry.request, cached)
          }
          entry.completeUnsafe(cached.exit as any)
        } else {
          cached.entry.uninterruptible = true
          const prevComplete = cached.entry.completeUnsafe
          cached.entry.completeUnsafe = function(exit) {
            prevComplete(exit)
            entry.completeUnsafe(exit)
          }
        }
        return false
      }
    })
  }))