Skip to content

Conversation

@x0sina
Copy link
Collaborator

@x0sina x0sina commented Jan 6, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Prevented potential deadlocks during configuration reads; made concurrent config access safe.
    • Improved error handling so sync issues are logged and do not drop processing.
  • Refactor

    • User lists now maintain deterministic ID-based ordering for faster lookups and reliable updates/removals.
    • Improved account update logic for more consistent ShadowSocks handling and overall sync performance.

✏️ Tip: You can customize this high-level summary in your review settings.

x0sina added 2 commits January 6, 2026 10:42
- Replace slice-based client storage with map[string]api.Account for O(1) lookups
- Add convertClientsMapToSlice() and convertClientsSliceToMap() helpers
- Refactor syncUsers(), updateUser(), and removeUser() to use maps
- Convert maps to slices only during JSON serialization in ToBytes()
- Maintain backward compatibility in NewXRayConfig() for existing JSON configs

Performance improvements:
- updateUser(): O(n) -> O(1) (~5000x faster for 10k users)
- removeUser(): O(n) -> O(1) (~5000x faster for 10k users)
- syncUsers(): Eliminates repeated O(n) scans during rebuilds

This resolves delayed user activation issues after /user/sync by eliminating
expensive slice operations that caused multi-minute delays during high load
or after node restarts.
- Add extractIDFromEmail() helper to extract ID part from 'id.username' format
- Update all sorting functions to sort by ID (part before dot) instead of full email
- Update updateUser() and removeUser() to use binary search with ID comparison
- Maintain O(log n) search performance while correctly identifying users by ID

This ensures binary search works correctly with the email field format where
the ID is the part before the dot (e.g., '12345.username' -> ID: '12345')
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

ID-based ordering was introduced in backend/xray/config.go: emails are normalized via a new helper to extract an ID prefix, user lists per protocol are sorted by that ID, and user add/update/remove operations now use binary-search insertion/removal. ToBytes() now acquires read locks on inbound configs during marshaling.

Changes

Cohort / File(s) Summary
ID extraction & helpers
backend/xray/config.go
Added extractIDFromEmail(email string) string to obtain the ID prefix (text before first dot) for ordering and lookups.
User sync / update / remove logic
backend/xray/config.go
syncUsers() builds per-protocol client lists and sorts them by extracted ID; errors during client creation are logged and processing continues. updateUser() and removeUser() now locate entries by ID using slices.BinarySearchFunc, remove or replace entries while preserving sorted order, and handle protocol-specific account types (Vmess, Vless, Trojan, Shadowsocks, Shadowsocks TCP).
Serialization & concurrency
backend/xray/config.go
ToBytes() acquires read locks on all inbound configs before marshaling and releases them after to ensure consistent concurrent reads.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibble IDs before the dot,
Sorting hops in every spot.
Binary searches, tidy and neat,
Locks held gently, no repeat.
A rabbit’s patchwork, snug and sweet.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Optimize user management map based storage' accurately describes the main change: replacing slice-based client storage with map-based storage and optimizing user management operations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b9b3e8 and a176149.

📒 Files selected for processing (1)
  • backend/xray/config.go

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@x0sina
Copy link
Collaborator Author

x0sina commented Jan 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/xray/config.go (1)

454-471: Locks are not released if json.Marshal panics.

The current pattern acquires all read locks before marshaling and releases them after, but if json.Marshal panics (e.g., due to a custom MarshalJSON method or concurrent modification), the locks will never be released, causing a deadlock.

Consider using a deferred cleanup pattern:

🔎 Suggested fix
 func (c *Config) ToBytes() ([]byte, error) {
-	// Acquire read locks for all inbounds
 	for _, i := range c.InboundConfigs {
 		i.mu.RLock()
 	}
+	defer func() {
+		for _, i := range c.InboundConfigs {
+			i.mu.RUnlock()
+		}
+	}()

 	b, err := json.Marshal(c)
-
-	// Release all locks
-	for _, i := range c.InboundConfigs {
-		i.mu.RUnlock()
-	}
-
 	if err != nil {
 		return nil, err
 	}
 	return b, nil
 }
🤖 Fix all issues with AI Agents
In @backend/xray/config.go:
- Around line 73-135: The functions convertClientsMapToSlice and
convertClientsSliceToMap are dead code (never called) while ToBytes() uses
json.Marshal directly and syncUsers/updateUser/removeUser expect typed slices;
either delete these two conversion functions to remove unused code, or if you
intended map-based storage, refactor ToBytes(), syncUsers, updateUser, and
removeUser to use the map strategy and call
convertClientsMapToSlice/convertClientsSliceToMap where needed (ensure ToBytes
invokes convertClientsMapToSlice before marshalling and that user management
functions read/write i.Settings["clients"] as map[string]api.Account).
- Around line 137-201: convertClientsSliceToMap is dead and incompatible with
existing code: either remove it or fully integrate maps across the flow; to fix,
delete the convertClientsSliceToMap method (and any references) so
Settings["clients"] remains the slice types expected by syncUsers, updateUser,
and removeUser, or alternatively refactor the callers
(syncUsers/updateUser/removeUser) and all code that assumes slice types to
accept the map form and use convertClientsMapToSlice for JSON serialization;
pick one approach and apply it consistently so Settings["clients"] has a single
canonical type and no unused conversion functions remain.
🧹 Nitpick comments (1)
backend/xray/config.go (1)

311-325: Potential nil slice dereference if type assertion fails.

Line 313 ignores the ok result from the type assertion. If Settings["clients"] is nil or a different type, clients will be nil. While the subsequent binary search and append operations happen to work on nil slices in Go, the intention is unclear and the behavior is fragile.

Consider adding an explicit nil check or initializing an empty slice when the type assertion fails:

🔎 Suggested fix
-	clients, _ := i.Settings["clients"].([]*api.VmessAccount)
+	clients, ok := i.Settings["clients"].([]*api.VmessAccount)
+	if !ok {
+		clients = []*api.VmessAccount{}
+	}

This same pattern applies to all protocol cases in updateUser() (lines 328, 343, 358, 373) and removeUser() (lines 404, 413, 422, 433, 441).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f3a90e and 0b9b3e8.

📒 Files selected for processing (1)
  • backend/xray/config.go
🔇 Additional comments (4)
backend/xray/config.go (4)

26-34: LGTM!

The helper function correctly extracts the ID portion before the first dot, with a sensible fallback to the full email when no dot is present.


215-226: Good improvement: Error handling for account creation.

The addition of error handling that logs and continues (rather than silently failing or panicking) is a sensible improvement. The sorting by extracted ID enables the binary search operations in updateUser() and removeUser().


397-450: Binary search removal logic is correct.

The use of BinarySearchFunc for O(log n) lookups is appropriate for the sorted slice structure. The same nil slice handling suggestion from updateUser() applies here as well.


223-227: Implementation uses O(log n) binary search, not O(1) maps as claimed in PR.

The PR description states "Replace slice-based client storage with map[string]api.Account to enable O(1) lookups," but the actual implementation:

  1. Stores clients as sorted slices (e.g., []*api.VmessAccount)
  2. Uses slices.BinarySearchFunc for O(log n) lookups
  3. Never calls the map conversion functions

This still provides a significant improvement over the original O(n) linear scans, but the PR description should be updated to accurately reflect the O(log n) binary search approach rather than O(1) map-based lookups.

- Deleted convertClientsMapToSlice() and convertClientsSliceToMap() methods as they are no longer needed.
- This cleanup reduces code complexity and improves maintainability by removing redundant logic related to client storage conversion.
@x0sina
Copy link
Collaborator Author

x0sina commented Jan 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@x0sina x0sina merged commit d8382db into dev Jan 6, 2026
3 checks passed
@ImMohammad20000 ImMohammad20000 deleted the optimize-user-management-map-based-storage branch January 6, 2026 08:23
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