Skip to content

buildSegment: custom SegmentKey fields are silently dropped by hashSegment (cache mis-segmentation) #284

Description

@JonasJesus42

Summary

buildSegment returns a SegmentKey, but hashSegment() only serializes a fixed set of known fields — device, loggedIn, salesChannel, regionId, flags. Any custom field a consumer adds to the returned object is silently dropped from the cache key, with no type error and no runtime warning. The result is a cache-segmentation that appears to work but doesn't: requests that should land in different cache buckets share one entry and the worker serves the wrong cached response.

How it bites

createDecoWorkerEntry({ buildSegment }) accepts a buildSegment: (request) => SegmentKey. A site that needs an extra cache dimension naturally writes:

buildSegment: (request) => ({
  device,
  ...(regionId ? { regionId } : {}),
  ...(delivery ? { delivery } : {}), // custom dimension — store/CEP selection
}),

This compiles (excess-property checks are skipped on object spread) and looks correct, but hashSegment ignores delivery:

function hashSegment(seg: SegmentKey): string {
  const parts: string[] = [seg.device];
  if (seg.loggedIn) parts.push("auth");
  if (seg.salesChannel) parts.push(`sc=${seg.salesChannel}`);
  if (seg.regionId) parts.push(`r=${seg.regionId}`);
  if (seg.flags?.length) parts.push(`f=${seg.flags.sort().join(",")}`);
  return parts.join("|");
}

So two requests that differ only by the custom dimension produce the same __seg hash → same cache key → the first-cached response is served to both. We hit this on a storefront where store/CEP selection (delivery-promise) must vary the PLP cache: the x-cache-segment header read desktop|r=RJ even with the store cookies set, and the store PLP kept serving the no-store cached page (1192 products instead of 924). Confirmed by invoking the loader directly (correct, store-filtered result) vs. the cached SSR path (wrong).

Workaround

Route the custom token through the one extensible field hashSegment honors — flags:

...(delivery ? { flags: [delivery] } : {}),

x-cache-segment then correctly shows desktop|r=RJ|f=pp:<storeId> and the cache varies.

Suggested fix (any one)

  1. Serialize all enumerable keys in hashSegment (stable-sorted) so custom dimensions just work; or
  2. Make SegmentKey exact / reject excess properties so the compiler catches an unsupported field at the buildSegment call site; or
  3. Document clearly that flags is the only consumer-extensible segmentation field, and that custom keys are ignored.

Option 1 is the least surprising for consumers. Option 2 turns the silent failure into a compile error.

Environment

  • @decocms/start (resolved from src/sdk/workerEntry.tshashSegment, SegmentKey)
  • Cloudflare Workers, TanStack Start storefront

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions