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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user