mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-10 05:10:58 +00:00
fix(xray): synchronize lifecycle state (#6138)
* fix(xray): synchronize lifecycle snapshots Protect process replacement and result caching with a lifecycle state object, so read paths keep one process snapshot while restarts swap state safely. Bound version probing to prevent a stalled binary from holding the restart lock. * test(xray): cover concurrent lifecycle reads Exercise status, result, and traffic reads while the managed process is replaced, so the race detector guards the lifecycle snapshot boundary. * fix(xray): guard process config snapshots Synchronize hot-applied config snapshots, keep Telegram reads on one lifecycle snapshot, and strengthen lifecycle timeout and concurrency regression coverage. --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
||||
)
|
||||
|
||||
func TestXrayLifecycleSnapshotDoesNotOverwriteNewerResult(t *testing.T) {
|
||||
state := xrayLifecycle{}
|
||||
first := xray.NewProcess(&xray.Config{})
|
||||
second := xray.NewProcess(&xray.Config{})
|
||||
|
||||
state.replace(first)
|
||||
state.storeResult(first, "first result")
|
||||
process, result := state.snapshot()
|
||||
if process != first || result != "first result" {
|
||||
t.Fatalf("snapshot = (%p, %q), want (%p, %q)", process, result, first, "first result")
|
||||
}
|
||||
state.replace(second)
|
||||
state.storeResult(first, "old result")
|
||||
|
||||
process, result = state.snapshot()
|
||||
if process != second {
|
||||
t.Fatal("snapshot returned the replaced process")
|
||||
}
|
||||
if result != "" {
|
||||
t.Fatalf("snapshot result = %q, want empty", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXrayLifecycleConcurrentStatusResultAndTrafficReads(t *testing.T) {
|
||||
previousProcess, previousResult := xrayState.snapshot()
|
||||
t.Cleanup(func() {
|
||||
xrayState.mu.Lock()
|
||||
xrayState.process = previousProcess
|
||||
xrayState.result = previousResult
|
||||
xrayState.mu.Unlock()
|
||||
})
|
||||
|
||||
first := xray.NewProcess(&xray.Config{})
|
||||
second := xray.NewProcess(&xray.Config{})
|
||||
service := XrayService{}
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Go(func() {
|
||||
for range 200 {
|
||||
xrayState.replace(first)
|
||||
xrayState.replace(second)
|
||||
}
|
||||
})
|
||||
|
||||
for range 4 {
|
||||
wg.Go(func() {
|
||||
for range 200 {
|
||||
_ = service.IsXrayRunning()
|
||||
_ = service.GetXrayResult()
|
||||
_, _, err := service.GetXrayTraffic()
|
||||
if err == nil || err.Error() != "xray is not running" {
|
||||
t.Errorf("GetXrayTraffic error = %v, want xray is not running", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
Reference in New Issue
Block a user