From aafe3a31fd296b8ce993c1aa16b64f9633edfcdc Mon Sep 17 00:00:00 2001 From: J Date: Tue, 23 Jun 2026 17:40:07 +0800 Subject: [PATCH] feat(lark): add MULTICA_LARK_HUB_DISABLED switch for the channel cutover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lark_*->channel_* cutover needs a way to make the Feishu bot briefly unavailable WITHOUT taking down the whole multica-api process — the Lark hub is a goroutine inside it, not a separate Deployment. MULTICA_LARK_HUB_DISABLED=true parks the hub at startup: the API serves HTTP normally but never claims a WS lease or opens a Feishu connection. Rollout (see migration 124 ROLLOUT note): ship the new release with the flag SET so new pods run API-only while old pods (hub on lark_*) drain during the rolling deploy — the two hubs never overlap. After the old pods are gone and migration 124 has run, flip the flag off; the new hub comes up on channel_*. The old backend does NOT need this switch — its hub stops when k8s terminates the old pods, not via a flag. Nil-ing LarkHub reuses the existing not-configured path so both the startup start and the shutdown join skip it. MUL-3515 Co-authored-by: multica-agent --- server/cmd/server/main.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go index 5fe9e31893..2658d66f17 100644 --- a/server/cmd/server/main.go +++ b/server/cmd/server/main.go @@ -357,6 +357,19 @@ func main() { // Lark do not pay any goroutine cost. Lifecycle is bound to // sweepCtx so the Hub winds down alongside the other long-running // workers, AFTER the HTTP server has drained. + // Cutover control (MUL-3515): MULTICA_LARK_HUB_DISABLED parks the Lark + // inbound hub WITHOUT taking down the rest of the API — the process still + // serves HTTP normally, it just never claims a WS lease or opens a Feishu + // connection. This is the switch the lark_*->channel_* rollout uses to keep + // the NEW hub dormant while old pods (still running their hub on lark_*) + // drain, so the two never double-process the same bot. Nil-ing LarkHub here + // reuses the same "Lark not configured" path, so the shutdown join below + // also skips it. The operator flips this off after migration 124 + old-pod + // drain to bring the new hub up on channel_*. See migration 124's ROLLOUT note. + if h.LarkHub != nil && os.Getenv("MULTICA_LARK_HUB_DISABLED") == "true" { + slog.Warn("Lark inbound hub disabled via MULTICA_LARK_HUB_DISABLED; API serves normally but no Feishu WebSocket is opened") + h.LarkHub = nil + } if h.LarkHub != nil { go h.LarkHub.Run(sweepCtx) }