Skip to content

Shard and procedure endpoint ids change on every build #252

Description

@JuanMarchetto

#[shard] and #[procedure] mint their endpoint id with Uuid::new_v4() at macro expansion time, so every compilation produces new URLs. A browser tab left open across a deploy keeps polling the previous binary's id, and the new binary does not serve it.

Four builds of one unchanged source in an application I built produced four ids:

d646e935-433f-44a3-88ea-fe71155e2a28
68c1a3c6-...
a91567b3-...
65adb204-...

What the open tab does then, from the network log of a tab that was never reloaded:

POST /_topcoat/shards/a823c355-9fd9-438f-a470-0507653715a8 -> 200 (3ms, 969B)
POST /_topcoat/shards/a823c355-9fd9-438f-a470-0507653715a8 -> 200 (2ms, 963B)
... rebuild and restart ...
POST /_topcoat/shards/a823c355-9fd9-438f-a470-0507653715a8 -> 405 (0B)

The status is 405 rather than 404 when a catch-all page claims the path. The failure is silent in both cases: fetchAndReplace swaps the empty body in, so the fifteen rows on screen are deleted while the heading, the summary and the filter box outside the shard stay put. Nothing reaches the console and no request fails from the application's point of view. A control run confirms the rebuild causes it and not the restart: stopping and starting the same binary cost the open tab one refused connection, after which it kept polling the same URL and picked up a delivery four seconds later.

@AmeinEskinder reproduced this independently on examples/shard, three builds with only a comment changed, and found two things I had not:

  • #[procedure] has the same construction at procedure.rs:123, so a stale tab's button click fails the same way.
  • topcoat asset bundle compiles the crate, so the bundling step can mint fresh ids partway through a deploy sequence.

There is already a pattern for a stable opaque id in the tree: AssetId::new hashes the crate name, the source file and the asset path. Something equivalent over crate name, module path and ident would survive a rebuild while staying opaque.

One argument against determinism, which belongs in the decision rather than in a reviewer's head: a fresh id per build means a URL someone learned once stops working at the next deploy, and making ids deterministic makes that URL permanent. It is not protection either way, since the page hands the id to anyone who loads it, and shard endpoints need their own authorization regardless.

Related: #247 covers the swap that makes the failure silent, and the id churn is the other half of what an open tab experiences across a deploy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions