Skip to content

feat(control-plane): reconcile pallet-resource-market offers with live capacity - #71

Merged
flo2517 merged 1 commit into
mainfrom
feat/issue-15-resource-market-reconciler
Aug 6, 2026
Merged

feat(control-plane): reconcile pallet-resource-market offers with live capacity#71
flo2517 merged 1 commit into
mainfrom
feat/issue-15-resource-market-reconciler

Conversation

@flo2517

@flo2517 flo2517 commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Closes #15.

Third and final slice, on top of the delegated calls (#69, merged) and the bridge read/write layer (#70, merged).

What this adds

internal/resourcemarket.Reconciler:

  • Lists the same live schedulable-provider view the scheduler ranks against.
  • Computes each provider's desired ResourceOffer from its declared total capacity (the ceiling the scheduler's atomic Postgres capacity check already uses — not the fast-changing available figure, which stays off-chain in Redis by design).
  • Compares against the chain's FinalizedOffer and calls AnnounceOfferFor only when it actually differs.
  • Withdraws (RemoveOfferFor) any provider that drops out of the schedulable set (deregistered, or heartbeat went stale).

Withdrawal tracking is in-memory, not persisted: after a restart, a vanished provider's stale offer survives up to one reconcile interval before being noticed and removed. Documented as a deliberately bounded, self-healing gap, not a security control — no scheduling decision consults on-chain offers yet, so a briefly-stale offer has no live consequence today.

Wired into cmd/controlplane/main.go via a marketBridge adapter combining *blockchainbridge.Registrar's write methods with *blockchainbridge.RPCClient's read methods into the resourcemarket.Market interface, run alongside the existing provider-join outbox reconciler.

Tests

8 new tests: announces a new provider, skips an already-correct offer, updates a changed offer, withdraws + forgets a vanished provider, skips providers without a usable key/capabilities, tolerates a directory failure without panicking, keeps retrying a failed announce, clampToUint32 boundaries.

Verified

gofmt -l .; go build ./...; go vet ./...; go test ./... — full suite green against merged main.

Still open (deliberately out of scope here)

  • The scheduler consulting finalized on-chain offers before selecting a provider.
  • Integer pricing (needs a proto change + consumer analysis).

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

…e capacity

Third and final slice of #15, on top of the delegated calls (#69) and
the bridge read/write layer (#70).

internal/resourcemarket/reconciler.go:

- ReconcileOnce lists the same live schedulable-provider view the
  scheduler ranks against, computes each provider's desired
  ResourceOffer from its declared *total* capacity (the ceiling the
  scheduler's atomic Postgres capacity check already uses -- not the
  fast-changing "available" figure, which stays off-chain in Redis by
  design), compares it to the chain's FinalizedOffer, and calls
  AnnounceOfferFor only when it actually differs. Providers that drop
  out of the schedulable set (deregistered, or heartbeat went stale)
  have their offer withdrawn via RemoveOfferFor.

- Withdrawal tracking (the offering map) is in-memory, not persisted:
  after a Control Plane restart, a provider that vanished during the
  outage keeps a stale on-chain offer for up to one reconcile interval
  before it's noticed and removed. This is a deliberately bounded,
  self-healing gap, not a security control -- no scheduling decision
  consults on-chain offers yet, so a briefly-stale offer has no live
  consequence today.

- clampToUint32 protects the pallet's u32 CPU field from a total large
  enough to overflow it, instead of silently wrapping.

Wired into cmd/controlplane/main.go via a marketBridge adapter that
combines *blockchainbridge.Registrar's write methods with
*blockchainbridge.RPCClient's read methods into the resourcemarket.Market
interface the reconciler depends on, run alongside the existing
provider-join outbox reconciler.

Adds 8 tests: announces a new provider, skips an already-correct
offer, updates a changed offer, withdraws a provider that drops out of
the schedulable set (and forgets it afterward), skips providers
without a usable 32-byte key or capabilities, tolerates a directory
read failure without panicking, keeps retrying a failed announce on
the next pass, and clampToUint32's boundary cases.

Verified (control-plane/): gofmt -l .; go build ./...; go vet ./...;
go test ./... -- full suite green against merged main, including this
package (0.027s).

This closes out #15's three planned slices. Still explicitly out of
scope, left for follow-up: the scheduler consulting finalized on-chain
offers before selecting a provider, and integer pricing (needs a
proto change + consumer analysis).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@flo2517
flo2517 merged commit 15a6faa into main Aug 6, 2026
4 checks passed
@flo2517
flo2517 deleted the feat/issue-15-resource-market-reconciler branch August 6, 2026 14:05
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.

Synchronize resource offers and authoritative capacity with the chain

2 participants