Skip to content

feat: refresh dynamic auth credentials#1008

Open
mturac wants to merge 28 commits into
redis:mainfrom
mturac:izon/fix-1007-auth-refresh
Open

feat: refresh dynamic auth credentials#1008
mturac wants to merge 28 commits into
redis:mainfrom
mturac:izon/fix-1007-auth-refresh

Conversation

@mturac

@mturac mturac commented Jul 8, 2026

Copy link
Copy Markdown

Fixes #1007

Summary

  • Add AuthCredentials.RefreshAfter so dynamic credential providers can schedule per-pipe credential refreshes after a caller-selected duration.
  • Refresh credentials with AUTH for RESP2 and RESP3 connections, including password-only RESP3 connections that authenticate as the default user.
  • Pause lifetime and credential-refresh timers while a pooled pipe is checked out, restart them when the pipe is returned, and discard a pipe if a timer callback is already active.
  • Close the pipe when fetching or applying refreshed credentials fails, honoring the configured connection timeout for AUTH.

Testing

  • git diff --check upstream/main...HEAD
  • go test -run '^TestNewPipe$' -count=1 -timeout=3m .
  • go test -race -run '^(TestNewPipe|TestPoolWithConnLifetime)$' -count=1 -timeout=5m .

Note

High Risk
Changes authentication timing and in-flight AUTH on pooled connections; mis-timed refresh or pool/timer races could drop connections or leave stale credentials until exit.

Overview
Adds AuthCredentials.RefreshAfter so AuthCredentialsFn can schedule per-connection credential rotation. Each pipe runs an authTimer that re-invokes the provider and issues AUTH on the live connection (RESP2/RESP3, including password-only RESP3 as default).

Timer lifecycle is aligned with connection lifetime: StopTimer / ResetTimer now pause and resume both lifetime and auth refresh while a pipe is checked out from the pool; _exit no longer stops timers globally. The pool drops newly created connections if StopTimer fails (timer already fired), matching reuse behavior for stale wires.

Failures while fetching or applying refreshed credentials close the pipe, with refresh AUTH bounded by ConnWriteTimeout. Broad pipe_test coverage plus pool tests for expired new connections.

Reviewed by Cursor Bugbot for commit 0e3a859. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread pipe.go
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
@mturac

mturac commented Jul 9, 2026

Copy link
Copy Markdown
Author

Validation refreshed on the current head:

git diff --check origin/main...HEAD
go test ./... -run 'TestPipe/Auth Credentials Refresh' -count=1
go test -race ./... -run 'TestPipe/Auth Credentials Refresh' -count=1
go test . -run TestPipe -count=1 -timeout=120s

All passed locally. The current CircleCI build/setup checks are also green.

Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go
Comment thread rueidis.go Outdated
@mturac

mturac commented Jul 9, 2026

Copy link
Copy Markdown
Author

Updated in 10ad622:

  • replaced RefreshBefore time.Time with RefreshAfter time.Duration;
  • removed the auth timer mutex and reuse the timer with Reset;
  • moved the refresh work out of the timer callback;
  • kept the busy-pipe, no-background, RESP2, and RESP3 default-user refresh coverage aligned with the new API.

Validation:

  • go test -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=2m .
  • go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=3m .
  • go test -run '^TestNewPipe$' -count=1 -timeout=2m .
  • go test ./... -run 'TestPipe|Auth Credentials Refresh|TestNewPipe' -count=1 -timeout=180s
  • go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh_(Closes_Busy_Pipe|Timeout|After_StopTimer)$' -count=20 -timeout=5m .
  • go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=3 -timeout=5m .
  • git diff --check origin/main...HEAD

@mturac

mturac commented Jul 9, 2026

Copy link
Copy Markdown
Author

Updated in d9a5659:

  • removed the custom auth-refresh blocked error and reuse the existing closing error when refresh cannot safely run while a command is pending;
  • removed the stored default-user flag and derive the refresh AUTH shape from the pipe protocol state instead.

Validation:

  • go test -run '^TestNewPipe$/Auth_Credentials_Refresh_(RESP3|RESP3_Default_User|RESP2|Closes_Busy_Pipe)$' -count=1 -timeout=2m .
  • go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=3 -timeout=5m .
  • go test -run '^TestNewPipe$' -count=1 -timeout=2m .
  • git diff --check origin/main...HEAD

@mturac
mturac force-pushed the izon/fix-1007-auth-refresh branch from d9a5659 to 3c88942 Compare July 9, 2026 08:51
Comment thread pipe.go
@mturac

mturac commented Jul 9, 2026

Copy link
Copy Markdown
Author

Update: refreshed against the current head after the follow-up commits.

Current commit: 29ed971d2d6e0accd4306d411dd716325754e228

Current CI status:

ci/circleci: setup  success
ci/circleci: build  success
all visible checks passing

Local validation on this head:

git diff --check upstream/main...HEAD
go test -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=2m .
go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=5m .

Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go
Comment thread pipe.go
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
@mturac

mturac commented Jul 10, 2026

Copy link
Copy Markdown
Author

Updated in 3907028.

  • Added comments explaining why auth refresh tracks pending/open MULTI state.
  • Added comments explaining why refresh is rescheduled while a transaction or synchronous command is using the connection.

Validation:

git diff --check upstream/main...HEAD
go test -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=2m .
go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=5m .

@mturac

mturac commented Jul 10, 2026

Copy link
Copy Markdown
Author

Updated in 89d1ec6.

  • Removed the extra auth timer mutex and stored auth callback/context/refresh interval fields.
  • The timer callback now captures the auth callback/context and keeps the current retry interval local to the callback.
  • Kept the busy-pipe / transaction deferral behavior intact, including the race case where credentials are fetched but AUTH cannot be safely sent yet.

Validation:

  • git diff --check upstream/main...HEAD
  • go test -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=2m .
  • go test -race -run '^TestNewPipe$/Auth_Credentials_Refresh' -count=1 -timeout=5m .
  • PATH="$(go env GOPATH)/bin:$PATH" ./dockertest.sh -skip Integration

Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated
Comment thread pipe.go Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit c514a7f. Configure here.

Comment thread pipe.go
Comment thread pipe.go Outdated
p.authRefreshAfter = authCredentials.RefreshAfter
ready := make(chan struct{})
p.authTimer = time.AfterFunc(p.authRefreshAfter, func() {
<-ready

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really needed?

Comment thread pool_test.go
Comment thread pool_test.go
Comment thread pipe.go
if err := ctx.Err(); err != nil {
return newErrResult(err)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep these empty lines

Comment thread pipe.go Outdated
reset = p.lftmTimer.Reset(p.lftm)
}
if p.authTimer != nil && p.authRefreshAfter > 0 {
reset = p.authTimer.Reset(p.authRefreshAfter) && reset

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, my bad, let's go back to make RefreshAfter a time.Time so that we can be more accurate when resetting the timer.

Comment thread pipe.go Outdated
return
}
if auth.Username != "" || auth.Password != "" {
args := []string{"AUTH"}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please preallocate the slice slots for username and password.

Comment thread pipe.go Outdated
r2ps bool // identify this pipe is used for resp2 pubsub or not
noNoDelay bool
optIn bool
authRefreshAfter time.Duration

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, please make this a time.Time. Also, fields should be aligned by size. It won't be the last field.

@mturac

mturac commented Jul 14, 2026

Copy link
Copy Markdown
Author

Updated in 48eed1c.

  • Changed AuthCredentials.RefreshAfter back to an absolute time.Time and reset the auth refresh timer with time.Until(...).
  • Removed the extra ready channel around the auth refresh timer.
  • Preallocated the AUTH argument slice and restored the requested blank line in Do.
  • Kept the current pool guard/test shape: reverting that guard locally reproduced the TestPoolWithAcquireCtx/Acquire connections, all exceed context deadline pool-size leak (actual: 1), so I left the guard in place and covered it with the targeted pool tests.

Validation:

  • go test . -run 'TestPipe/Auth Credentials Refresh|TestPoolWithConnLifetime|TestPoolWithAcquireCtx'

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.

Support AUTH/HELLO refresh

2 participants