Skip to content

feat(artifact): load newer manifests on demand - #319

Open
lokewate wants to merge 2 commits into
feat/manifest-explicit-versionsfrom
feat/manifest-on-demand
Open

lokewate wants to merge 2 commits into
feat/manifest-explicit-versionsfrom
feat/manifest-on-demand

Conversation

@lokewate

@lokewate lokewate commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Replicas load their artifact sets at different times. A flow started by a replica that loaded manifest_v4.json can run its next step on a replica that started before v4 was published. This PR makes Set.Registry load a version above the highest it has, instead of failing that step.

Stack

Manifest versioning, in five PRs to review and merge in order. Each one builds and passes its tests on its own.

  1. feat(artifact)!: load versioned manifests as a set #305 artifact: load versioned manifests as a set
  2. feat(artifact)!: resolve explicitly versioned rows across manifests #346 artifact: resolve explicitly versioned rows across manifests
  3. feat(artifact): load newer manifests on demand #319 artifact: load newer manifests on demand ← this PR
  4. feat(workflow)!: carry the manifest version through every workflow #322 workflow: carry the manifest version through every workflow
  5. feat(taskflow)!: resolve a task's templates through its manifest version #306 taskflow: resolve a task's templates through its manifest version

#320 (validate every artifact a manifest lists) and #321 (manifestcheck) are parked: pinning doesn't need them, and a release job can bring validation back later.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Other (please describe):

Changes Made

  • Set.Registry loads a missing newer version on demand. Current does not move: a new manifest becomes current when the process reloads the set.
  • A probe fetches only up to the version asked for, so a broken later manifest cannot fail it.
  • It holds no lock that lookups wait on. A one-slot channel serializes probes across the loader round trips, and a caller waiting for it gives up when its own context ends; mu is taken only to read state and to install what a probe fetched.
  • Back-off. After a probe that fails or finds nothing, the next one waits 10s, so retries of a flow pinned to an unpublished version do not each cost a loader round trip. A probe cut short by its caller's context does not start the wait, since it says nothing about what is published.
  • GetExplicit probes the same way when no loaded manifest lists the version asked for, and reports a cancelled request as cancelled, not as ErrNotFound.

Testing

  • I have tested this change locally
  • I have added tests that prove my fix is effective or that my feature works
  • I have tested edge cases
  • All existing tests pass

Run with go test -race ./artifact/. The tests cover loading a manifest published after the set was loaded, a bad manifest found on demand (reported, not cached), a probe stopping at the version asked for, back-off after both empty and failed probes, a probe not blocking lookups of loaded versions, concurrent Registry calls, a pin still loading newer versions on demand, GetExplicit finding a version only a newer manifest lists, a caller waiting behind a stuck probe giving up at its deadline, and a cancelled probe not starting the back-off. The last two fail without the fix (the first hangs).

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have checked that there are no merge conflicts

Related Issues

Part of OpenNSW/one-trade-artifacts#243. Host: OpenNSW/nsw-srilanka#660.

Screenshots/Demo

N/A

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4f95df01-7f42-4839-87d1-33ff5068c098
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mushrafmim mushrafmim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

lokewate and others added 2 commits October 26, 2026 20:08
Replicas load their sets at different times, so a flow pinned by a
replica that started after a manifest was published can run its next
step on a replica that started before it. Set.Registry now loads a
version above the highest it has, and GetExplicit probes for a newer
manifest when no loaded one lists the version asked for.

A probe fetches only up to the version asked for, so a broken later
manifest cannot fail it, and holds no lock that lookups of loaded
versions wait on. After a probe that fails or finds nothing it waits
10s before probing again, so retries of a pinned flow do not each cost
a loader round trip. Current does not move until the set is reloaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elled probes

Two edge cases in on-demand probing:

- A caller waiting for another caller's probe could not give up: the
  probe lock was a sync.Mutex, held across loader round trips. With a
  loader that has no timeout of its own (the GitHub loader uses
  http.DefaultClient), a probe started without a deadline could block
  every waiter indefinitely. The lock is now a one-slot channel, and a
  waiter returns its context's error when its context ends.
- A probe cut short by its caller's context (a browser closing during a
  static-data request) counted as "found nothing" and started the
  10-second back-off for everyone, so a flow on a newly published
  manifest was told the version was unknown. A cancelled probe now
  leaves the back-off as it was, and GetExplicit reports the
  cancellation instead of ErrNotFound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mushrafmim
mushrafmim force-pushed the feat/manifest-on-demand branch from adc34a6 to 2a498a2 Compare October 26, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants