mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
MAIN HAD NOT COMPILED SINCE 2026-07-30. Commit 5e3210f759 rewrote tables.go in three telemetry packages and the sentrytypes Event struct without updating their consumers, while its message claimed the read paths 'build clean' — proven false with a detached worktree at clean HEAD. That is why the deployed backend is SHA-pinned and stale: CI could not build main at all. THE RENAME: /api/v1|v2|v3|v4/<rest> -> /v1/o11y/<rest> (290 replacements; the host already says api, so the path never does, and one live generation carries no version segments). 21 duplicate v1 registrations DELETED with their v2 twins kept — gorilla/mux is first-match, so after the collapse the v1 line would have shadowed v2 silently, with no error, forever: rules (6), dashboards (6), services/top_operations/entry_point_operations/ttl (5), features, query_range v3+v4 -> v5, metrics/dashboards. 16 orphaned handler funcs (430 lines) deleted. CONSUMERS MIGRATED to the one-way table names, never the reverse: tables.go's deletion of the versioned constants was deliberate (stated three times), and the new blocks are positional 1:1 rewrites of the old, so every resolution is a constant the same commit declared — SpanIndexV3->span, LogsV2->log, SamplesV4->metric, TimeseriesV4->series, UpdatedMetadata->descriptor, ambiguity on operation-vs-trace settled by tables.go's own inline comments. sentrytypes.Frame/Event.Frames/Event.Handled RECONSTRUCTED, not guessed: pinned independently by eventsql.go's projection, eventstore.go's scan targets and module.go's clampLine/InApp, and asserted literally by the pre-existing TestFramesRoundTrip. TraceSummary.Sample -> Message (event.error has no sample column). Event.Sample deleted: a field that always read "" while claiming to hold the occurrence JSON. PATH MANGLERS KILLED, both deployment shapes: mount.go rewriteExternalPath+ setPath deleted (it mapped /v1/o11y/services -> /api/services => 404 on the cloud-binary path — proven with a real test before deletion); server.go StripPrefix removed (it reduced /v1/o11y/services -> /services => the web catch-all answered index.html on the standalone path). versionless.go + versionless_test.go + both call sites deleted: its regex matched 0 of the registered routes and walked the router at every startup to register nothing — the second spelling of one path, gone with the mechanism that needed it. FRONTEND: prependBase '/api/v1/' -> '/v1/o11y/' (both baseURL branches), 17 /api/v1/assistant literals renamed (note: no assistant route exists in pkg/ — client-only surface, a real product gap, not papered over), 21 MSW mocks moved so they intercept the paths the app now requests. GATE, all executed: go build ./... green; go test ./... green (exit 0); 0 /api/v[0-9] references in pkg code (frontend's 5 are query-builder escaping fixtures); 0 duplicate (method,path) registrations; /v1/o11y/services survives both mount.go and server.go untouched. Co-authored-by: Hanzo Dev <[email protected]>
149 lines
7.8 KiB
Go
149 lines
7.8 KiB
Go
package telemetrymetadata
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
|
)
|
|
|
|
func TestExtractFieldKeysFromTblStatement(t *testing.T) {
|
|
|
|
var statement = `CREATE TABLE event.log
|
|
(
|
|
` + "`ts_bucket_start`" + ` UInt64 CODEC(DoubleDelta, LZ4),
|
|
` + "`resource_fingerprint`" + ` String CODEC(ZSTD(1)),
|
|
` + "`timestamp`" + ` UInt64 CODEC(DoubleDelta, LZ4),
|
|
` + "`observed_timestamp`" + ` UInt64 CODEC(DoubleDelta, LZ4),
|
|
` + "`id`" + ` String CODEC(ZSTD(1)),
|
|
` + "`trace_id`" + ` String CODEC(ZSTD(1)),
|
|
` + "`span_id`" + ` String CODEC(ZSTD(1)),
|
|
` + "`trace_flags`" + ` UInt32,
|
|
` + "`severity_text`" + ` LowCardinality(String) CODEC(ZSTD(1)),
|
|
` + "`severity_number`" + ` UInt8,
|
|
` + "`body`" + ` String CODEC(ZSTD(2)),
|
|
` + "`attributes_string`" + ` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
|
|
` + "`attributes_number`" + ` Map(LowCardinality(String), Float64) CODEC(ZSTD(1)),
|
|
` + "`attributes_bool`" + ` Map(LowCardinality(String), Bool) CODEC(ZSTD(1)),
|
|
` + "`resources_string`" + ` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
|
|
` + "`scope_name`" + ` String CODEC(ZSTD(1)),
|
|
` + "`scope_version`" + ` String CODEC(ZSTD(1)),
|
|
` + "`scope_string`" + ` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
|
|
` + "`attribute_number_input_size`" + ` Int64 DEFAULT attributes_number['input_size'] CODEC(ZSTD(1)),
|
|
` + "`attribute_number_input_size_exists`" + ` Bool DEFAULT if(mapContains(attributes_number, 'input_size') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`attribute_string_log$$iostream`" + ` String DEFAULT attributes_string['log.iostream'] CODEC(ZSTD(1)),
|
|
` + "`attribute_string_log$$iostream_exists`" + ` Bool DEFAULT if(mapContains(attributes_string, 'log.iostream') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`attribute_string_log$$file$$path`" + ` String DEFAULT attributes_string['log.file.path'] CODEC(ZSTD(1)),
|
|
` + "`attribute_string_log$$file$$path_exists`" + ` Bool DEFAULT if(mapContains(attributes_string, 'log.file.path') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$cluster$$name`" + ` String DEFAULT resources_string['k8s.cluster.name'] CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$cluster$$name_exists`" + ` Bool DEFAULT if(mapContains(resources_string, 'k8s.cluster.name') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$namespace$$name`" + ` String DEFAULT resources_string['k8s.namespace.name'] CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$namespace$$name_exists`" + ` Bool DEFAULT if(mapContains(resources_string, 'k8s.namespace.name') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$pod$$name`" + ` String DEFAULT resources_string['k8s.pod.name'] CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$pod$$name_exists`" + ` Bool DEFAULT if(mapContains(resources_string, 'k8s.pod.name') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$node$$name`" + ` String DEFAULT resources_string['k8s.node.name'] CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$node$$name_exists`" + ` Bool DEFAULT if(mapContains(resources_string, 'k8s.node.name') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$container$$name`" + ` String DEFAULT resources_string['k8s.container.name'] CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$container$$name_exists`" + ` Bool DEFAULT if(mapContains(resources_string, 'k8s.container.name') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$deployment$$name`" + ` String DEFAULT resources_string['k8s.deployment.name'] CODEC(ZSTD(1)),
|
|
` + "`resource_string_k8s$$deployment$$name_exists`" + ` Bool DEFAULT if(mapContains(resources_string, 'k8s.deployment.name') != 0, true, false) CODEC(ZSTD(1)),
|
|
` + "`attribute_string_processor`" + ` String DEFAULT attributes_string['processor'] CODEC(ZSTD(1)),
|
|
` + "`attribute_string_processor_exists`" + ` Bool DEFAULT if(mapContains(attributes_string, 'processor') != 0, true, false) CODEC(ZSTD(1)),
|
|
INDEX body_idx lower(body) TYPE ngrambf_v1(4, 60000, 5, 0) GRANULARITY 1,
|
|
INDEX id_minmax id TYPE minmax GRANULARITY 1,
|
|
INDEX severity_number_idx severity_number TYPE set(25) GRANULARITY 4,
|
|
INDEX severity_text_idx severity_text TYPE set(25) GRANULARITY 4,
|
|
INDEX trace_flags_idx trace_flags TYPE bloom_filter GRANULARITY 4,
|
|
INDEX scope_name_idx scope_name TYPE tokenbf_v1(10240, 3, 0) GRANULARITY 4,
|
|
INDEX ` + "`resource_string_k8s$$cluster$$name_idx`" + ` ` + "`resource_string_k8s$$cluster$$name`" + ` TYPE bloom_filter(0.01) GRANULARITY 64,
|
|
INDEX ` + "`resource_string_k8s$$namespace$$name_idx`" + ` ` + "`resource_string_k8s$$namespace$$name`" + ` TYPE bloom_filter(0.01) GRANULARITY 64,
|
|
INDEX ` + "`resource_string_k8s$$pod$$name_idx`" + ` ` + "`resource_string_k8s$$pod$$name`" + ` TYPE bloom_filter(0.01) GRANULARITY 64,
|
|
INDEX ` + "`resource_string_k8s$$node$$name_idx`" + ` ` + "`resource_string_k8s$$node$$name`" + ` TYPE bloom_filter(0.01) GRANULARITY 64,
|
|
INDEX ` + "`resource_string_k8s$$container$$name_idx`" + ` ` + "`resource_string_k8s$$container$$name`" + ` TYPE bloom_filter(0.01) GRANULARITY 64,
|
|
INDEX ` + "`resource_string_k8s$$deployment$$name_idx`" + ` ` + "`resource_string_k8s$$deployment$$name`" + ` TYPE bloom_filter(0.01) GRANULARITY 64,
|
|
INDEX attribute_string_processor_idx attribute_string_processor TYPE bloom_filter(0.01) GRANULARITY 64
|
|
)
|
|
ENGINE = ReplicatedMergeTree('/datastore/tables/{uuid}/{shard}', '{replica}')
|
|
PARTITION BY toDate(timestamp / 1000000000)
|
|
ORDER BY (ts_bucket_start, resource_fingerprint, severity_text, timestamp, id)
|
|
TTL toDateTime(timestamp / 1000000000) + toIntervalSecond(2592000)
|
|
SETTINGS ttl_only_drop_parts = 1, index_granularity = 8192`
|
|
|
|
keys, err := ExtractFieldKeysFromTblStatement(statement)
|
|
if err != nil {
|
|
t.Fatalf("failed to extract field keys from tbl statement: %v", err)
|
|
}
|
|
|
|
// some expected keys
|
|
expectedKeys := []*telemetrytypes.TelemetryFieldKey{
|
|
{
|
|
Name: "k8s.pod.name",
|
|
FieldContext: telemetrytypes.FieldContextResource,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "k8s.cluster.name",
|
|
FieldContext: telemetrytypes.FieldContextResource,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "k8s.namespace.name",
|
|
FieldContext: telemetrytypes.FieldContextResource,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "k8s.deployment.name",
|
|
FieldContext: telemetrytypes.FieldContextResource,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "k8s.node.name",
|
|
FieldContext: telemetrytypes.FieldContextResource,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "k8s.container.name",
|
|
FieldContext: telemetrytypes.FieldContextResource,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "processor",
|
|
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "input_size",
|
|
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
FieldDataType: telemetrytypes.FieldDataTypeNumber,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "log.iostream",
|
|
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
{
|
|
Name: "log.file.path",
|
|
FieldContext: telemetrytypes.FieldContextAttribute,
|
|
FieldDataType: telemetrytypes.FieldDataTypeString,
|
|
Materialized: true,
|
|
},
|
|
}
|
|
|
|
for _, key := range expectedKeys {
|
|
if !slices.ContainsFunc(keys, func(k *telemetrytypes.TelemetryFieldKey) bool {
|
|
return k.Name == key.Name && k.FieldContext == key.FieldContext && k.FieldDataType == key.FieldDataType && k.Materialized == key.Materialized
|
|
}) {
|
|
t.Errorf("expected key %v not found", key)
|
|
}
|
|
}
|
|
}
|