Skip to content

Read-only resources fail-fast on IsNotFound, causing redundant backoff when consumer has a watch #114

@sourcehawk

Description

@sourcehawk

Context

pkg/component/read.go:readResource does a straight Client.Get on each read-only
resource and treats every error, including IsNotFound, as a hard failure. The error
bubbles up from Component.Reconcile and propagates to controller-runtime, which
applies exponential backoff.

The problem

When the consumer has a watch on the type of the missing read-only resource (i.e.
they'd be re-enqueued the moment it appears), the backoff is purely redundant —
it just delays the eventual reconcile and produces error-log noise in the
meantime. The watch-driven enqueue would react in milliseconds.

This is a common shape: an operator references external objects (user-provided
Secrets carrying credentials, sibling-operator-produced contract CRDs) and wants
to observe them, hash their content for rollout triggers, etc. The cleanest place
to express "I'm waiting for this object" is the Component itself.

Workarounds today (all unsatisfying)

  1. Accept the backoff and log noise.
  2. Pre-resolve refs in the controller body before building the Component, attach
    a synthetic Blocked guard if any are missing, skip registering the read-only
    resource that pass. Loses framework symmetry; the existence check lives in
    user code, the hash flow lives in the framework.
  3. Special-case IsNotFound in the controller body. Asymmetric and hacky.

Proposed solution shape

A ResourceOptions flag (or a separate constructor) that opts the read-only
resource into "Blocked on absence" behavior: on IsNotFound, treat the resource
as Guard-Blocked with a reason like "waiting for <kind> <namespace>/<name>"
rather than returning an error. Subsequent resources in the Component skip per
the existing guard-blocked semantics. The consumer is expected to have a watch
on the type; the framework neither requires nor verifies that.

This keeps fail-fast as the default (correct when no watch exists) while giving
consumers a clean opt-in for the watch-backed case.

Open question

Whether this belongs on ResourceOptions (per-resource), as a new
secret.NewReadOnlyBuilder().BlockedOnAbsence() style builder, or as a separate
Component registration method. Happy to PR whichever shape you'd prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions