From 8640c8177ca544558fa6c89eeb76b72e084c7fd9 Mon Sep 17 00:00:00 2001 From: esubaalew Date: Tue, 24 Feb 2026 15:18:54 +0300 Subject: [PATCH] fix(wecom): correctly retain boundary message during dedupe map rotation When the dedupe map rotates, the previous logic entirely cleared the map, meaning the message that triggered the rotation was immediately forgotten and could be duplicated immediately. This change seeds the new map with the current message to prevent that. Also adds a defensive nil check. --- pkg/channels/wecom/dedupe.go | 8 ++++++-- pkg/channels/wecom_dedupe_test.go | 15 ++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/pkg/channels/wecom/dedupe.go b/pkg/channels/wecom/dedupe.go index 09f5a8a41..8ca98a30d 100644 --- a/pkg/channels/wecom/dedupe.go +++ b/pkg/channels/wecom/dedupe.go @@ -14,14 +14,18 @@ func markMessageProcessed(msgMu *sync.RWMutex, processedMsgs *map[string]bool, m msgMu.Lock() defer msgMu.Unlock() + if *processedMsgs == nil { + *processedMsgs = make(map[string]bool) + } + if (*processedMsgs)[msgID] { return false } (*processedMsgs)[msgID] = true - // Keep existing behavior: when over limit, reset dedupe map entirely. + // When over limit, reset dedupe map but keep the current message. if len(*processedMsgs) > maxEntries { - *processedMsgs = make(map[string]bool) + *processedMsgs = map[string]bool{msgID: true} } return true diff --git a/pkg/channels/wecom_dedupe_test.go b/pkg/channels/wecom_dedupe_test.go index 467f79979..71f987892 100644 --- a/pkg/channels/wecom_dedupe_test.go +++ b/pkg/channels/wecom_dedupe_test.go @@ -60,14 +60,19 @@ func TestMarkMessageProcessed_RotationClearsMapAtBoundary(t *testing.T) { t.Fatalf("expected map size 1 after first insert, got %d", len(processed)) } - // Inserting second unique message exceeds maxEntries and should reset map. + // Inserting second unique message exceeds maxEntries and should reset map, but keep the new message. if ok := markMessageProcessed(&mu, &processed, "msg-2", 1); !ok { t.Fatalf("second unique message should be accepted") } - if len(processed) != 0 { - t.Fatalf("expected map to be reset after rotation, got size %d", len(processed)) + if len(processed) != 1 { + t.Fatalf("expected map to retain current message after rotation, got size %d", len(processed)) } - if processed["msg-2"] { - t.Fatalf("expected current message marker to be cleared after rotation") + if !processed["msg-2"] { + t.Fatalf("expected current message marker to be retained after rotation") + } + + // Because msg-2 was retained, an immediate duplicate should be rejected. + if ok := markMessageProcessed(&mu, &processed, "msg-2", 1); ok { + t.Fatalf("duplicate message immediately after rotation should be rejected") } }