Skip to content

xray: let the last entry for a user win within one user batch - #134

Open
Free-Guy-IR wants to merge 2 commits into
PasarGuard:devfrom
Free-Guy-IR:upn/xray-user-sync-w
Open

Free-Guy-IR wants to merge 2 commits into
PasarGuard:devfrom
Free-Guy-IR:upn/xray-user-sync-w

Conversation

@Free-Guy-IR

@Free-Guy-IR Free-Guy-IR commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #129

Found by code review; never seen in production. A robustness fix: the upstream panel and bridge do not send a batch with a repeated email today (node_bridge_py c5632b65a10a keeps one queued entry per email, storage.py:159-166). Head d41c6fd0dd44, based on main v0.5.7 739eab94a934; git merge-tree --write-tree onto dev 9ccd2638c7fe is clean. No code change since the PR was opened.

What

UpdateUsers (via buildInboundUpdates) and Config.syncUsers now keep only the last entry per email, at the position of its first entry, before they build the per-inbound updates or rebuild an inbound's clients. Config.updateUsers gets the same input, although no RPC reaches it today (its only caller is UpdateUsersAndRestart).

Real node, real Xray

Node built from dev 9ccd2638 and from this PR merged onto dev, REST, real Xray 26.3.27, macOS arm64, one trojan inbound, core read with xray api inbounduser. One PUT /users/sync/chunked per step:

batch for 1.alice before last entry wants dev this PR
[on trojan, no inbounds] (added then removed) absent absent present absent
[no inbounds, on trojan] (removed then kept) present present absent present

Tests

  • backend/xray/user_sync_batch_test.go: TestUpdateUsersLetsTheLastEntryForAUserWin and TestUpdateUsersAndSyncUsersLetTheLastEntryForAUserWinInTheConfig.
  • backend/xray/user_sync_interleave_test.go: TestConcurrentSyncUserAndUpdateUsersKeepCoreAndConfigInAgreement. This is an ordering test, not a race test: syncMu serializes SyncUser and UpdateUsers, and the test checks that core and config agree whichever runs first, and that a lone batch ends as its last entry says.

go1.27.3, -race -count=3, macOS arm64. On dev 9ccd2638 with the two test files (production code unchanged), all three tests fail in all three runs:

user_sync_batch_test.go:44: operations sent to xray:
user_sync_batch_test.go:71: a user whose last entry removes them is on trojan: [1.alice]
user_sync_batch_test.go:76: a user whose last entry adds them is not on trojan: []
user_sync_interleave_test.go:69: round 0: after a lone batch whose last entry wants alice on trojan = false, core = true

The round at which the interleave test fails varies between runs (0, 1 and 2 in the run above). On this PR merged onto dev all three pass in all three runs (ok backend/xray 6.973s).

Behaviour change

An email listed more than once is applied once, at the position of its first entry, with the content of its last entry. When two different users in one batch share a credential, this can change the order in which they are added to the core. Batches without a repeated email are applied as before (read from the code).

UpdateUsers compared every entry with the state before the batch and
applied all removals before any add, so when one batch carried the same
email twice the outcome did not follow the last entry. The full sync
that rebuilds the config also kept a user an earlier entry added even
when a later entry dropped them. Both now keep only the last entry for
each email.
@coderabbitai

coderabbitai Bot commented Nov 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f3900ea4-f92d-42d5-8ef8-5f1af15641f1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

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