Sitelet https://github.com/hashicorp/consul/pull/23854
Skip to content

fix(agent): resolve Connect CA data race, cache watcher timer leak, and FSM restore error check - #23854

Open
mandar1045 wants to merge 2 commits into
hashicorp:mainfrom
mandar1045:fix/connect-ca-cache-fsm-bugs
Open

fix(agent): resolve Connect CA data race, cache watcher timer leak, and FSM restore error check#23854
mandar1045 wants to merge 2 commits into
hashicorp:mainfrom
mandar1045:fix/connect-ca-cache-fsm-bugs

Conversation

@mandar1045

Copy link
Copy Markdown

Summary

Found a few issues going through the connect CA / cache / raft paths, fixing all three here since they're small and unrelated enough that separate PRs felt like overkill.

  1. Data race in Connect CA - secondaryGetActivePrimaryCARoot() in agent/consul/leader_connect_ca.go was releasing stateLock before iterating over c.primaryRoots.Roots. If a root update came in mid-iteration you'd get a data race. Fixed by holding the lock through the iteration.

  2. Timer leak in cache watchers - notifyBlockingQuery and notifyPollingQuery in agent/cache/watch.go were using time.After, which doesn't get cleaned up if the watch context gets canceled before the timer fires. Under enough watch churn this leaks. Swapped to time.NewTimer with an explicit timer.Stop().

  3. Unchecked error in Raft FSM - FSM.Restore() in agent/consul/fsm/fsm.go wasn't checking the error from storageRestoration.Commit() before swapping in the new state store pointer. So a failed commit could silently leave you with a bad/partial state store. Now it bails out on the error instead of swapping pointers.

None of these are huge, but all three felt worth fixing together since they touch stuff that's easy to overlook until it bites you under load.

Testing

  • go test -race ./agent/consul/
  • go test -race ./agent/cache/

Ran both a handful of times to make sure the race fix actually holds up and not just passing by luck.

…store error handling

- leader_connect_ca.go: Hold stateLock across primaryRoots.Roots iteration in secondaryGetActivePrimaryCARoot() to avoid data race.
- watch.go: Replace time.After with time.NewTimer and timer.Stop() in notifyBlockingQuery and notifyPollingQuery to prevent memory leaks on context cancellation.
- fsm.go: Check return error of storageRestoration.Commit() in FSM.Restore() before swapping state store pointer.
@mandar1045
mandar1045 requested review from a team as code owners August 24, 2026 09:02
@hashicorp-cla-app

Copy link
Copy Markdown

CLA assistant check

Thank you for your submission! We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution. Read and sign the agreement

Learn more about why HashiCorp requires a CLA and what the CLA includes

Have you signed the CLA already but the status is still pending? Recheck it.

1 similar comment
@hashicorp-cla-app

Copy link
Copy Markdown

CLA assistant check

Thank you for your submission! We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution. Read and sign the agreement

Learn more about why HashiCorp requires a CLA and what the CLA includes

Have you signed the CLA already but the status is still pending? Recheck it.

@mandar1045

Copy link
Copy Markdown
Author

Hi team!

CLA signed and changelog entry added. Really excited to contribute to Consul!

Appreciate your time reviewing this PR whenever you get a chance—happy to make any adjustments or add more tests if needed!

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.

1 participant