// result_query.go:jobs 表按 request_id 查询(W3 新增文件;禁改 W1 既有文件)。 package store import ( "database/sql" "fmt" "time" "onesvm.com/onesvm/browser-server/internal/config" ) // JobByRequestID 按 request_id 查最新一条任务(/result/{request_id} 支撑; // request_id 在 jobs 表唯一(gateway 生成 ULID),取 id 最大者兜底重试重入)。 func (d *DB) JobByRequestID(reqID string) (*Job, error) { job, err := scanJob(d.raw.QueryRow( `SELECT id, request_id, intent, payload, priority, status, available_at, lease_until, attempts, worker, error, created_at, updated_at FROM jobs WHERE request_id = ? ORDER BY id DESC LIMIT 1`, reqID)) if err != sql.ErrNoRows && err != nil { return nil, fmt.Errorf("store: JobByRequestID: %w", err) } if err == sql.ErrNoRows { return nil, ErrNotFound } return job, nil } // CountDead 死信数(/metrics)。 func (d *DB) CountDead() (int, error) { var n int if err := d.raw.QueryRow(`SELECT COUNT(id) FROM dead_letters`).Scan(&n); err != nil { return 0, fmt.Errorf("store: CountDead: %w", err) } return n, nil } // QueueWaitAvgMs 平均排队等待(created_at → 首次 running 的 updated_at 差, // 简化口径:全部 jobs 的 (updated_at-created_at) 均值,毫秒;估算指标)。 func (d *DB) QueueWaitAvgMs() (float64, error) { rows, err := d.raw.Query(`SELECT created_at, updated_at FROM jobs WHERE status IN ('done','failed','dead') LIMIT 500`) if err != nil { return 0, fmt.Errorf("store: QueueWaitAvgMs: %w", err) } defer rows.Close() var sumMS, n int64 for rows.Next() { var c, u string if err := rows.Scan(&c, &u); err != nil { return 0, fmt.Errorf("store: QueueWaitAvgMs scan: %w", err) } ct, err1 := time.Parse(time.RFC3339, c) ut, err2 := time.Parse(time.RFC3339, u) if err1 == nil && err2 == nil && ut.After(ct) { sumMS += ut.Sub(ct).Milliseconds() n++ } } if err := rows.Err(); err != nil { return 0, fmt.Errorf("store: QueueWaitAvgMs rows: %w", err) } if n == 0 { return 0, nil } return float64(sumMS) / float64(n), nil } var _ = config.Now