refactor(server/monitoring): route monitor desktop tasks by account presence

Daily monitoring tasks were pinned to a single primary client. If that
client went offline the task wedged even when another desktop client of
the same workspace was online and bound to the same account. Drop the
primary-client constraint on the materialized collect rows, and instead
pick a target per-platform from live account/client presence in Redis,
falling back to the DB client_id when the desktop client is still flagged
online. Desktop task lease for kind=monitor now matches by account
ownership in addition to target_client_id, so any client owning the
account can drain the queue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 09:14:24 +08:00
parent 93cf734673
commit 7605890a14
10 changed files with 600 additions and 48 deletions
@@ -26,6 +26,7 @@ type monitorDesktopTaskSpec struct {
MonitorTaskID int64
TenantID int64
WorkspaceID int64
TargetAccountID uuid.UUID
TargetClientID uuid.UUID
PlatformID string
BusinessDate string
@@ -40,6 +41,19 @@ type monitorDesktopTaskSpec struct {
InterruptGeneration int
}
type monitorDesktopAccountCandidate struct {
PlatformID string
AccountID uuid.UUID
DBClientID *uuid.UUID
HealthRank int
RecordOrder int
}
type monitorDesktopTaskTarget struct {
AccountID uuid.UUID
ClientID uuid.UUID
}
type monitorDesktopInterruptTarget struct {
TaskID string
MonitorTaskID int64
@@ -181,7 +195,7 @@ func (s *MonitoringService) dispatchMonitorDesktopTasks(
return nil, nil, nil
}
accountMap, err := s.loadMonitorDesktopAccountMap(ctx, specs[0].TenantID, specs[0].WorkspaceID, specs[0].TargetClientID)
targets, err := s.loadMonitorDesktopTaskTargets(ctx, specs[0].TenantID, specs[0].WorkspaceID, specs)
if err != nil {
return nil, nil, err
}
@@ -196,7 +210,15 @@ func (s *MonitoringService) dispatchMonitorDesktopTasks(
publishableTasks := make([]*repository.DesktopTask, 0, len(specs))
fallbackLegacyTaskIDs := make([]int64, 0)
for _, spec := range specs {
task, shouldPublish, fallbackLegacy, upsertErr := s.upsertMonitorDesktopTask(ctx, tx, repo, spec, accountMap)
target, ok := targets[normalizeMonitoringPlatformID(spec.PlatformID)]
if !ok || target.AccountID == uuid.Nil || target.ClientID == uuid.Nil {
fallbackLegacyTaskIDs = append(fallbackLegacyTaskIDs, spec.MonitorTaskID)
continue
}
spec.TargetAccountID = target.AccountID
spec.TargetClientID = target.ClientID
task, shouldPublish, fallbackLegacy, upsertErr := s.upsertMonitorDesktopTask(ctx, tx, repo, spec)
if upsertErr != nil {
return nil, nil, upsertErr
}
@@ -215,20 +237,32 @@ func (s *MonitoringService) dispatchMonitorDesktopTasks(
return publishableTasks, fallbackLegacyTaskIDs, nil
}
func (s *MonitoringService) loadMonitorDesktopAccountMap(
func (s *MonitoringService) loadMonitorDesktopTaskTargets(
ctx context.Context,
tenantID, workspaceID int64,
targetClientID uuid.UUID,
) (map[string]uuid.UUID, error) {
specs []monitorDesktopTaskSpec,
) (map[string]monitorDesktopTaskTarget, error) {
platformIDs := uniqueMonitorDesktopTaskPlatformIDs(specs)
if len(platformIDs) == 0 {
return nil, nil
}
rows, err := s.businessPool.Query(ctx, `
SELECT DISTINCT ON (platform_id)
SELECT
platform_id,
desktop_id
desktop_id,
COALESCE(client_id::text, '') AS client_id,
CASE health
WHEN 'live' THEN 0
WHEN 'risk' THEN 1
WHEN 'captcha' THEN 2
ELSE 3
END AS health_rank
FROM platform_accounts
WHERE tenant_id = $1
AND workspace_id = $2
AND client_id = $3
AND deleted_at IS NULL
AND platform_id = ANY($3::text[])
ORDER BY platform_id ASC,
CASE health
WHEN 'live' THEN 0
@@ -239,35 +273,158 @@ func (s *MonitoringService) loadMonitorDesktopAccountMap(
verified_at DESC NULLS LAST,
updated_at DESC,
created_at DESC
`, tenantID, workspaceID, targetClientID)
`, tenantID, workspaceID, platformIDs)
if err != nil {
return nil, response.ErrInternal(50117, "desktop_account_lookup_failed", "failed to resolve monitor desktop accounts")
}
defer rows.Close()
result := make(map[string]uuid.UUID)
candidates := make([]monitorDesktopAccountCandidate, 0)
for rows.Next() {
var (
platformID string
desktopID uuid.UUID
item monitorDesktopAccountCandidate
clientIDText string
)
if scanErr := rows.Scan(&platformID, &desktopID); scanErr != nil {
if scanErr := rows.Scan(&item.PlatformID, &item.AccountID, &clientIDText, &item.HealthRank); scanErr != nil {
return nil, response.ErrInternal(50117, "desktop_account_lookup_failed", "failed to parse monitor desktop accounts")
}
result[platformID] = desktopID
if parsedClientID, parseErr := uuid.Parse(strings.TrimSpace(clientIDText)); parseErr == nil {
item.DBClientID = &parsedClientID
}
item.PlatformID = normalizeMonitoringPlatformID(item.PlatformID)
item.RecordOrder = len(candidates)
candidates = append(candidates, item)
}
if err := rows.Err(); err != nil {
return nil, response.ErrInternal(50117, "desktop_account_lookup_failed", "failed to iterate monitor desktop accounts")
}
accountIDs := monitorDesktopAccountCandidateAccountIDs(candidates)
accountPresence := loadDesktopAccountPresence(ctx, s.redis, accountIDs)
clientIDs := monitorDesktopAccountCandidateClientIDs(candidates, accountPresence)
onlineClients, err := s.loadOnlineMonitorDesktopClients(ctx, workspaceID, clientIDs)
if err != nil {
return nil, err
}
return selectMonitorDesktopTaskTargets(candidates, accountPresence, onlineClients), nil
}
func uniqueMonitorDesktopTaskPlatformIDs(specs []monitorDesktopTaskSpec) []string {
if len(specs) == 0 {
return nil
}
seen := make(map[string]struct{}, len(specs))
result := make([]string, 0, len(specs))
for _, spec := range specs {
platformID := normalizeMonitoringPlatformID(spec.PlatformID)
if platformID == "" {
continue
}
if _, ok := seen[platformID]; ok {
continue
}
seen[platformID] = struct{}{}
result = append(result, platformID)
}
return result
}
func monitorDesktopAccountCandidateAccountIDs(candidates []monitorDesktopAccountCandidate) []uuid.UUID {
result := make([]uuid.UUID, 0, len(candidates))
for _, candidate := range candidates {
if candidate.AccountID != uuid.Nil {
result = append(result, candidate.AccountID)
}
}
return uniqueUUIDs(result)
}
func monitorDesktopAccountCandidateClientIDs(candidates []monitorDesktopAccountCandidate, accountPresence map[uuid.UUID]uuid.UUID) []uuid.UUID {
result := make([]uuid.UUID, 0, len(candidates)*2)
for _, candidate := range candidates {
if candidate.DBClientID != nil && *candidate.DBClientID != uuid.Nil {
result = append(result, *candidate.DBClientID)
}
if accountPresence != nil {
if clientID, ok := accountPresence[candidate.AccountID]; ok && clientID != uuid.Nil {
result = append(result, clientID)
}
}
}
return uniqueUUIDs(result)
}
func (s *MonitoringService) loadOnlineMonitorDesktopClients(ctx context.Context, workspaceID int64, clientIDs []uuid.UUID) (map[uuid.UUID]bool, error) {
result := make(map[uuid.UUID]bool)
clientIDs = uniqueUUIDs(clientIDs)
if len(clientIDs) == 0 {
return result, nil
}
for clientID, online := range loadDesktopClientPresence(ctx, s.redis, clientIDs) {
if online {
result[clientID] = true
}
}
rows, err := s.businessPool.Query(ctx, `
SELECT id
FROM desktop_clients
WHERE workspace_id = $1
AND id = ANY($2::uuid[])
AND revoked_at IS NULL
AND last_seen_at IS NOT NULL
AND last_seen_at >= NOW() - $3::interval
`, workspaceID, clientIDs, formatPgInterval(desktopClientPresenceTTL))
if err != nil {
return nil, response.ErrInternal(50117, "desktop_client_lookup_failed", "failed to resolve online monitor desktop clients")
}
defer rows.Close()
for rows.Next() {
var clientID uuid.UUID
if scanErr := rows.Scan(&clientID); scanErr != nil {
return nil, response.ErrInternal(50117, "desktop_client_lookup_failed", "failed to parse online monitor desktop clients")
}
result[clientID] = true
}
if err := rows.Err(); err != nil {
return nil, response.ErrInternal(50117, "desktop_client_lookup_failed", "failed to iterate online monitor desktop clients")
}
return result, nil
}
func selectMonitorDesktopTaskTargets(
candidates []monitorDesktopAccountCandidate,
accountPresence map[uuid.UUID]uuid.UUID,
onlineClients map[uuid.UUID]bool,
) map[string]monitorDesktopTaskTarget {
result := make(map[string]monitorDesktopTaskTarget)
for _, candidate := range candidates {
platformID := normalizeMonitoringPlatformID(candidate.PlatformID)
if platformID == "" || candidate.AccountID == uuid.Nil {
continue
}
if _, exists := result[platformID]; exists {
continue
}
if clientID, ok := accountPresence[candidate.AccountID]; ok && clientID != uuid.Nil && onlineClients[clientID] {
result[platformID] = monitorDesktopTaskTarget{AccountID: candidate.AccountID, ClientID: clientID}
continue
}
if candidate.DBClientID != nil && *candidate.DBClientID != uuid.Nil && onlineClients[*candidate.DBClientID] {
result[platformID] = monitorDesktopTaskTarget{AccountID: candidate.AccountID, ClientID: *candidate.DBClientID}
}
}
return result
}
func (s *MonitoringService) upsertMonitorDesktopTask(
ctx context.Context,
tx pgx.Tx,
repo repository.DesktopTaskRepository,
spec monitorDesktopTaskSpec,
accountMap map[string]uuid.UUID,
) (*repository.DesktopTask, bool, bool, error) {
var (
existingDesktopID uuid.UUID
@@ -297,9 +454,9 @@ func (s *MonitoringService) upsertMonitorDesktopTask(
if existingStatus != "queued" && !expiredInProgress {
return task, false, false, nil
}
targetAccountID := task.TargetAccountID
if override, ok := accountMap[spec.PlatformID]; ok {
targetAccountID = override
targetAccountID := spec.TargetAccountID
if targetAccountID == uuid.Nil {
targetAccountID = task.TargetAccountID
}
payloadJSON, payloadErr := marshalMonitorDesktopTaskPayload(spec)
if payloadErr != nil {
@@ -372,8 +529,8 @@ func (s *MonitoringService) upsertMonitorDesktopTask(
return nil, false, false, response.ErrInternal(50118, "desktop_task_lookup_failed", "failed to inspect active phase2 monitor desktop task")
}
targetAccountID, ok := accountMap[spec.PlatformID]
if !ok {
targetAccountID := spec.TargetAccountID
if targetAccountID == uuid.Nil || spec.TargetClientID == uuid.Nil {
return nil, false, true, nil
}
payloadJSON, payloadErr := marshalMonitorDesktopTaskPayload(spec)