feat(discovery): add RelaySet.PriorityRelaysWithTrace + PriorityMultiHopWithTrace; wire metrics emission and sampled debug log
cognitive committed
Apr 30, 2026 at 05:27 UTC
2b5b6fb678b7d3460b2e5c24a27a1d224bc39cb0
1 file changed
+61
-4
portal/discovery/relayset.go
+61
-4
@@ -9,6 +9,8 @@ import (
9
"sync"
10
"time"
11
12
+ "github.com/rs/zerolog/log"
13
+
14
"github.com/gosuda/portal-tunnel/v2/portal/auth"
15
"github.com/gosuda/portal-tunnel/v2/types"
16
)
@@ -220,7 +222,12 @@ func (s *RelaySet) ConfirmedRelays() []RelayState {
222
return policy.SelectConfirmed(states)
223
}
224
223
-func (s *RelaySet) PriorityRelays(clientState ClientState) []string {
225
+// PriorityRelaysWithTrace returns the same ordered relay-URL list as
226
+// PriorityRelays, plus a SelectionTrace populated with pool statistics,
227
+// eligibility classification, and the scoring parameters used. Prometheus
228
+// metrics are emitted from the trace before returning, and a sampled zerolog
229
+// debug entry is written.
230
+func (s *RelaySet) PriorityRelaysWithTrace(clientState ClientState) ([]string, SelectionTrace) {
231
s.mu.RLock()
232
states := make([]RelayState, 0, len(s.relays))
233
for _, state := range s.relays {
@@ -229,10 +236,33 @@ func (s *RelaySet) PriorityRelays(clientState ClientState) []string {
236
policy := s.policy
237
s.mu.RUnlock()
238
232
- return policy.SelectPriority(states, clientState)
239
+ result, trace := policy.SelectPriorityWithTrace(states, clientState)
240
+ EmitFromTrace(trace)
241
+ log.Debug().
242
+ Uint8("client_hash", trace.ClientHash).
243
+ Int("pool_size", trace.PoolTotal).
244
+ Int("output_count", len(trace.OutputURLs)).
245
+ Str("mode", trace.Mode).
246
+ Bool("congested", trace.Congested).
247
+ Strs("top3", first3(trace.OutputURLs)).
248
+ Msg("relay selection")
249
+ return result, trace
250
}
251
235
-func (s *RelaySet) PriorityMultiHop(clientState ClientState) []string {
252
+// PriorityRelays returns the ordered list of relay URLs for a client. It
253
+// delegates to PriorityRelaysWithTrace and discards the trace. The public
254
+// signature is unchanged through all phases.
255
+func (s *RelaySet) PriorityRelays(clientState ClientState) []string {
256
+ out, _ := s.PriorityRelaysWithTrace(clientState)
257
+ return out
258
+}
259
+
260
+// PriorityMultiHopWithTrace returns the same ordered relay-URL list as
261
+// PriorityMultiHop, plus a SelectionTrace populated with pool statistics,
262
+// eligibility classification, and the scoring parameters used. Prometheus
263
+// metrics are emitted from the trace before returning, and a sampled zerolog
264
+// debug entry is written.
265
+func (s *RelaySet) PriorityMultiHopWithTrace(clientState ClientState) ([]string, SelectionTrace) {
266
s.mu.RLock()
267
states := make([]RelayState, 0, len(s.relays))
268
for _, state := range s.relays {
@@ -241,7 +271,34 @@ func (s *RelaySet) PriorityMultiHop(clientState ClientState) []string {
271
policy := s.policy
272
s.mu.RUnlock()
273
244
- return policy.SelectMultiHop(states, clientState)
274
+ result, trace := policy.SelectMultiHopWithTrace(states, clientState)
275
+ EmitFromTrace(trace)
276
+ log.Debug().
277
+ Uint8("client_hash", trace.ClientHash).
278
+ Int("pool_size", trace.PoolTotal).
279
+ Int("output_count", len(trace.OutputURLs)).
280
+ Str("mode", trace.Mode).
281
+ Bool("congested", trace.Congested).
282
+ Strs("top3", first3(trace.OutputURLs)).
283
+ Msg("relay selection")
284
+ return result, trace
285
+}
286
+
287
+// PriorityMultiHop returns the ordered list of relay URLs for multi-hop
288
+// routing. It delegates to PriorityMultiHopWithTrace and discards the trace.
289
+// The public signature is unchanged through all phases.
290
+func (s *RelaySet) PriorityMultiHop(clientState ClientState) []string {
291
+ out, _ := s.PriorityMultiHopWithTrace(clientState)
292
+ return out
293
+}
294
+
295
+// first3 returns a slice containing the first three elements of s, or all
296
+// elements if s has fewer than three. It never modifies the input slice.
297
+func first3(s []string) []string {
298
+ if len(s) <= 3 {
299
+ return s
300
+ }
301
+ return s[:3]
302
}
303
304
func (s *RelaySet) OverlayPeerStates() []RelayState {