From 1789f4007cf8be0a7700b2782391cf9ce6ecab2c Mon Sep 17 00:00:00 2001 From: toki Date: Sun, 2 Aug 2026 12:21:16 +0900 Subject: [PATCH] =?UTF-8?q?fix(edge):=20=EB=8F=99=EC=8B=9C=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20ID=20=EC=B6=A9=EB=8F=8C=EC=9D=84=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/edge/internal/service/run_wire.go | 18 ++++++++++++- .../internal/service/service_internal_test.go | 26 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/apps/edge/internal/service/run_wire.go b/apps/edge/internal/service/run_wire.go index 6bf07a41..badb07ce 100644 --- a/apps/edge/internal/service/run_wire.go +++ b/apps/edge/internal/service/run_wire.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "sync/atomic" "time" "google.golang.org/protobuf/types/known/structpb" @@ -10,8 +11,23 @@ import ( iop "iop/proto/gen/iop" ) +var lastRunIDNanos atomic.Int64 + func NewRunID() string { - return fmt.Sprintf("manual-%d", time.Now().UnixNano()) + return newRunIDAt(time.Now().UnixNano()) +} + +func newRunIDAt(now int64) string { + for { + previous := lastRunIDNanos.Load() + next := now + if next <= previous { + next = previous + 1 + } + if lastRunIDNanos.CompareAndSwap(previous, next) { + return fmt.Sprintf("manual-%d", next) + } + } } func IsNodeDisconnected(event *iop.EdgeNodeEvent) bool { diff --git a/apps/edge/internal/service/service_internal_test.go b/apps/edge/internal/service/service_internal_test.go index ee0ad705..838afbb4 100644 --- a/apps/edge/internal/service/service_internal_test.go +++ b/apps/edge/internal/service/service_internal_test.go @@ -18,6 +18,32 @@ import ( iop "iop/proto/gen/iop" ) +func TestRunIDRemainsUniqueAtSameClockTick(t *testing.T) { + const count = 128 + ids := make(chan string, count) + var wg sync.WaitGroup + wg.Add(count) + for i := 0; i < count; i++ { + go func() { + defer wg.Done() + ids <- newRunIDAt(1) + }() + } + wg.Wait() + close(ids) + + seen := make(map[string]struct{}, count) + for id := range ids { + if _, exists := seen[id]; exists { + t.Fatalf("duplicate run id %q", id) + } + seen[id] = struct{}{} + } + if len(seen) != count { + t.Fatalf("unique run ids = %d, want %d", len(seen), count) + } +} + // TestRefreshProviderCapacityAffectsNextDispatch verifies that a provider // capacity change applied via SetRuntimeConfig is reflected in the candidate // capacity used for the next dispatch admission, since candidates are rebuilt