Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions internal/web/service/outbound_subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,13 @@ func (s *OutboundSubscriptionService) recordError(sub *model.OutboundSubscriptio
// written back into parsed[i]["tag"]. The returned slice holds the assigned tags
// in order. When tagPrefix is empty a "sub<subID>-" prefix is used for fresh tags.
func assignStableTags(parsed []link.Outbound, identities []string, prev map[string]string, prevTagByIndex map[int]string, subID int, tagPrefix string) []string {
reservedStableTags := map[string]bool{}
for i := range parsed {
if i < len(identities) && prev[identities[i]] != "" {
reservedStableTags[prev[identities[i]]] = true
}
}

used := map[string]bool{} // uniqueness within this refresh batch
assigned := make([]string, len(parsed))
for i := range parsed {
Expand All @@ -470,12 +477,14 @@ func assignStableTags(parsed []link.Outbound, identities []string, prev map[stri
id = identities[i]
}
candidate := ""
identityTag := ""
if old, ok := prev[id]; ok && old != "" {
candidate = old
identityTag = old
}
if candidate == "" {
// try to reuse by rough positional match from previous fetch (best effort)
if old, ok := prevTagByIndex[i]; ok && old != "" {
if old, ok := prevTagByIndex[i]; ok && old != "" && !reservedStableTags[old] {
candidate = old
}
}
Expand All @@ -493,7 +502,7 @@ func assignStableTags(parsed []link.Outbound, identities []string, prev map[stri
}
// ensure local uniqueness inside this batch
final := candidate
for k := 1; used[final]; k++ {
for k := 1; used[final] || (reservedStableTags[final] && final != identityTag); k++ {
final = fmt.Sprintf("%s-%d", candidate, k)
}
used[final] = true
Expand Down
44 changes: 43 additions & 1 deletion internal/web/service/outbound_subscription_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"bytes"
"errors"
"slices"
"testing"

"gorm.io/gorm"
Expand Down Expand Up @@ -166,12 +167,53 @@ func TestAssignStableTags(t *testing.T) {

t.Run("falls back to the previous tag at the same position", func(t *testing.T) {
parsed := []link.Outbound{{"tag": "JP-Tokyo"}}
got := assignStableTags(parsed, []string{"id-new"}, map[string]string{}, map[int]string{0: "sub1-oldpos"}, 1, "")
prev := map[string]string{"id-gone": "sub1-oldpos"}
got := assignStableTags(parsed, []string{"id-new"}, prev, map[int]string{0: "sub1-oldpos"}, 1, "")
if got[0] != "sub1-oldpos" {
t.Fatalf("got %q, want sub1-oldpos", got[0])
}
})

t.Run("does not let an inserted link steal a stable tag", func(t *testing.T) {
parsed := []link.Outbound{{"tag": "Poland"}, {"tag": "NewServer"}, {"tag": "Netherlands"}}
prev := map[string]string{
"id-poland": "sub1-poland",
"id-netherlands": "sub1-netherlands",
}
prevTagByIndex := map[int]string{0: "sub1-poland", 1: "sub1-netherlands"}

got := assignStableTags(parsed, []string{"id-poland", "id-new", "id-netherlands"}, prev, prevTagByIndex, 1, "")
want := []string{"sub1-poland", "sub1-newserver", "sub1-netherlands"}
if !slices.Equal(got, want) {
t.Fatalf("got %v, want %v", got, want)
}
})

t.Run("does not let a fresh tag steal a stable tag", func(t *testing.T) {
parsed := []link.Outbound{{"tag": "Netherlands"}, {"tag": "Renamed"}}
prev := map[string]string{"id-netherlands": "sub1-netherlands"}

got := assignStableTags(parsed, []string{"id-new", "id-netherlands"}, prev, nil, 1, "")
want := []string{"sub1-netherlands-1", "sub1-netherlands"}
if !slices.Equal(got, want) {
t.Fatalf("got %v, want %v", got, want)
}
})

t.Run("skips reserved tags while adding a suffix", func(t *testing.T) {
parsed := []link.Outbound{{"tag": "Netherlands"}, {"tag": "First"}, {"tag": "Second"}}
prev := map[string]string{
"id-first": "sub1-netherlands",
"id-second": "sub1-netherlands-1",
}

got := assignStableTags(parsed, []string{"id-new", "id-first", "id-second"}, prev, nil, 1, "")
want := []string{"sub1-netherlands-2", "sub1-netherlands", "sub1-netherlands-1"}
if !slices.Equal(got, want) {
t.Fatalf("got %v, want %v", got, want)
}
})

t.Run("allocates a fresh tag with the default sub<id>- prefix", func(t *testing.T) {
parsed := []link.Outbound{{"tag": "Tokyo"}}
got := assignStableTags(parsed, []string{"id-x"}, nil, nil, 7, "")
Expand Down
Loading