mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-06 05:49:12 +02:00
* fix(daemon): prevent duplicate runtime registration on profile switch The daemon_id included a profile name suffix (e.g. "hostname-staging"), so switching profiles created a new daemon_id that bypassed the UPSERT dedup constraint, leaving orphaned runtime records in the database. Three changes: - Remove profile suffix from daemon_id — use stable hostname only. The unique constraint (workspace_id, daemon_id, provider) already prevents collisions within the same workspace. - Auto-migrate agents from old offline runtimes to the newly registered runtime during DaemonRegister (same workspace/provider/owner). - Add TTL-based GC in the runtime sweeper to delete offline runtimes with no active agents after 7 days. Closes MUL-695 * fix(daemon): address code review issues on PR #906 1. Move gcRuntimes() to the main sweep loop — previously it was inside sweepStaleRuntimes() after an early return, so it only ran when new runtimes were marked stale. Now it runs every sweep cycle independently. 2. Fix DeleteStaleOfflineRuntimes to exclude runtimes with ANY agent reference (not just active ones). The FK agent.runtime_id is ON DELETE RESTRICT, so archived agents also block deletion. 3. Scope MigrateAgentsToRuntime to the same machine by matching daemon_id LIKE '<current_daemon_id>-%'. This prevents cross-machine agent migration when the same user has multiple devices.
175 lines
4.8 KiB
Go
175 lines
4.8 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.30.0
|
|
// source: runtime_usage.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const getRuntimeTaskHourlyActivity = `-- name: GetRuntimeTaskHourlyActivity :many
|
|
SELECT EXTRACT(HOUR FROM started_at)::int AS hour, COUNT(*)::int AS count
|
|
FROM agent_task_queue
|
|
WHERE runtime_id = $1 AND started_at IS NOT NULL
|
|
GROUP BY hour
|
|
ORDER BY hour
|
|
`
|
|
|
|
type GetRuntimeTaskHourlyActivityRow struct {
|
|
Hour int32 `json:"hour"`
|
|
Count int32 `json:"count"`
|
|
}
|
|
|
|
func (q *Queries) GetRuntimeTaskHourlyActivity(ctx context.Context, runtimeID pgtype.UUID) ([]GetRuntimeTaskHourlyActivityRow, error) {
|
|
rows, err := q.db.Query(ctx, getRuntimeTaskHourlyActivity, runtimeID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []GetRuntimeTaskHourlyActivityRow{}
|
|
for rows.Next() {
|
|
var i GetRuntimeTaskHourlyActivityRow
|
|
if err := rows.Scan(&i.Hour, &i.Count); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getRuntimeUsageSummary = `-- name: GetRuntimeUsageSummary :many
|
|
SELECT provider, model,
|
|
SUM(input_tokens)::bigint AS total_input_tokens,
|
|
SUM(output_tokens)::bigint AS total_output_tokens,
|
|
SUM(cache_read_tokens)::bigint AS total_cache_read_tokens,
|
|
SUM(cache_write_tokens)::bigint AS total_cache_write_tokens
|
|
FROM runtime_usage
|
|
WHERE runtime_id = $1
|
|
GROUP BY provider, model
|
|
ORDER BY provider, model
|
|
`
|
|
|
|
type GetRuntimeUsageSummaryRow struct {
|
|
Provider string `json:"provider"`
|
|
Model string `json:"model"`
|
|
TotalInputTokens int64 `json:"total_input_tokens"`
|
|
TotalOutputTokens int64 `json:"total_output_tokens"`
|
|
TotalCacheReadTokens int64 `json:"total_cache_read_tokens"`
|
|
TotalCacheWriteTokens int64 `json:"total_cache_write_tokens"`
|
|
}
|
|
|
|
func (q *Queries) GetRuntimeUsageSummary(ctx context.Context, runtimeID pgtype.UUID) ([]GetRuntimeUsageSummaryRow, error) {
|
|
rows, err := q.db.Query(ctx, getRuntimeUsageSummary, runtimeID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []GetRuntimeUsageSummaryRow{}
|
|
for rows.Next() {
|
|
var i GetRuntimeUsageSummaryRow
|
|
if err := rows.Scan(
|
|
&i.Provider,
|
|
&i.Model,
|
|
&i.TotalInputTokens,
|
|
&i.TotalOutputTokens,
|
|
&i.TotalCacheReadTokens,
|
|
&i.TotalCacheWriteTokens,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listRuntimeUsage = `-- name: ListRuntimeUsage :many
|
|
SELECT id, runtime_id, date, provider, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, created_at, updated_at FROM runtime_usage
|
|
WHERE runtime_id = $1
|
|
AND date >= $2
|
|
ORDER BY date DESC
|
|
`
|
|
|
|
type ListRuntimeUsageParams struct {
|
|
RuntimeID pgtype.UUID `json:"runtime_id"`
|
|
Date pgtype.Date `json:"date"`
|
|
}
|
|
|
|
func (q *Queries) ListRuntimeUsage(ctx context.Context, arg ListRuntimeUsageParams) ([]RuntimeUsage, error) {
|
|
rows, err := q.db.Query(ctx, listRuntimeUsage, arg.RuntimeID, arg.Date)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []RuntimeUsage{}
|
|
for rows.Next() {
|
|
var i RuntimeUsage
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.RuntimeID,
|
|
&i.Date,
|
|
&i.Provider,
|
|
&i.Model,
|
|
&i.InputTokens,
|
|
&i.OutputTokens,
|
|
&i.CacheReadTokens,
|
|
&i.CacheWriteTokens,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const upsertRuntimeUsage = `-- name: UpsertRuntimeUsage :exec
|
|
INSERT INTO runtime_usage (runtime_id, date, provider, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
ON CONFLICT (runtime_id, date, provider, model)
|
|
DO UPDATE SET
|
|
input_tokens = EXCLUDED.input_tokens,
|
|
output_tokens = EXCLUDED.output_tokens,
|
|
cache_read_tokens = EXCLUDED.cache_read_tokens,
|
|
cache_write_tokens = EXCLUDED.cache_write_tokens,
|
|
updated_at = now()
|
|
`
|
|
|
|
type UpsertRuntimeUsageParams struct {
|
|
RuntimeID pgtype.UUID `json:"runtime_id"`
|
|
Date pgtype.Date `json:"date"`
|
|
Provider string `json:"provider"`
|
|
Model string `json:"model"`
|
|
InputTokens int64 `json:"input_tokens"`
|
|
OutputTokens int64 `json:"output_tokens"`
|
|
CacheReadTokens int64 `json:"cache_read_tokens"`
|
|
CacheWriteTokens int64 `json:"cache_write_tokens"`
|
|
}
|
|
|
|
func (q *Queries) UpsertRuntimeUsage(ctx context.Context, arg UpsertRuntimeUsageParams) error {
|
|
_, err := q.db.Exec(ctx, upsertRuntimeUsage,
|
|
arg.RuntimeID,
|
|
arg.Date,
|
|
arg.Provider,
|
|
arg.Model,
|
|
arg.InputTokens,
|
|
arg.OutputTokens,
|
|
arg.CacheReadTokens,
|
|
arg.CacheWriteTokens,
|
|
)
|
|
return err
|
|
}
|