131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
const (
|
|
defaultMonitoringReceivedAlertThreshold = 15 * time.Minute
|
|
defaultMonitoringReceivedInspectLimit = 100
|
|
)
|
|
|
|
type monitoringStaleReceivedTask struct {
|
|
ID int64
|
|
TenantID int64
|
|
BrandID int64
|
|
QuestionID int64
|
|
AIPlatformID string
|
|
CollectorType string
|
|
BusinessDate time.Time
|
|
CallbackReceivedAt time.Time
|
|
}
|
|
|
|
type MonitoringStaleReceivedTask = monitoringStaleReceivedTask
|
|
|
|
func markMonitoringStaleReceivedTasks(ctx context.Context, tx pgx.Tx, now time.Time, threshold time.Duration, limit int) ([]monitoringStaleReceivedTask, error) {
|
|
if limit <= 0 {
|
|
limit = defaultMonitoringReceivedInspectLimit
|
|
}
|
|
|
|
rows, err := tx.Query(ctx, `
|
|
WITH candidates AS (
|
|
SELECT
|
|
id,
|
|
tenant_id,
|
|
brand_id,
|
|
question_id,
|
|
ai_platform_id,
|
|
collector_type,
|
|
business_date,
|
|
callback_received_at
|
|
FROM monitoring_collect_tasks
|
|
WHERE collector_type = $1
|
|
AND (
|
|
status = 'received'
|
|
OR (
|
|
COALESCE(execution_owner, 'legacy') = 'desktop_tasks'
|
|
AND status = 'pending'
|
|
AND callback_received_at IS NOT NULL
|
|
)
|
|
)
|
|
AND callback_received_at IS NOT NULL
|
|
AND callback_received_at < $2
|
|
AND (
|
|
request_payload_json IS NULL
|
|
OR jsonb_typeof(request_payload_json) <> 'object'
|
|
OR NOT (request_payload_json ? 'received_alerted_at')
|
|
)
|
|
ORDER BY callback_received_at ASC, id ASC
|
|
FOR UPDATE SKIP LOCKED
|
|
LIMIT $3
|
|
)
|
|
UPDATE monitoring_collect_tasks t
|
|
SET request_payload_json = (
|
|
CASE
|
|
WHEN t.request_payload_json IS NULL OR jsonb_typeof(t.request_payload_json) <> 'object'
|
|
THEN '{}'::jsonb
|
|
ELSE t.request_payload_json
|
|
END
|
|
) || jsonb_build_object(
|
|
'received_alerted_at', $4::text,
|
|
'received_alert_count', (
|
|
CASE
|
|
WHEN COALESCE(t.request_payload_json ->> 'received_alert_count', '') ~ '^[0-9]+$'
|
|
THEN (t.request_payload_json ->> 'received_alert_count')::int
|
|
ELSE 0
|
|
END
|
|
) + 1
|
|
),
|
|
updated_at = NOW()
|
|
FROM candidates c
|
|
WHERE t.id = c.id
|
|
RETURNING
|
|
c.id,
|
|
c.tenant_id,
|
|
c.brand_id,
|
|
c.question_id,
|
|
c.ai_platform_id,
|
|
c.collector_type,
|
|
c.business_date,
|
|
c.callback_received_at
|
|
`, monitoringCollectorType, now.Add(-threshold), limit, now.UTC().Format(time.RFC3339))
|
|
if err != nil {
|
|
return nil, monitoringInternalError(50041, "received_watchdog_query_failed", "failed to inspect stale received monitoring tasks", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
result := make([]monitoringStaleReceivedTask, 0)
|
|
for rows.Next() {
|
|
var item monitoringStaleReceivedTask
|
|
if scanErr := rows.Scan(
|
|
&item.ID,
|
|
&item.TenantID,
|
|
&item.BrandID,
|
|
&item.QuestionID,
|
|
&item.AIPlatformID,
|
|
&item.CollectorType,
|
|
&item.BusinessDate,
|
|
&item.CallbackReceivedAt,
|
|
); scanErr != nil {
|
|
return nil, monitoringInternalError(50041, "received_watchdog_scan_failed", "failed to parse stale received monitoring tasks", scanErr)
|
|
}
|
|
result = append(result, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, monitoringInternalError(50041, "received_watchdog_scan_failed", "failed to iterate stale received monitoring tasks", err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func MarkMonitoringStaleReceivedTasks(ctx context.Context, tx pgx.Tx, now time.Time, threshold time.Duration, limit int) ([]MonitoringStaleReceivedTask, error) {
|
|
items, err := markMonitoringStaleReceivedTasks(ctx, tx, now, threshold, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|