# Problem Statement
I need to implement cache semantics in front of certain IO operations. These IO operations are not mutating, but they are relatively expensive compared to the overhead of cache lookup.
An example would be the OpenID JSON Web Key Set (JWKS) protocol: Parallel HTTP requests coming into a cluster of Clojure HTTP servers may carry a JSON Web Token (JWT), which needs to be verified as signed by a trusted issuer. The JWKS protocol provides a mechanism to retrieve public keys from the trusted issuer for this verification. The ratio of requests to keys can be billions to 1; thus, it is highly conducive to caching in some form.
I have built this caching to date using clojure.cache.wrapped where the cache essentially holds promises to JWKS responses, keyed by JWT key-id. The basic idea is that when a new JWT key-id is encountered, the cache misses and starts an HTTP request to the IDP to retrieve the key, caching a promise. The overhead to kick off the request is lightweight, even if the round-trip response may take a few milliseconds.
Clients of the cache may deref this promise in whatever way makes sense. Long resolved keys deref immediately. Keys in the middle of being resolved may have multiple clients waiting on the promise.
The problem I currently have is related to inefficiency caused by the clojure.cache internal use of (swap!) when the cache is highly contended. In the above scenario, a given key-id typically expires simultaneously. Thus, API clients tend to refresh JWTs around the same time and present a new yet-to-be-cached key to the system.
The net result is a stampede from multiple threads for the same key, thus putting substantial pressure on a specific entry in the cache. Multiple value-fns are called to kick off a JWKS operation for the same key ID, even though the response is stable and we only need one. The system still behaves correctly with the current behavior, but it is less efficient because we issue multiple redundant IO operations to get the same key ID.
One could argue that this IO is "side-effecting" and is thus incompatible with (swap!). While I agree that mutating side effects would certainly be a problem, these "read-only" side effects are not incompatible with the general notion of caching, so it would be ideal if the caching library could support them by eliminating the stampeding re-entrace to the value-fn for the same key.
I have been able to work around the current behavior by wrapping the lookup-or-miss with a construct such as:
```
(:require [clojure.core.cache.wrapped :as cache]))
(defn lookup-or-miss
[cache-atom e value-fn]
(or (cache/lookup cache-atom e)
(locking cache-atom
(cache/lookup-or-miss cache-atom e value-fn))))
```
However, perhaps it makes sense to consider a contribution to the upstream library, assuming others see value in solving the stated problem.
Here are a few thoughts on this front:
1) Introducing locking either in the current wrapped implementation or as an option/alternative for those who need stronger guarantees.
2) Re-implement the clojure.cache.wrapped internals to something that supports at-most-once semantics by getting away from (swap!) in favor of something like (volatile!) + (locking).