-
Notifications
You must be signed in to change notification settings - Fork 0
perf(clickhouse): prepare task event attribute inserts #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: qa/agent-triggerdotdev-trigger-dev/pr-09-4860/base
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import { clickhouseTest } from "@internal/testcontainers"; | ||
| import { z } from "zod"; | ||
| import { ClickHouse } from "./index.js"; | ||
|
|
||
| function clickhouseDate(value: Date) { | ||
| return value.toISOString().replace("T", " ").replace("Z", ""); | ||
| } | ||
|
|
||
| describe("task events v2", () => { | ||
| clickhouseTest( | ||
| "stores materialized attributes with explicit insert columns", | ||
| async ({ clickhouseContainer }) => { | ||
| const ch = new ClickHouse({ url: clickhouseContainer.getConnectionUrl(), name: "test" }); | ||
| const startTime = new Date("2026-09-01T10:00:00.000Z"); | ||
| const expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000); | ||
| const spanId = "span_ephemeral_attributes"; | ||
|
|
||
| const [insertError] = await ch.taskEventsV2.insert([ | ||
| { | ||
| environment_id: "env_ephemeral_attributes", | ||
| organization_id: "org_ephemeral_attributes", | ||
| project_id: "project_ephemeral_attributes", | ||
| task_identifier: "ephemeral-attributes", | ||
| run_id: "run_ephemeral_attributes", | ||
| start_time: clickhouseDate(startTime), | ||
| duration: "1000000", | ||
| trace_id: "trace_ephemeral_attributes", | ||
| span_id: spanId, | ||
| parent_span_id: "", | ||
| message: "Ephemeral attributes", | ||
| kind: "SPAN", | ||
| status: "OK", | ||
| attributes: { | ||
| z: 1, | ||
| a: "hello", | ||
| nested: { enabled: true }, | ||
| }, | ||
| metadata: "{}", | ||
| expires_at: clickhouseDate(expiresAt), | ||
| }, | ||
| ]); | ||
| expect(insertError).toBeNull(); | ||
|
|
||
| const readAttributes = ch.reader.query({ | ||
| name: "read-ephemeral-task-event-attributes", | ||
| query: `SELECT attributes_text, | ||
| toUInt8(inserted_at > toDateTime64('2020-01-01 00:00:00', 3)) AS has_inserted_at | ||
| FROM trigger_dev.task_events_v2 | ||
| WHERE environment_id = {environmentId: String} | ||
| AND span_id = {spanId: String}`, | ||
| params: z.object({ environmentId: z.string(), spanId: z.string() }), | ||
| schema: z.object({ attributes_text: z.string(), has_inserted_at: z.number() }), | ||
| }); | ||
| const [readError, rows] = await readAttributes({ | ||
| environmentId: "env_ephemeral_attributes", | ||
| spanId, | ||
| }); | ||
| expect(readError).toBeNull(); | ||
| expect(rows).toEqual([ | ||
| { | ||
| attributes_text: '{"a":"hello","nested":{"enabled":true},"z":1}', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · HIGH The test asserts an exact JSON string for attributes_text, but JavaScript object key ordering is not guaranteed. Impact: The test asserts an exact JSON string for attributes_text, but JavaScript object key ordering is not guaranteed. The input object { z: 1, a: 'hello', nested: { enabled: true } } may serialize in a different order across runtimes or V8 versions, causing intermittent CI failures. The test should parse attributes_text and compare objects. Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · MEDIUM The test expects attributes_text to be '{"a":"hello","nested":{"enabled":true},"z":1}', but the input attributes object is { z: 1, a: "hello", nested: { enabled: true } }. Impact: The test expects attributes_text to be '{"a":"hello","nested":{"enabled":true},"z":1}', but the input attributes object is { z: 1, a: "hello", nested: { enabled: true } }. JSON object key ordering is not guaranteed by the JavaScript runtime, so this assertion can fail intermittently depending on insertion order. The test should parse the JSON and compare objects rather than asserting an exact string. Suggested fix: Fix the review finding before release. |
||
| has_inserted_at: 1, | ||
| }, | ||
| ]); | ||
| } | ||
| ); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,6 +176,27 @@ export function getSpanDetailsQueryBuilder(ch: ClickhouseReader, settings?: Clic | |
| // V2 Table Functions (partitioned by inserted_at instead of start_time) | ||
| // ============================================================================ | ||
|
|
||
| const TASK_EVENT_V2_INSERT_COLUMNS = [ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shipwright · HIGH The new TASK_EVENT_V2_INSERT_COLUMNS constant is defined as a bare array with a satisfies tuple constraint, but there is no comment explaining why explicit columns are required or Impact: The new TASK_EVENT_V2_INSERT_COLUMNS constant is defined as a bare array with a satisfies tuple constraint, but there is no comment explaining why explicit columns are required or what happens if the table schema changes. A future maintainer adding a column to TaskEventV2Input may not realize they must also update this list, causing silent data loss or insert failures. Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright. |
||
| "environment_id", | ||
| "organization_id", | ||
| "project_id", | ||
| "task_identifier", | ||
| "run_id", | ||
| "start_time", | ||
| "duration", | ||
| "trace_id", | ||
| "span_id", | ||
| "parent_span_id", | ||
| "message", | ||
| "kind", | ||
| "status", | ||
| "attributes", | ||
| "metadata", | ||
| "expires_at", | ||
| "machine_id", | ||
| "inserted_at", | ||
| ] satisfies [string, ...string[]]; | ||
|
|
||
| export const TaskEventV2Input = z.object({ | ||
| environment_id: z.string(), | ||
| organization_id: z.string(), | ||
|
|
@@ -204,6 +225,7 @@ export function insertTaskEventsV2(ch: ClickhouseWriter, settings?: ClickHouseSe | |
| return ch.insertUnsafe<TaskEventV2Input>({ | ||
| name: "insertTaskEventsV2", | ||
| table: "trigger_dev.task_events_v2", | ||
| columns: TASK_EVENT_V2_INSERT_COLUMNS, | ||
| settings: { | ||
| enable_json_type: 1, | ||
| type_json_skip_duplicated_paths: 1, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shipwright · HIGH
The migration from ClickHouse map accessor syntax (attributes.gen_ai.response.model.:String) to JSONExtractString(attributes_text, ...) changes query semantics.
Impact: The migration from ClickHouse map accessor syntax (attributes.gen_ai.response.model.:String) to JSONExtractString(attributes_text, ...) changes query semantics. If attributes_text is not populated for older rows or is populated asynchronously, these queries may silently return empty results, causing the missing-model detection to skip spans and potentially under-bill or misreport LLM usage. No backfill or data migra…
Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.