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:
PathGao
2026-07-30 03:00:29 +08:00
committed by GitHub
parent e467b25f03
commit ad5f2a28cb
12 changed files with 275 additions and 87 deletions
+2 -2
View File
@@ -494,8 +494,8 @@ func (t *Tgbot) clientInfoMsg(
status := t.I18nBot("tgbot.offline")
isOnline := false
if service.XrayProcess().IsRunning() {
if slices.Contains(service.XrayProcess().GetOnlineClients(), traffic.Email) {
if process := service.XrayProcess(); process != nil && process.IsRunning() {
if slices.Contains(process.GetOnlineClients(), traffic.Email) {
status = t.I18nBot("tgbot.online")
isOnline = true
}
+7 -3
View File
@@ -105,7 +105,10 @@ func (t *Tgbot) prepareServerUsageInfo() string {
t.lastStatus = t.serverService.GetStatus(t.lastStatus)
t.setCachedStatus(t.lastStatus)
}
onlines := service.XrayProcess().GetOnlineClients()
var onlines []string
if process := service.XrayProcess(); process != nil {
onlines = process.GetOnlineClients()
}
info += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
info += t.I18nBot("tgbot.messages.version", "Version=="+config.GetPanelVersion())
@@ -361,11 +364,12 @@ func (t *Tgbot) notifyExhausted() {
// onlineClients retrieves and sends information about online clients.
func (t *Tgbot) onlineClients(chatId int64, messageID ...int) {
if !service.XrayProcess().IsRunning() {
process := service.XrayProcess()
if process == nil || !process.IsRunning() {
return
}
onlines := service.XrayProcess().GetOnlineClients()
onlines := process.GetOnlineClients()
onlinesCount := len(onlines)
output := t.I18nBot("tgbot.messages.onlinesCount", "Count=="+fmt.Sprint(onlinesCount))
keyboard := tu.InlineKeyboard(tu.InlineKeyboardRow(