Skip to content

Navigation Menu

Sign in
Sign up

chore(ci): fix flaky membership test and enforce gofmt - #171

Merged
gammons merged 2 commits into
main from
fix/ci-hygiene
Sep 3, 2026
Merged

chore(ci): fix flaky membership test and enforce gofmt #171
gammons merged 2 commits into
main from
fix/ci-hygiene

Conversation

@gammons

@gammons gammons commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Two CI-hygiene fixes found while triaging the open PR queue. Both were producing false signals that cost review time on unrelated PRs.

1. Flaky TestBackgroundFetchRetriesAfterBackoffExpiry

This test was failing intermittently in CI on PRs that don't touch internal/slack/membership — currently red on #163, #166 and #169, all of which are innocent.

It used two barriers that don't gate the state it asserts on:

  • waitForCallCountfakeMemberAPI records a call on entry, but backgroundFetch writes lastFailed only after the API call returns (manager.go:183-185). So backdating lastFailed after waitForCallCount(1) raced the manager's own write and got clobbered, leaving the backoff live so the retry never fired → timed out waiting for 2 API calls.
  • waitForPushEnsureFresh calls pushSnapshot synchronously on the caller's goroutine (manager.go:91-92), so waitForPush(sink, 2) was satisfied by the test's own second EnsureFresh, not by the background fetch. The assertion then ran while that fetch was still in flight → lastFailed not cleared by a successful fetch.

Replaced both with a waitUntil helper that polls manager state under its own lock, and added an optional post-call delay to fakeMemberAPI so the slow-API interleaving that used to be a rare race is now this test's default path.

The product code was correctdelete(m.lastFailed, channelID) at manager.go:222 is properly locked. This was purely a test bug.

Verified:

  • With the 50ms delay, the old test failed 5/5.
  • The new test passes 20/20 under -race.
  • It still fails as intended when delete(m.lastFailed, channelID) is removed from manager.go, so it hasn't been weakened into a no-op.

2. gofmt enforcement

.golangci.yml enabled no formatter, so drift accumulated silently — 29 files were unformatted on main.

The cost shows up in review: contributors' editors reformat whatever they touch, so alignment churn rides along inside feature diffs. The same Edge *edge.Client realignment currently appears in four separate open PRs (#161, #162, #167, #170) for exactly this reason, and two open PRs introduce new gofmt failures that CI can't currently see.

Enables the gofmt formatter and reformats the tree in one pass, so the next diff touching these files is only the actual change.

Verification

  • go build ./... — pass
  • go vet ./... — pass
  • go test ./... -race — pass, 54 packages, 0 failures

Recommend merging this first, so the rest of the PR queue rebases onto a tree where CI tells the truth.

...ed PRs
TestBackgroundFetchRetriesAfterBackoffExpiry used two barriers that do
not actually gate the state it asserts on, so it failed intermittently
in CI on PRs that do not touch this package (#163, #166, #169).
Both barriers were false:
 - fakeMemberAPI records a call on *entry*, but backgroundFetch writes
 lastFailed only after the API call returns. Backdating lastFailed
 after waitForCallCount(1) therefore raced the manager's own write
 and got clobbered, leaving the backoff live so the retry never
 fired: "timed out waiting for 2 API calls".
 - EnsureFresh calls pushSnapshot synchronously on the caller's
 goroutine, so waitForPush(sink, 2) was satisfied by the test's own
 second EnsureFresh rather than by the background fetch. The
 lastFailed assertion then ran while that fetch was still in flight:
 "lastFailed not cleared by a successful fetch".
Replace both with waitUntil, which polls the manager state under its own
lock. Add an optional post-call delay to fakeMemberAPI and use it here,
so the slow-API interleaving that used to be a rare race is now the
test's default path.
Verified: with the 50ms delay the old test failed 5/5; the new one
passes 20/20 under -race, and still fails as intended when
`delete(m.lastFailed, channelID)` is removed from manager.go.
golangci-lint had no formatter enabled, so gofmt drift accumulated
silently: 29 files were unformatted on main. The cost lands on review —
contributors' editors reformat whatever they touch, so alignment churn
rides along in feature diffs and has to be separated from real changes
every time. The same `Edge *edge.Client` realignment shows up in four
open PRs for exactly this reason, and two open PRs introduce new gofmt
failures that CI currently cannot see.
Enable the gofmt formatter and reformat the tree in one pass so the
next diff that touches these files is only the change itself.
No semantic changes: build, vet, and `go test ./... -race` (54 packages)
all pass.
gammons merged commit c89847b into main Sep 3, 2026
3 checks passed
Yukaii pushed a commit to Yukaii/slk that referenced this pull request Sep 4, 2026
TestUserResolver_BatchesMissesThroughEdge failed intermittently (~10% of
runs locally, and it just failed CI on gammons#128) with:
 user_resolver_test.go: U001 was not cached from the edge batch:
 getting user: sql: no rows in result set
Same root cause as the membership flake fixed in gammons#171: the test waited
on a barrier that does not gate the state it asserts.
fakeBatcher.UsersInfo records the batch and returns immediately. The
resolver only *then* loops applyEdgeUser, which is what calls
UpsertUserFromEdge. So `for len(batcher.calls()) == 0 { ... }` returned
before any row existed, and the db.GetUser assertions raced the write.
Wait for the applied result instead — both cache rows present and both
UserResolvedMsg sent. applyEdgeUser writes the row and then sends, per
user, so the message count is the later signal and implies every write
has landed. Adds a waitUntil helper to the package and documents why a
batcher call count must not be used as a barrier.
Verified: 40/40 under -race (previously ~2 failures per 20), and it
still fails as intended when UpsertUserFromEdge is stubbed out, so it
has not been weakened into a no-op.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /