Skip to main content

fragments

Function fragments 

Source
pub fn fragments<'a, I, T, K, F>(
    ctx: &Ctx,
    items: &'a [I],
    ttl: Duration,
    key: K,
    build: F,
) -> impl Future<Output = Result<Vec<Fragment>>> + Send + use<'a, I, T, K, F>
where T: Template, K: Fn(&I) -> String, F: Fn(&'a I) -> T,
Expand description

Collection caching, like Rails’ render collection:, cached: true: one Fragment per item, read from KV in bulk.

key gives each item’s cache key (see cache::key); all keys are read with KV bulk reads (up to 100 keys per read, one subrequest each), then only the missing items are rendered with build and stored, one KV write each. The fragments come back in the order of items. Everything else works as in fragment.

Free plan: KV bills a bulk read as one read per key (100,000 a day) and each miss as one write (1,000 a day): a list of 20 posts costs 20 reads per view, and up to 20 writes after the posts change.

§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 a KV key is longer than 512 bytes or ttl is below 60 seconds.
  • Error::Internal when a template fails to render.

§Examples

use std::time::Duration;

use askama::Template;
use axum::{extract::State, response::Html};
use ocre::{Ctx, Result, cache::{self, Fragment}, render};

struct Post { id: i64, title: String, updated_at: String }

#[derive(Template)]
#[template(source = "<li>{{ post.title }}</li>", ext = "html")]
struct Row<'a> { post: &'a Post }

#[derive(Template)]
#[template(source = "<ul>{% for row in rows %}{{ row }}{% endfor %}</ul>", ext = "html")]
struct Index { rows: Vec<Fragment> }

async fn index(State(ctx): State<Ctx>) -> Result<Html<String>> {
    let posts: Vec<Post> = Vec::new(); // e.g. post::all(&ctx, page).await?
    let rows = cache::fragments(
        &ctx,
        &posts,
        Duration::from_secs(86_400),
        |post| cache::key(&[&"posts", &post.id, &post.updated_at, &"row-v1"]),
        |post| Row { post },
    )
    .await?;
    render(&Index { rows })
}