Skip to main content

fetch

Function fetch 

Source
pub fn fetch<'a, T, F, Fut>(
    ctx: &Ctx,
    key: &'a str,
    ttl: Duration,
    compute: F,
) -> impl Future<Output = Result<T>> + Send + use<'a, T, F, Fut>
where T: Serialize + DeserializeOwned, F: FnOnce() -> Fut, Fut: Future<Output = Result<T>>,
Expand description

Read-through cache, like Rails’ Rails.cache.fetch: the value under key in KV, or else the result of compute.

On a hit, the stored JSON is decoded as T and compute does not run. On a miss, compute runs and its value is stored for ttl (at least MIN_TTL, truncated to whole seconds). KV failures (over a daily limit, a value from an older deploy that no longer decodes as T) are logged with LOG_PREFIX and the value is computed, so the handler keeps working.

Stored values are the JSON of T; put a version in the key (:v1) and bump it when T changes. The returned future is Send, so it can be awaited in axum handlers. A key already read or written during the same request is answered from memory (Rails’ local cache). With STORE_VAR set to "null", compute always runs and nothing is stored.

Free plan (September 2026): one KV read per call (100,000 a day), plus one KV write on a miss (1,000 a day; one per second per key). A key refreshed every ttl seconds costs up to 86,400 / ttl writes a day.

§Errors

  • Error::Internal (500) when the CACHE binding is missing, naming the fix: ocre g cache adds CACHE: bindings.kv(), to cloudflare.config.ts.
  • Error::Internal when key is empty or longer than 512 bytes, or ttl is below 60 seconds.
  • Error::Internal when the computed value does not serialize to JSON.
  • Any error returned by compute, unchanged (nothing is stored).

KV read and write failures are not errors: they are logged.

§Examples

use std::time::Duration;

use axum::{Json, extract::State};
use ocre::{Ctx, Result};

async fn index(State(ctx): State<Ctx>) -> Result<Json<Vec<Post>>> {
    let posts: Vec<Post> = ocre::cache::fetch(&ctx, "posts:recent:v1", Duration::from_secs(600), || async {
        recent(&ctx).await // e.g. a D1 query
    })
    .await?;
    Ok(Json(posts))
}