perf(db): generate task message IDs as UUIDv7 (#7220)

Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
Multica Eve
2026-08-19 17:17:28 +08:00
committed by GitHub
co-authored by Sol-Boy multica-agent
parent f9b7bf52ea
commit ea07a29138
4 changed files with 56 additions and 5 deletions
+14 -1
View File
@@ -16,6 +16,7 @@ import (
"time"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/multica-ai/multica/server/internal/analytics"
@@ -4381,7 +4382,18 @@ func (h *Handler) ReportTaskMessages(w http.ResponseWriter, r *http.Request) {
}
}
for _, msg := range req.Messages {
messageIDs := make([]pgtype.UUID, len(req.Messages))
for i := range messageIDs {
id, err := uuid.NewV7()
if err != nil {
slog.Error("failed to generate task message id", "task_id", taskID, "error", err)
writeError(w, http.StatusInternalServerError, "failed to persist task message")
return
}
messageIDs[i] = pgtype.UUID{Bytes: [16]byte(id), Valid: true}
}
for i, msg := range req.Messages {
// Redact sensitive information before persisting or broadcasting.
msg.Content = redact.Text(msg.Content)
msg.Output = redact.Text(msg.Output)
@@ -4409,6 +4421,7 @@ func (h *Handler) ReportTaskMessages(w http.ResponseWriter, r *http.Request) {
inputJSON, _ = json.Marshal(msg.Input)
}
created, createErr := h.Queries.CreateTaskMessage(r.Context(), db.CreateTaskMessageParams{
ID: messageIDs[i],
TaskID: parseUUID(taskID),
Seq: int32(msg.Seq),
Type: msg.Type,
@@ -9,6 +9,7 @@ import (
"testing"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/multica-ai/multica/server/internal/util"
db "github.com/multica-ai/multica/server/pkg/db/generated"
)
@@ -198,6 +199,41 @@ func TestReportTaskMessagesCallbackWithNULSucceeds(t *testing.T) {
}
}
func TestReportTaskMessagesCreatesUUIDv7(t *testing.T) {
ctx := context.Background()
_, taskID := seedNULTask(t, "uuidv7-messages-agent")
w := httptest.NewRecorder()
req := daemonTaskRequest(t, "/api/daemon/tasks/"+taskID+"/messages", taskID, map[string]any{
"messages": []any{
map[string]any{
"seq": 1,
"type": "text",
"content": "UUIDv7 task message",
},
},
})
testHandler.ReportTaskMessages(w, req)
if w.Code != http.StatusOK {
t.Fatalf("ReportTaskMessages returned %d, want 200: %s", w.Code, w.Body.String())
}
var rawID string
if err := testPool.QueryRow(ctx, `
SELECT id::text
FROM task_message WHERE task_id = $1 AND seq = 1`, taskID).Scan(&rawID); err != nil {
t.Fatalf("read persisted task message id: %v", err)
}
messageID, err := uuid.Parse(rawID)
if err != nil {
t.Fatalf("parse task message id %q: %v", rawID, err)
}
if got := messageID.Version(); got != 7 {
t.Fatalf("task message UUID version = %d, want 7", got)
}
}
// TestPostgresRejectsUnsanitizedTaskPayloads is the control for both tests
// above: it proves the database really does refuse these payloads, so the
// handler tests are demonstrating a fix rather than a no-op. If PostgreSQL ever
+4 -2
View File
@@ -12,12 +12,13 @@ import (
)
const createTaskMessage = `-- name: CreateTaskMessage :one
INSERT INTO task_message (task_id, seq, type, tool, content, input, output)
VALUES ($1, $2, $3, $4, $5, $6, $7)
INSERT INTO task_message (id, task_id, seq, type, tool, content, input, output)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id, task_id, seq, type, tool, content, input, output, created_at
`
type CreateTaskMessageParams struct {
ID pgtype.UUID `json:"id"`
TaskID pgtype.UUID `json:"task_id"`
Seq int32 `json:"seq"`
Type string `json:"type"`
@@ -29,6 +30,7 @@ type CreateTaskMessageParams struct {
func (q *Queries) CreateTaskMessage(ctx context.Context, arg CreateTaskMessageParams) (TaskMessage, error) {
row := q.db.QueryRow(ctx, createTaskMessage,
arg.ID,
arg.TaskID,
arg.Seq,
arg.Type,
+2 -2
View File
@@ -1,6 +1,6 @@
-- name: CreateTaskMessage :one
INSERT INTO task_message (task_id, seq, type, tool, content, input, output)
VALUES ($1, $2, $3, $4, $5, $6, $7)
INSERT INTO task_message (id, task_id, seq, type, tool, content, input, output)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING *;
-- name: ListTaskMessages :many