mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-08-25 07:28:39 +00:00
e778a72829
* feat(operator): pair calls edges across trace tables and derive virtual-node edges Union the normalized client and server spans of all trace tables before the join, so a client span pairs with a server span stored in a different table. A client span with no matching server span becomes an edge to a virtual node named by span attributes (peer.service / db.name / server.address), with confidence < 1.0 and attributes.connection_type; a window's real pairs win over virtual candidates for the same edge key. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * feat(operator): derive same-row co-declared edges from the built-in vocabulary A table declaring both entity types of a vocabulary pair witnesses the edge on every row carrying both identities: runs_on / contains / part_of for any declaring table (provenance 'attribute'), agent uses model / agent invoked tool only for trace sources (span-structure observations, provenance 'trace'). Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * feat(operator): derive parent_agent-calls-agent edges from span structure Trace tables declaring an agent entity pair each span with its child span across tables (no span-kind filter), keep pairs whose agent identities differ, and aggregate RED metrics per window, anchored on the parent span like the service derivation is anchored on the client. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * feat(frontend): feed co-declared and agent sources into the relationships scan scan_relationships now passes every declaring table (with its trace-ness) to the co-declared branch and the trace tables' agent declarations to the agent-calls derivation. enumerate validates the fixed trace-v1 columns and derives around a malformed trace table instead of failing the whole scan. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * test: cover cross-table pairing, virtual nodes, co-declared and agent edges sqlness exercises the new derivations end to end (including a malformed trace-model table being skipped); the integration authorization test now also pins that a pair split across tables derives no edge when the caller cannot read one side. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * chore: update the relationships module doc for the new branches Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * chore: import shared derivation helpers via crate paths The fmt CI gate rejects module-level 'use super::' imports. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix: fold co-declared duplicates, decouple agent calls, verify the trace time index Review findings: the co-declared branch lacked a cross-source DISTINCT, so two tables witnessing the same edge in one window emitted duplicate rows; the agent-calls derivation was gated on a usable service declaration; the trace schema guard accepted a table whose time index is not the column the derivations bucket by. The empty-trace-table test asserted a union invariant with no information and is dropped. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix: rename the agent-tool edge to invokes and track current OTel peer attributes The vocabulary's other relation names are present tense; semconv 1.39/1.26 replaced peer.service and db.name with service.peer.name and db.namespace, so the virtual-node candidates now check the current names first and keep the deprecated ones for existing telemetry. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix: trust the trace-v1 table option instead of matching the fixed schema The option is only ever stamped by the ingest path, which guarantees the fixed span columns; matching column types here couples the graph to every trace schema evolution (e.g. #8816) for a case that cannot occur. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> --------- Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
301 lines
12 KiB
SQL
301 lines
12 KiB
SQL
-- The computed entity-graph tables under greptime_private are read-only
|
|
-- virtual tables: readable, but rejecting every DDL/DML path.
|
|
select observed_at, entity_type, entity_id, scope from greptime_private.semantic_entities;
|
|
|
|
select observed_at, src_id, dst_id, rel_type from greptime_private.semantic_relationships;
|
|
|
|
insert into greptime_private.semantic_entities (observed_at, entity_type, entity_id) values (now(), 'service', 'svc-a');
|
|
|
|
-- Plain-literal VALUES takes the direct region-insert path, not the query
|
|
-- engine; the table-level guard must hold there too.
|
|
insert into greptime_private.semantic_entities (observed_at, entity_type, entity_id) values (0, 'service', 'svc-b');
|
|
|
|
create table greptime_private.semantic_entities (ts timestamp time index);
|
|
|
|
create table greptime_private.semantic_relationships (ts timestamp time index);
|
|
|
|
alter table greptime_private.semantic_entities add column extra string;
|
|
|
|
truncate table greptime_private.semantic_entities;
|
|
|
|
drop table greptime_private.semantic_entities;
|
|
|
|
drop table greptime_private.semantic_relationships;
|
|
|
|
-- Read-time derivation: declare entity identities on a user table and observe
|
|
-- them in the computed registry (single-column and composite ids, scope and
|
|
-- descriptive columns, JSON output columns).
|
|
create table graph_app_latency (
|
|
ts timestamp time index,
|
|
service_name string,
|
|
host string,
|
|
env string,
|
|
latency double,
|
|
primary key (service_name, host, env)
|
|
) with (
|
|
'greptime.semantic.entity.service.id' = 'service_name',
|
|
'greptime.semantic.entity.service.scope' = 'env',
|
|
'greptime.semantic.entity.process.id' = 'service_name,host',
|
|
'greptime.semantic.entity.process.descriptive' = 'env'
|
|
);
|
|
|
|
insert into graph_app_latency values (now(), 'cart', 'h1', 'us-east', 3.5);
|
|
|
|
-- SQLNESS PROTOCOL MYSQL
|
|
select entity_type, entity_id, entity_id_attrs, scope, descriptive, source_tables
|
|
from greptime_private.semantic_entities
|
|
order by entity_type, entity_id;
|
|
|
|
drop table graph_app_latency;
|
|
|
|
-- Renaming a physical table INTO a reserved computed-table name would let the
|
|
-- overlay shadow it, orphaning its data.
|
|
create table greptime_private.graph_rename_probe (ts timestamp time index);
|
|
|
|
alter table greptime_private.graph_rename_probe rename semantic_entities;
|
|
|
|
drop table greptime_private.graph_rename_probe;
|
|
|
|
-- Calls derivation: a trace-v1-model table with client/server span pairs.
|
|
-- Trace tables need no entity declaration: the service entities are implicit.
|
|
create table graph_traces (
|
|
"timestamp" timestamp(9) time index,
|
|
trace_id string,
|
|
span_id string,
|
|
parent_span_id string,
|
|
span_kind string,
|
|
span_status_code string,
|
|
service_name string,
|
|
duration_nano bigint unsigned,
|
|
primary key (service_name)
|
|
) with ('table_data_model' = 'greptime_trace_v1', 'append_mode' = 'true');
|
|
|
|
-- The epoch-timestamped pair falls outside the default one-hour derivation
|
|
-- window and must not appear in the results.
|
|
insert into graph_traces values
|
|
(now(), 't1', 'c1', NULL, 'SPAN_KIND_CLIENT', 'STATUS_CODE_UNSET', 'frontend', 0),
|
|
(now(), 't1', 's1', 'c1', 'SPAN_KIND_SERVER', 'STATUS_CODE_ERROR', 'cart', 1500000000),
|
|
(now(), 't2', 'c2', NULL, 'SPAN_KIND_CLIENT', 'STATUS_CODE_UNSET', 'frontend', 0),
|
|
(now(), 't2', 's2', 'c2', 'SPAN_KIND_SERVER', 'STATUS_CODE_UNSET', 'cart', 500000000),
|
|
(0, 't0', 'c0', NULL, 'SPAN_KIND_CLIENT', 'STATUS_CODE_UNSET', 'stale-src', 0),
|
|
(0, 't0', 's0', 'c0', 'SPAN_KIND_SERVER', 'STATUS_CODE_UNSET', 'stale-dst', 100);
|
|
|
|
-- SQLNESS PROTOCOL MYSQL
|
|
select src_type, src_id, dst_type, dst_id, rel_type, provenance, confidence,
|
|
request_count, error_count, duration_sum, duration_count, attributes
|
|
from greptime_private.semantic_relationships
|
|
order by src_id;
|
|
|
|
-- SQLNESS PROTOCOL MYSQL
|
|
select entity_type, entity_id, entity_id_attrs, scope, source_tables
|
|
from greptime_private.semantic_entities
|
|
order by entity_id;
|
|
|
|
drop table graph_traces;
|
|
|
|
-- Declared edges: the physical table is created by the system on the first
|
|
-- INSERT with its canonical schema. Re-asserting an edge stores a new revision;
|
|
-- reads keep only the latest one per edge key.
|
|
insert into greptime_private.semantic_relationships_declared
|
|
(observed_at, src_type, src_id, rel_type, dst_type, dst_id, provenance, scope, generation_id, confidence)
|
|
values (now() - interval '10 minute', 'service', 'frontend', 'depends_on', 'service', 'users-db', 'declared', '', '', 0.5);
|
|
|
|
insert into greptime_private.semantic_relationships_declared
|
|
(observed_at, src_type, src_id, rel_type, dst_type, dst_id, provenance, scope, generation_id, confidence)
|
|
values (now() - interval '5 minute', 'service', 'frontend', 'depends_on', 'service', 'users-db', 'declared', '', '', 1.0);
|
|
|
|
-- An old open-ended declaration stays valid until its row expires; an edge
|
|
-- retired in the past must not appear.
|
|
insert into greptime_private.semantic_relationships_declared
|
|
(observed_at, src_type, src_id, rel_type, dst_type, dst_id, provenance, scope, generation_id, valid_until)
|
|
values
|
|
('1970-01-01 00:00:01', 'service', 'legacy', 'depends_on', 'service', 'mainframe', 'declared', '', '', NULL),
|
|
('1970-01-01 00:00:01', 'service', 'retired', 'depends_on', 'service', 'oldsys', 'declared', '', '', '2000-01-01 00:00:00');
|
|
|
|
select src_id, dst_id, rel_type, provenance, confidence
|
|
from greptime_private.semantic_relationships
|
|
order by src_id;
|
|
|
|
-- An explicit observed_at window replaces the default last hour; the emitted
|
|
-- timestamps of declared edges are synthesized inside the queried window.
|
|
select observed_at, window_start, fresh_until, src_id, dst_id
|
|
from greptime_private.semantic_relationships
|
|
where observed_at >= '2001-01-01 00:00:00' and observed_at < '2001-01-02 00:00:00'
|
|
order by src_id;
|
|
|
|
-- A lower bound alone is fine (the upper bound defaults to now)...
|
|
select src_id, dst_id, provenance
|
|
from greptime_private.semantic_relationships
|
|
where observed_at >= now() - interval '30 minute'
|
|
order by src_id;
|
|
|
|
-- ...but an upper bound alone would scan unbounded history: explicit error.
|
|
select src_id from greptime_private.semantic_relationships
|
|
where observed_at < '2001-01-02 00:00:00';
|
|
|
|
-- The declared-edge table's definition is system-owned: user CREATE/ALTER are
|
|
-- rejected and a physical table cannot be renamed into the name, while plain
|
|
-- DML stays allowed.
|
|
create table greptime_private.semantic_relationships_declared (ts timestamp time index);
|
|
|
|
alter table greptime_private.semantic_relationships_declared add column extra string;
|
|
|
|
create table greptime_private.declared_rename_probe (ts timestamp time index);
|
|
|
|
alter table greptime_private.declared_rename_probe rename semantic_relationships_declared;
|
|
|
|
drop table greptime_private.declared_rename_probe;
|
|
|
|
delete from greptime_private.semantic_relationships_declared;
|
|
|
|
select src_id from greptime_private.semantic_relationships order by src_id;
|
|
|
|
-- DROP is allowed (nothing structural is lost: the next INSERT recreates the
|
|
-- canonical table) and cleans up after this test.
|
|
drop table greptime_private.semantic_relationships_declared;
|
|
|
|
insert into greptime_private.semantic_relationships_declared
|
|
(observed_at, src_type, src_id, rel_type, dst_type, dst_id, provenance, scope, generation_id)
|
|
values (now(), 'service', 'reborn', 'depends_on', 'service', 'db', 'declared', '', '');
|
|
|
|
select src_id from greptime_private.semantic_relationships order by src_id;
|
|
|
|
drop table greptime_private.semantic_relationships_declared;
|
|
|
|
-- Same-row co-declaration: a table declaring both entity types of a built-in
|
|
-- vocabulary pair witnesses the edge on every row carrying both identities;
|
|
-- the vocabulary fixes the direction.
|
|
create table graph_pod_metrics (
|
|
ts timestamp time index,
|
|
instance string,
|
|
host string,
|
|
"service" string,
|
|
cpu double,
|
|
primary key (instance, host, "service")
|
|
) with (
|
|
'greptime.semantic.entity.service.instance.id' = 'instance',
|
|
'greptime.semantic.entity.host.id' = 'host',
|
|
'greptime.semantic.entity.service.id' = 'service'
|
|
);
|
|
|
|
insert into graph_pod_metrics values (now(), 'cart-0', 'node-1', 'cart', 0.5);
|
|
|
|
select src_type, src_id, dst_type, dst_id, rel_type, provenance, confidence
|
|
from greptime_private.semantic_relationships
|
|
order by rel_type, src_id;
|
|
|
|
drop table graph_pod_metrics;
|
|
|
|
-- Cross-table calls pairing: the client span and its child server span land in
|
|
-- different trace tables and must still pair into one edge. A trace-model
|
|
-- table that lost the fixed span columns is skipped instead of failing the
|
|
-- scan.
|
|
create table graph_traces_a (
|
|
"timestamp" timestamp(9) time index,
|
|
trace_id string,
|
|
span_id string,
|
|
parent_span_id string,
|
|
span_kind string,
|
|
span_status_code string,
|
|
service_name string,
|
|
duration_nano bigint unsigned,
|
|
primary key (service_name)
|
|
) with ('table_data_model' = 'greptime_trace_v1', 'append_mode' = 'true');
|
|
|
|
create table graph_traces_b (
|
|
"timestamp" timestamp(9) time index,
|
|
trace_id string,
|
|
span_id string,
|
|
parent_span_id string,
|
|
span_kind string,
|
|
span_status_code string,
|
|
service_name string,
|
|
duration_nano bigint unsigned,
|
|
primary key (service_name)
|
|
) with ('table_data_model' = 'greptime_trace_v1', 'append_mode' = 'true');
|
|
|
|
create table graph_traces_malformed (
|
|
ts timestamp time index,
|
|
note string
|
|
) with ('table_data_model' = 'greptime_trace_v1');
|
|
|
|
insert into graph_traces_a values
|
|
(now(), 't1', 'c1', NULL, 'SPAN_KIND_CLIENT', 'STATUS_CODE_UNSET', 'frontend', 0);
|
|
|
|
insert into graph_traces_b values
|
|
(now(), 't1', 's1', 'c1', 'SPAN_KIND_SERVER', 'STATUS_CODE_ERROR', 'cart', 1500000000);
|
|
|
|
insert into graph_traces_malformed values (now(), 'not a trace');
|
|
|
|
select src_id, dst_id, rel_type, provenance, confidence, request_count, error_count
|
|
from greptime_private.semantic_relationships
|
|
order by src_id;
|
|
|
|
drop table graph_traces_a;
|
|
|
|
drop table graph_traces_b;
|
|
|
|
drop table graph_traces_malformed;
|
|
|
|
-- Virtual nodes: a client span with no matching server span names its peer
|
|
-- through span attributes; the edge carries confidence < 1.0 and the
|
|
-- connection type.
|
|
create table graph_traces_virtual (
|
|
"timestamp" timestamp(9) time index,
|
|
trace_id string,
|
|
span_id string,
|
|
parent_span_id string,
|
|
span_kind string,
|
|
span_status_code string,
|
|
service_name string,
|
|
duration_nano bigint unsigned,
|
|
"span_attributes.peer.service" string,
|
|
"span_attributes.db.name" string,
|
|
primary key (service_name)
|
|
) with ('table_data_model' = 'greptime_trace_v1', 'append_mode' = 'true');
|
|
|
|
insert into graph_traces_virtual values
|
|
(now(), 't1', 'c1', NULL, 'SPAN_KIND_CLIENT', 'STATUS_CODE_UNSET', 'frontend', 250000000, 'redis', NULL),
|
|
(now(), 't2', 'c2', NULL, 'SPAN_KIND_CLIENT', 'STATUS_CODE_UNSET', 'frontend', 100000000, NULL, 'orders-db');
|
|
|
|
-- SQLNESS PROTOCOL MYSQL
|
|
select src_id, dst_id, rel_type, confidence, request_count, attributes
|
|
from greptime_private.semantic_relationships
|
|
order by dst_id;
|
|
|
|
drop table graph_traces_virtual;
|
|
|
|
-- Agent edges: span structure derives parent_agent calls agent, and span rows
|
|
-- co-declaring agent+model / agent+tool witness uses / invokes.
|
|
create table graph_agent_traces (
|
|
"timestamp" timestamp(9) time index,
|
|
trace_id string,
|
|
span_id string,
|
|
parent_span_id string,
|
|
span_kind string,
|
|
span_status_code string,
|
|
service_name string,
|
|
duration_nano bigint unsigned,
|
|
agent_id string,
|
|
model_name string,
|
|
tool_name string,
|
|
primary key (service_name, agent_id, model_name, tool_name)
|
|
) with (
|
|
'table_data_model' = 'greptime_trace_v1',
|
|
'append_mode' = 'true',
|
|
'greptime.semantic.entity.agent.id' = 'agent_id',
|
|
'greptime.semantic.entity.model.id' = 'model_name',
|
|
'greptime.semantic.entity.tool.id' = 'tool_name'
|
|
);
|
|
|
|
insert into graph_agent_traces values
|
|
(now(), 't1', 'p1', NULL, 'SPAN_KIND_INTERNAL', 'STATUS_CODE_UNSET', 'app', 0, 'orchestrator', NULL, NULL),
|
|
(now(), 't1', 'a1', 'p1', 'SPAN_KIND_INTERNAL', 'STATUS_CODE_UNSET', 'app', 2000000000, 'researcher', 'gpt-5', NULL),
|
|
(now(), 't1', 'a2', 'a1', 'SPAN_KIND_INTERNAL', 'STATUS_CODE_UNSET', 'app', 500000000, 'researcher', NULL, 'web_search');
|
|
|
|
select src_type, src_id, dst_type, dst_id, rel_type, provenance
|
|
from greptime_private.semantic_relationships
|
|
order by rel_type, dst_id;
|
|
|
|
drop table graph_agent_traces;
|