×marble
all posts
Jun 13, 2026·12 min read

Sub-100ms Feature Serving with Edge Functions: Vercel + Cloudflare Patterns

Edge functions can serve personalization features in sub-20ms p99. The patterns that work on Vercel Edge and Cloudflare Workers, with KV/Edge Config trade-offs.

Alex Shrestha·Founder, ×marble

Sub-100ms Feature Serving with Edge Functions: Vercel + Cloudflare Patterns

TL;DR.

  • Sub-100ms feature serving with edge functions is a budget problem, not an algorithm problem. The numbers are tight: TLS, middleware, DOM hydration, and the function itself all share the same <100 ms envelope.
  • Vercel Edge Config returns most reads in <5 ms and 99% under 15 ms at p99, because it pushes data to every region before the first request.
  • Cloudflare Workers KV serves hot reads with a ~12 ms median when cached at the colo and runs <5 ms p99 against its internal backend. Cache misses can land anywhere from 50 ms to 200 ms.
  • Both stores are eventually consistent. Writes propagate globally in ~60 s (Workers KV) or "a few seconds" (Edge Config). Anything that needs read-after-write should not live in a KV at the edge.
  • The pattern that works: hot-path features (segments, flags, A/B buckets) at the edge, cold-path features (deep history, embeddings) at an origin store, and a deterministic fallback when either layer fails.

Most personalization features die in the middleware. The model is fine. The features are fine. The user just bounced because the page hung for 240 ms while you waited on a region-locked database. If you're trying to serve personalization features inside a real <100 ms page-load budget, the edge isn't optional — but Vercel Edge Config and Cloudflare Workers KV are very different tools, and the choice changes your architecture. This post walks the trade-offs with the numbers that matter.

The sub-100ms budget is mostly not yours

Before talking about edge stores, look at where the time actually goes. A typical Largest Contentful Paint budget on a mid-range mobile device on a 4G connection is around 2.5 s — Google's "good" LCP threshold. Inside that, your <100 ms window for a personalized response has to absorb DNS, TCP, TLS handshake, the initial server processing for the document, and your function code. On a cold Worker start with a cache miss to a regional KV, you can lose 200 ms before your handler returns.

The implication: sub-100ms feature serving at the edge is a latency-budget exercise, not a feature engineering exercise. You're not trying to write a faster model. You're trying to make sure every read inside the function is bounded — and that means the data store has to be co-located with the function. That's what Edge Config and Workers KV solve, and that's also where they diverge.

If you're new to the architecture, the reference architecture for real-time personalization lays out where edge feature serving sits in the broader stack. This post is about the inner loop.

Vercel Edge Config vs Cloudflare Workers KV: what they actually do

These two services look superficially similar — global key-value lookups inside an edge runtime — but they're optimized for opposite workloads. Vercel Edge Config personalization works well precisely because Vercel does not treat it as a key-value store. According to Vercel's launch post, Edge Config is a push-based distribution system: every region receives the full config before the first request hits it. Reads are local, not networked. Vercel quotes most reads under 5 ms and 99% under 15 ms at p99.

Cloudflare Workers KV personalization works by a different model. It's a hybrid: small objects go to distributed databases, large ones to R2, with a tiered cache that races multiple backends. From Cloudflare's KV redesign post, the new backend hits <5 ms p99 internally for reads. The user-facing number depends on cache state:

  • Hot reads — the key is in the colo's local cache. Median latency around 12 ms in Cloudflare's published figures.
  • Cold reads — the key has to traverse the regional and central tiers. Add up to 80–200 ms p99 depending on the colo.

The practical consequence: Edge Config is bounded. Workers KV has a long tail. If you need a hard p99 SLO and your data set fits the size limits, Edge Config is the simpler bet. If you have a larger, more dynamic personalization payload — millions of user segments, frequent partial updates — Workers KV is the right shape, and you accept the cold-miss tail.

Hot-path features vs cold-path features

The most useful framing we've found for sub-100ms feature serving with edge functions is hot path vs cold path. Borrow it from systems work: hot path is what runs on every request, cold path is what runs out-of-band.

Hot-path features belong in the edge store:

  • User segment / cohort
  • Active feature flags and A/B bucket
  • Country / region overrides
  • Per-user lastSeen bucket (coarse — "today", "this week", "stale")
  • Soft block / soft allow lists
  • Deterministic ranking weights

These are small, change slowly, and are read on every request. Edge Config and Workers KV are both fine for this.

Cold-path features belong at the origin or in a separate store:

  • Full user embedding vectors
  • Long event history (>90 days)
  • Cross-user collaborative filtering tables
  • Knowledge-graph traversal results
  • Anything requiring strong read-after-write

You don't read these inside the request. You either compute against them async (in a queue worker) and write a summarized output back to the edge store, or you call out to a regional origin and accept a higher latency for the small fraction of requests that need it. The split is the architecture. Trying to put cold-path features at the edge is how you get a <100 ms budget that ships at 280 ms p99.

This isn't theoretical. The teams we see succeed run hot-path features at the edge and treat the origin store as a slow tier. The ones that fail try to serve embedding-similarity queries from a Worker.

Eventual consistency and the propagation window

This is where Vercel Edge Config personalization and Cloudflare Workers KV personalization both punish you if you don't plan for it. Both stores are eventually consistent — they trade strong consistency for global availability.

  • Vercel Edge Config: Vercel describes write propagation as "a few seconds" globally. Pragmatically, expect 1–5 s before all regions see a new value.
  • Cloudflare Workers KV: Cloudflare's docs put the propagation window at "up to 60 seconds or more." The default cache TTL is 60 s, and the consistency window matches it.

If you're using either for personalization, the question is: does anything in your hot path need read-after-write? If a user updates their preference and immediately reloads, will they see the old value for up to a minute? Sometimes that's fine (preferences, opt-ins). Often it's not (consent flags, payment-tier changes that gate access).

The pattern that works:

  1. Write to a strongly consistent regional store (Postgres, D1, Durable Objects) as the source of truth.
  2. Async replicate to the edge store for read-only access.
  3. For requests where the user just changed something, set a short signed cookie that overrides the edge value for the next 60 s.

That third step is the one most teams skip. It's also the one that makes the user experience feel correct during the propagation window.

For deeper context on how this fits the broader stack, see the marketing engineer's personalization stack.

Bot vs human routing at the edge

A specific edge personalization pattern that pays for itself: route bots and humans to different code paths before you do any feature work. Bots — Googlebot, Bingbot, SEO tools, scrapers — should usually get the canonical, non-personalized version of the page. Humans get the personalized one.

The split lives in middleware:

// Vercel Edge Middleware example
export default async function middleware(req: NextRequest) {
  const ua = req.headers.get('user-agent') || ''
  const isBot = /(bot|crawler|spider|googlebot|bingbot)/i.test(ua)

  if (isBot) {
    return NextResponse.rewrite(new URL('/canonical' + req.nextUrl.pathname, req.url))
  }

  // Personalization branch: only humans pay this cost
  const segment = await get('segment:' + req.cookies.get('uid')?.value)
  // ... attach segment header, route to variant
}

The pattern works for three reasons. First, you skip the personalization read entirely for bots, which removes them from your edge-store hit budget and keeps your cache hit ratio honest. Second, you avoid serving personalized content to crawlers, which is bad for SEO and worse for your canonical tag strategy. Third, bot traffic is bursty — a single SEO audit can quintuple your edge function invocations, and you don't want personalization features doing useless work for that traffic.

A related pattern: serve a stable, identical document to bots and inject personalization client-side for humans. This keeps the cacheable HTML cacheable while preserving the personalized experience for real users.

Cache patterns that actually move p99

Two cache patterns we've seen consistently move sub-100ms feature serving from "usually fast" to "always fast":

1. Cookie-encoded segment. Cookies are sent on every request and live at zero edge latency. If you can fit the user's segment, A/B bucket, and a lastUpdated timestamp into a single signed cookie (~200 bytes), you skip the KV read entirely on the hot path. The KV becomes a source-of-truth for cold starts and a fallback when the cookie is missing or expired.

2. Stale-while-revalidate at the edge. Read the edge store with a short TTL, but always return the cached value immediately and trigger an async refresh in the background. The Cache API on both Vercel and Cloudflare supports this directly. The first request after a TTL expiry pays the cold-read cost; everyone else in that window gets the cached value at memory-access speed.

A third pattern — coarse cohorts instead of per-user keys — deserves its own callout. If you have 50 user segments and 50M users, store the 50 segment payloads in the edge store, not 50M user records. Look up segment:premium-us-mobile instead of user:abc123. KV cache hit ratio goes from "depends on the user" to "always 100% after the first request per colo." This single change is the highest-impact cache pattern we've found for edge personalization.

For more on why coarse-grained features beat per-user features in many cases, see cold-start problem and day-zero personalization, which covers the same trade-off from a different angle.

Failure modes you'll actually hit

The edge looks magical until something breaks. The failure modes worth designing for:

  • Cold-region miss. New region gets first request; KV read takes 180 ms. Mitigation: warm critical keys via a synthetic monitor that hits each region post-deploy.
  • Cookie spoofing. Anyone can write a cookie. If your segment is in a cookie, sign it. Otherwise an attacker hands themselves the "VIP" segment.
  • Edge store outage. Treat the edge store as a cache, not a database. If get() throws or times out, fall back to a deterministic default segment. The page should always render — it just renders less personalized.
  • Propagation race. User changes a setting; the write hits region A but the next read goes to region B and gets the old value. Use the cookie-override pattern above for the propagation window.
  • Function CPU budget. Vercel Edge Functions cap at 25 ms CPU. Cloudflare Workers cap at 50 ms CPU on the free plan, with paid plans going higher. CPU-heavy personalization (running an embedding model, complex ranking) doesn't fit. Push it to a regional function or async pipeline.

Most of these aren't catastrophic on their own. They become catastrophic when you ship without thinking about them, and a regional incident drives every one at the same time. The discipline: each personalization read must have a typed default and a hard timeout. No exceptions.

For a broader treatment of how to think about explainability and fallbacks, see explainable recommendations and the path of a recommendation.

How ×marble fits in

The patterns above are what we use internally to serve personalization for ×marble and our sub-products. Vivo (vivo.timesmarble.com) generates per-user daily briefings — every request reads a user's interest graph from an edge-served projection, with the full knowledge graph living in a regional store. Marble × Music (marblexmusic.com) does the same for Spotify recommendations.

The product takeaway: you do not need to build the hot path / cold path split yourself. ×marble runs the cold-path knowledge graph and the hot-path edge projection, and gives you a single SDK that hits the edge tier in sub-20 ms p99. If you'd rather not spend a quarter wiring up Edge Config replication, signed cookies, and stale-while-revalidate, that's what we built. See personalization platforms in 2026 for the broader product positioning.

FAQ

What is sub-100ms feature serving with edge functions?

Sub-100ms feature serving with edge functions is the practice of returning personalization features — user segments, feature flags, A/B buckets, ranking weights — from a globally distributed edge runtime in under 100 milliseconds end-to-end. Both Vercel Edge Functions (paired with Edge Config) and Cloudflare Workers (paired with Workers KV) can hit this latency when the data set is small enough to fit in their respective edge stores and the access pattern is read-heavy with infrequent writes.

How fast is Vercel Edge Config personalization?

Vercel publishes Edge Config read latency as "often less than 1 ms" with 99% of reads completing under 15 ms at p99. The number is achievable because Edge Config pushes data to every region before the first request, so reads are local rather than networked. This makes Vercel Edge Config personalization a strong fit for feature flags, A/B test assignment, and small per-segment configuration — anything that fits within Edge Config's size limits and tolerates a few-second write propagation window.

What is the eventual consistency window for Cloudflare Workers KV?

Cloudflare Workers KV propagates writes globally in up to 60 seconds. The default cache TTL is 60 seconds, and the consistency window matches it. This means a write to one region may not be visible from another region for up to a minute. For Cloudflare Workers KV personalization use cases where the user has just made a change and expects to see it immediately, pair the KV write with a short signed cookie that overrides the cached value during the propagation window.

When should I use Edge Config vs Workers KV?

Use Vercel Edge Config when your personalization payload is small, your write frequency is low, and you need a tight p99 latency ceiling — feature flags, A/B test configs, redirect rules, IP blocks, coarse user segments. Use Cloudflare Workers KV when your payload is larger (millions of keys), your writes are more frequent, or you're already on the Cloudflare stack. Workers KV trades a longer cold-read tail for greater scale and write throughput; Edge Config trades scale for predictable read latency.

Further reading

the product behind these notes

×marble is the personalization graph.

One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.

See ×marble