Skip to content

fix: resolve readiness WaitReady blocking for 5 minutes on startup - #408

Open
AlinsRan wants to merge 3 commits into
masterfrom
fix/readiness-wait-ready-block-v2
Open

fix: resolve readiness WaitReady blocking for 5 minutes on startup#408
AlinsRan wants to merge 3 commits into
masterfrom
fix/readiness-wait-ready-block-v2

Conversation

@AlinsRan

@AlinsRan AlinsRan commented May 8, 2026

Copy link
Copy Markdown
Contributor

Root Cause

readiness.Start() is asynchronous. A controller's reconcile loop can call Done() before Start() finishes registering resources into r.state. When this happens:

  1. Done() checks r.state[gvk] — entry doesn't exist yet (race condition)
  2. Done() returns early without doing anything
  3. Start() later registers the resource into r.state
  4. Nobody ever calls Done() again for that resource
  5. WaitReady blocks until the full 5-minute timeout

Changes

  • Core fix: Done() now blocks on <-r.started before touching r.state, ensuring Start() has finished registering all resources before any Done() call can proceed
  • Semantic fix: WaitReady() returns false on timeout instead of true — timed out ≠ ready
  • Cleanup: Remove unnecessary mutex in registerState() — safe because Done() is now guaranteed to run after Start() closes r.started, so registerState() is always called before any concurrent Done() access
  • Observability: Add log statements to trace readiness lifecycle

Testing

  • go build ./... passes
  • go vet ./... passes

Summary by CodeRabbit

  • Bug Fixes

    • Fixed readiness timeout logic to return correct status
    • Improved readiness state initialization to ensure proper synchronization
  • Chores

    • Enhanced readiness state logging with more detailed diagnostic information

Backport fixes from upstream apache/apisix-ingress-controller#2663.

Root cause: readiness.Start() is asynchronous. If a controller's
reconcile loop calls Done() before Start() finishes registering
resources, Done() finds no state entry and returns early. The resource
is never removed from state, causing WaitReady to block until the
5-minute timeout.

Changes:
- Done() now waits for Start() to complete (<-r.started) before
  operating on state, eliminating the race condition
- WaitReady() returns false on timeout instead of true (semantic fix:
  timed-out != ready)
- Remove unnecessary mutex in registerState() since Done() is now
  guaranteed to run after Start() closes r.started
- Add log statements for easier debugging of readiness lifecycle
Copilot AI review requested due to automatic review settings May 8, 2026 03:31
@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ba8e9e1-9f83-4a12-9c6c-0803d075a018

📥 Commits

Reviewing files that changed from the base of the PR and between e269dd2 and 9ed7940.

📒 Files selected for processing (2)
  • internal/manager/readiness/manager.go
  • internal/manager/readiness/manager_test.go
📝 Walkthrough

Walkthrough

The readiness manager now synchronizes initialization and resource cleanup: Done() waits for Start() to complete before modifying state. WaitReady() correctly returns false on timeout instead of true. Logging is enhanced with explicit registration counts and resource details.

Changes

Readiness Manager Lifecycle Fixes

Layer / File(s) Summary
Initialization Synchronization
internal/manager/readiness/manager.go
Done() blocks on <-r.started before acquiring the lock and removing resource state, ensuring the manager is initialized before cleanup proceeds.
Timeout Handling Correction
internal/manager/readiness/manager.go
WaitReady() timeout branch now returns false instead of true when the timeout expires.
Logging Enhancements
internal/manager/readiness/manager.go
Start() logging replaces expected field with registered_count and adds V(1)-level output of full resources list; "readiness manager started" is logged on completion.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning No E2E tests included. The PR fixes a critical concurrency bug in readiness manager startup but provides zero test coverage to prevent regression. Add E2E tests: (1) startup initialization, (2) Done() called before/after Start(), (3) WaitReady() timeout returns false, (4) state cleanup when all resources done. Simulate controller reconciliation calling Done().
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main bug fix: resolving WaitReady blocking for 5 minutes on startup, which is the primary issue fixed in this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed No security vulnerabilities found. Readiness manager logs only non-sensitive metadata (namespace, name, counts). No credential handling, database ops, authorization logic, or crypto operations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/readiness-wait-ready-block-v2

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

Copilot AI 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.

Pull request overview

This PR backports an upstream fix to the readiness manager to prevent startup full-sync from being blocked for the full 5-minute timeout due to a race between asynchronous Start() initialization and early Done() calls from controller reconciles.

Changes:

  • Gate Done() execution on readiness initialization by waiting on r.started before accessing r.state.
  • Fix WaitReady() timeout semantics to return false (timeout ≠ ready).
  • Adjust readiness lifecycle logging and remove the mutex from registerState().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/manager/readiness/manager.go
Comment thread internal/manager/readiness/manager.go
Comment thread internal/manager/readiness/manager.go Outdated
@AlinsRan AlinsRan reopened this May 8, 2026
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-27T10:35:19Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 0
      Passed: 32
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 1 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-27T10:35:37Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-27T10:53:18Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 1
      Passed: 11
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests failed with 1 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 2
      Passed: 9
      Skipped: 0
  name: GATEWAY-TLS
  summary: Core tests failed with 2 test failures.

AlinsRan added 2 commits June 16, 2026 12:36
Done() waits on r.started, so the early return on a failed List left every
reconcile worker blocked forever in its deferred Done(). Closing the
channel from a defer covers that path and, because the defer runs last,
also keeps Start()'s unguarded len(r.state) reads out of reach of Done(),
which deletes from the same map under r.mu -- a concurrent map access the
race detector reports today.

The per-object 'marking resource as done' line drops to V(1); it is
deferred by every reconciler and one line per reconciled object is a lot
of steady-state noise in a large cluster.
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