Skip to content

fix(watcher): harden incremental auth sync and mirrored auth dir handling#1920

Open
shenshuoyaoyouguang wants to merge 2 commits intorouter-for-me:devfrom
shenshuoyaoyouguang:pr/watcher-auth-fixes
Open

fix(watcher): harden incremental auth sync and mirrored auth dir handling#1920
shenshuoyaoyouguang wants to merge 2 commits intorouter-for-me:devfrom
shenshuoyaoyouguang:pr/watcher-auth-fixes

Conversation

@shenshuoyaoyouguang
Copy link
Contributor

Summary

  • avoid full auth snapshot rebuilds during watcher file events
  • harden mirrored auth dir handling for runtime auth refresh paths
  • add watcher coverage for incremental sync and mirrored auth directory behavior

Business value

  • reduces auth file churn and unnecessary full rescans
  • lowers risk of stale runtime auth state in mirrored auth workspaces
  • makes the watcher PR smaller and easier to review in isolation

Tests

  • go test ./internal/watcher/...

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the watcher component's authentication synchronization process by transitioning from full snapshot rebuilds to incremental updates for individual auth file changes. It also strengthens the handling of mirrored authentication directories, ensuring the system consistently uses the correct auth paths. These changes aim to reduce unnecessary processing, enhance stability, and provide a more granular and efficient way to manage authentication state.

Highlights

  • Incremental Auth Sync: Implemented an incremental synchronization mechanism for authentication files, eliminating the need for full auth snapshot rebuilds on individual file changes. This significantly reduces auth file churn and improves performance.
  • Hardened Mirrored Auth Directory Handling: Enhanced the handling of mirrored authentication directories to ensure runtime auth refresh paths are correctly managed. This includes using an 'effective' auth directory and robust path comparison utilities.
  • Refactored Auth Synthesis Logic: The logic for synthesizing authentication entries from individual files has been refactored into a dedicated function, SynthesizeAuthFile, improving modularity and reusability.
  • Removed Global Server Update Debouncing: The serverUpdateTimer and related debouncing logic for global server updates have been removed, as incremental auth updates no longer necessitate a full server reload for each file change.
  • Improved Watcher Test Coverage: Added new test cases specifically for the mirrored auth directory behavior and verified that individual auth file events do not trigger full snapshot operations, ensuring the new incremental sync works as expected.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/watcher/clients.go
    • Imported the new synthesizer package.
    • Initialized fileAuthsByPath map to store auths per file path.
    • Modified reloadClients to use synthesizer.SynthesizeAuthFile for initial auth loading.
    • Updated addOrUpdateClient and removeClient to perform incremental updates using computePerPathUpdatesLocked and dispatchAuthUpdates, removing full refreshAuthState calls.
    • Added computePerPathUpdatesLocked to calculate specific auth additions, modifications, or deletions.
    • Added authSliceToMap helper to convert a slice of auths to a map by ID.
    • Removed stopServerUpdateTimer and triggerServerUpdate functions.
  • internal/watcher/dispatcher.go
    • Introduced snapshotCoreAuthsFunc for testability of the auth snapshot process.
    • Modified refreshAuthState to use snapshotCoreAuthsFunc and retrieve config/auth directory under a read lock.
  • internal/watcher/events.go
    • Updated start to use effectiveAuthDir() for watching the correct authentication directory.
    • Modified handleEvent to use pathBelongsToDir and effectiveAuthDir() for more robust auth file event detection.
  • internal/watcher/mirrored_auth_dir_test.go
    • Added new test file to cover mirrored auth directory functionality across Start, handleEvent, refreshAuthState, and SnapshotCoreAuths.
  • internal/watcher/path_scope.go
    • Added new file containing pathBelongsToDir and normalizeAbsolutePath utility functions for reliable path comparisons.
  • internal/watcher/synthesizer/file.go
    • Removed unused runtime import.
    • Refactored auth file processing into a new synthesizeFileAuths helper function.
    • Introduced SynthesizeAuthFile as a public function for synthesizing auth entries from a single file payload.
    • Adjusted ID generation to correctly handle relative paths under the AuthDir.
  • internal/watcher/watcher.go
    • Removed sync/atomic import and related fields (serverUpdateMu, serverUpdateTimer, serverUpdateLast, serverUpdatePend, stopped).
    • Added fileAuthsByPath map to the Watcher struct.
    • Removed serverUpdateDebounce constant.
    • Initialized fileAuthsByPath in NewWatcher.
    • Updated NewWatcher to set w.authDir to the mirrored path if an authDirProvider is detected.
    • Removed stopped.Store(true) and stopServerUpdateTimer() from the Stop method.
    • Modified SnapshotCoreAuths to use effectiveAuthDir().
    • Added effectiveAuthDir() helper to determine the active authentication directory.
  • internal/watcher/watcher_test.go
    • Updated various test cases (TestAddOrUpdateClientTriggersReloadAndHash, TestRemoveClientRemovesHash, TestHandleEventRemovesAuthFile, TestHandleEventAuthWriteTriggersUpdate, TestHandleEventAtomicReplaceChangedTriggersUpdate, TestHandleEventRemoveKnownFileDeletes) to assert that global reload callbacks are no longer triggered by individual auth file events.
    • Removed TestTriggerServerUpdateCancelsPendingTimerOnImmediate due to the removal of the triggerServerUpdate function.
    • Added TestAuthFileEventsDoNotInvokeSnapshotCoreAuths to verify that incremental updates avoid full snapshots.
    • Added TestAuthSliceToMap to test the new helper function.
    • Modified TestNewWatcherDetectsPersisterAndAuthDir to verify w.authDir is also updated to the mirrored path.
Activity
  • The pull request description outlines the intent to avoid full auth snapshot rebuilds, harden mirrored auth directory handling, and add watcher coverage for these behaviors.
  • The author has provided tests to validate the changes, specifically go test ./internal/watcher/....
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant improvements to the watcher's authentication handling. The move to incremental auth synchronization for file events is a great enhancement that should reduce unnecessary full rescans and improve performance. The hardening of mirrored auth directory handling by using effectiveAuthDir and the more robust pathBelongsToDir check makes the watcher more reliable. The added tests provide good coverage for these new behaviors. I have one suggestion regarding case normalization for auth IDs on Windows to prevent potential bugs on case-insensitive filesystems.

Comment on lines +89 to +94
id := fullPath
if strings.TrimSpace(ctx.AuthDir) != "" {
if rel, errRel := filepath.Rel(ctx.AuthDir, fullPath); errRel == nil && rel != "" {
id = rel
}
// On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths.
if runtime.GOOS == "windows" {
id = strings.ToLower(id)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The case normalization for auth IDs on Windows has been removed. This could lead to duplicate auth entries on case-insensitive file systems like Windows, where file paths might be reported with different casing. To ensure consistency with sdk/auth/filestore.go and prevent duplicates, it's important to normalize the ID to lowercase on Windows.

This change will also require re-adding the runtime import to this file.

	id := fullPath
	if authDir := strings.TrimSpace(ctx.AuthDir); authDir != "" {
		if rel, errRel := filepath.Rel(authDir, fullPath); errRel == nil && rel != "" {
			id = rel
		}
	}
	// On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths.
	if runtime.GOOS == "windows" {
		id = strings.ToLower(id)
	}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 803a275. I restored Windows auth ID case normalization in internal/watcher/synthesizer/file.go so synthesized file-backed auth IDs stay aligned with sdk/auth.NormalizeFileAuthID and do not duplicate on case-insensitive filesystems.

@shenshuoyaoyouguang shenshuoyaoyouguang marked this pull request as ready for review March 7, 2026 09:39
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@shenshuoyaoyouguang
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant improvements to the watcher's authentication handling. The switch to incremental auth synchronization for file events is a great enhancement that will reduce unnecessary full rescans and improve performance. The logic for this appears sound, and I've only suggested a minor optimization to reduce object allocations. The hardening of mirrored auth directory handling through the effectiveAuthDir and pathBelongsToDir functions is also a solid improvement, making path resolution more robust. The addition of comprehensive tests for both incremental sync and mirrored directory behavior is excellent and ensures these new features are well-covered. Overall, this is a high-quality change that delivers on its goals.

Comment on lines +245 to +253
if !ok {
w.currentAuths[id] = newAuth.Clone()
updates = append(updates, AuthUpdate{Action: AuthUpdateActionAdd, ID: id, Auth: newAuth.Clone()})
continue
}
if !authEqual(existing, newAuth) {
w.currentAuths[id] = newAuth.Clone()
updates = append(updates, AuthUpdate{Action: AuthUpdateActionModify, ID: id, Auth: newAuth.Clone()})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There's an opportunity to optimize object allocations here. The newAuth object is freshly synthesized and not shared with other parts of the system at this point. Therefore, it can be stored directly in w.currentAuths without cloning. A clone is only necessary for the AuthUpdate struct that is sent over the channel to consumers, to prevent concurrent modification. This change avoids one Clone() call per auth update, improving efficiency.

Suggested change
if !ok {
w.currentAuths[id] = newAuth.Clone()
updates = append(updates, AuthUpdate{Action: AuthUpdateActionAdd, ID: id, Auth: newAuth.Clone()})
continue
}
if !authEqual(existing, newAuth) {
w.currentAuths[id] = newAuth.Clone()
updates = append(updates, AuthUpdate{Action: AuthUpdateActionModify, ID: id, Auth: newAuth.Clone()})
}
if !ok {
w.currentAuths[id] = newAuth
updates = append(updates, AuthUpdate{Action: AuthUpdateActionAdd, ID: id, Auth: newAuth.Clone()})
continue
}
if !authEqual(existing, newAuth) {
w.currentAuths[id] = newAuth
updates = append(updates, AuthUpdate{Action: AuthUpdateActionModify, ID: id, Auth: newAuth.Clone()})
}

@shenshuoyaoyouguang
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly improves the watcher's authentication handling by introducing an incremental synchronization mechanism, which avoids costly full state rebuilds on file events. The changes also harden the handling of mirrored authentication directories, making the system more robust. The new pathBelongsToDir function provides a more reliable way to check file paths. The added tests provide good coverage for the new functionality. The overall code quality is high, and the refactoring improves modularity and maintainability. I have one minor suggestion regarding code style in a test file.

t.Fatalf("expected mirroredAuthDir %s, got %s", tmp, w.mirroredAuthDir)
}
if w.authDir != tmp {
t.Fatalf("expected runtime authDir to switch to mirrored path %s, got %s", tmp, w.authDir)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This line has inconsistent indentation. While the current indentation (8 spaces) might align visually, it uses spaces instead of tabs, which is inconsistent with the surrounding code and the standard Go formatting (gofmt). Using tabs would ensure consistent style across the project.

Suggested change
t.Fatalf("expected runtime authDir to switch to mirrored path %s, got %s", tmp, w.authDir)
t.Fatalf("expected runtime authDir to switch to mirrored path %s, got %s", tmp, w.authDir)

@shenshuoyaoyouguang
Copy link
Contributor Author

Review Navigation: This PR is now suitable for priority review. Scope focuses on watcher incremental sync and mirrored auth dir handling. CI has passed, automated review only has minor optimization suggestions remaining, no known high-risk blockers.

@luispater luispater changed the base branch from main to dev March 7, 2026 14:33
Copy link
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

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

Summary

This change moves auth-file handling to the incremental path, but I found a blocking regression in remove handling, and the branch is currently not mergeable into dev.

Key findings

  • Blocking: handleEvent now filters auth events through pathBelongsToDir(...). For deleted files, normalizeAbsolutePath resolves the existing auth dir through symlinks but cannot resolve the removed file path the same way, so the comparison can fail on macOS (/var/... vs /private/var/...). That causes remove/rename events to be dropped before removeClient runs, leaving stale auth hashes/runtime auth state behind.
  • Evidence: go test ./internal/watcher/... fails in TestHandleEventRemovesAuthFile and TestHandleEventRemoveKnownFileDeletes.
  • Blocking: this PR is currently in a conflicting state against dev. I reproduced the merge locally and hit unresolved conflicts in:
    • internal/watcher/clients.go
    • internal/watcher/dispatcher.go
    • internal/watcher/watcher_test.go

Test plan

  • go build -o /tmp/test-output-pr1920 ./cmd/server && rm /tmp/test-output-pr1920
  • go test ./internal/watcher/... ❌

Follow-up

  • Please fix the delete-path ownership check so removed auth files are still recognized correctly, resolve the merge conflicts against dev, and rerun gofmt on the touched watcher files.

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