Skip to content

Commit 00946bb

Browse files
Alaxay8Fangliding
authored andcommitted
Observatory: Clear removed outbounds (XTLS#5876)
* fix: prune stale observatory status * More readable Refactor observer to clear removed outbounds instead of updating status. Introduced slices package for improved outbound checking. --------- Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
1 parent 434cc68 commit 00946bb

2 files changed

Lines changed: 77 additions & 4 deletions

File tree

app/observatory/observer.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net"
66
"net/http"
77
"net/url"
8+
"slices"
89
"sort"
910
"sync"
1011
"time"
@@ -70,7 +71,7 @@ func (o *Observer) background() {
7071

7172
outbounds := hs.Select(o.config.SubjectSelector)
7273

73-
o.updateStatus(outbounds)
74+
o.clearRemovedOutbounds(outbounds)
7475

7576
sleepTime := time.Second * 10
7677
if o.config.ProbeInterval != 0 {
@@ -111,11 +112,19 @@ func (o *Observer) background() {
111112
}
112113
}
113114

114-
func (o *Observer) updateStatus(outbounds []string) {
115+
func (o *Observer) clearRemovedOutbounds(outbounds []string) {
115116
o.statusLock.Lock()
116117
defer o.statusLock.Unlock()
117-
// TODO should remove old inbound that is removed
118-
_ = outbounds
118+
if len(o.status) == 0 {
119+
return
120+
}
121+
var pruned []*OutboundStatus
122+
for _, status := range o.status {
123+
if slices.Contains(outbounds, status.OutboundTag) {
124+
pruned = append(pruned, status)
125+
}
126+
}
127+
o.status = pruned
119128
}
120129

121130
func (o *Observer) probe(outbound string) ProbeResult {

app/observatory/observer_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package observatory
2+
3+
import "testing"
4+
5+
func TestObserverUpdateStatusPrunesStaleOutbounds(t *testing.T) {
6+
observer := &Observer{
7+
status: []*OutboundStatus{
8+
{
9+
OutboundTag: "keep",
10+
Alive: true,
11+
Delay: 42,
12+
LastErrorReason: "",
13+
LastSeenTime: 111,
14+
LastTryTime: 222,
15+
},
16+
{
17+
OutboundTag: "drop",
18+
Alive: false,
19+
Delay: 99999999,
20+
LastErrorReason: "probe failed",
21+
LastSeenTime: 333,
22+
LastTryTime: 444,
23+
},
24+
},
25+
}
26+
27+
observer.clearRemovedOutbounds([]string{"keep"})
28+
29+
if len(observer.status) != 1 {
30+
t.Fatalf("expected 1 status after pruning, got %d", len(observer.status))
31+
}
32+
33+
got := observer.status[0]
34+
if got.OutboundTag != "keep" {
35+
t.Fatalf("expected remaining status for keep, got %q", got.OutboundTag)
36+
}
37+
if !got.Alive {
38+
t.Fatal("expected remaining status to preserve Alive field")
39+
}
40+
if got.Delay != 42 {
41+
t.Fatalf("expected remaining status to preserve Delay, got %d", got.Delay)
42+
}
43+
if got.LastSeenTime != 111 {
44+
t.Fatalf("expected remaining status to preserve LastSeenTime, got %d", got.LastSeenTime)
45+
}
46+
if got.LastTryTime != 222 {
47+
t.Fatalf("expected remaining status to preserve LastTryTime, got %d", got.LastTryTime)
48+
}
49+
}
50+
51+
func TestObserverUpdateStatusClearsWhenNoOutboundsRemain(t *testing.T) {
52+
observer := &Observer{
53+
status: []*OutboundStatus{
54+
{OutboundTag: "drop-1"},
55+
{OutboundTag: "drop-2"},
56+
},
57+
}
58+
59+
observer.clearRemovedOutbounds(nil)
60+
61+
if len(observer.status) != 0 {
62+
t.Fatalf("expected all statuses to be removed, got %d", len(observer.status))
63+
}
64+
}

0 commit comments

Comments
 (0)