mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-08 20:40:40 +01:00
refactor: drop evolution for list select
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
init-clickhouse:
|
||||
image: clickhouse/clickhouse-server:25.12.5
|
||||
image: clickhouse/clickhouse-server:26.4.3
|
||||
container_name: init-clickhouse
|
||||
command:
|
||||
- bash
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
volumes:
|
||||
- ${PWD}/fs/tmp/var/lib/clickhouse/user_scripts/:/var/lib/clickhouse/user_scripts/
|
||||
clickhouse:
|
||||
image: clickhouse/clickhouse-server:25.12.5
|
||||
image: clickhouse/clickhouse-server:26.4.3
|
||||
container_name: clickhouse
|
||||
volumes:
|
||||
- ${PWD}/fs/etc/clickhouse-server/config.d/config.xml:/etc/clickhouse-server/config.d/config.xml
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
)
|
||||
|
||||
// jsonAttrColRe matches the raw `attributes` JSON column in the SELECT list (a bare column, not
|
||||
// the `attributes_string`/`_number`/`_bool` maps).
|
||||
var jsonAttrColRe = regexp.MustCompile(`,\s*attributes\s*,`)
|
||||
// the `attributes_string`/`_number`/`_bool` maps), mid-list or as the last column before FROM.
|
||||
var jsonAttrColRe = regexp.MustCompile(`,\s*attributes\s*(,| FROM )`)
|
||||
|
||||
func newBulkTestBuilder(t *testing.T, releaseTime time.Time) (*traceQueryStatementBuilder, *telemetrytypestest.MockMetadataStore) {
|
||||
t.Helper()
|
||||
@@ -46,10 +46,11 @@ func newBulkTestBuilder(t *testing.T, releaseTime time.Time) (*traceQueryStateme
|
||||
return b, store
|
||||
}
|
||||
|
||||
// TestBulkAttributeColumnsAcrossWindows asserts the empty-selectFields ("all fields") list query
|
||||
// scans the attribute homes the column evolution resolves to for the window: the legacy maps before
|
||||
// the JSON rollout, the JSON column after it, and both across it.
|
||||
func TestBulkAttributeColumnsAcrossWindows(t *testing.T) {
|
||||
// TestListQuerySelectsAllAttributeHomes asserts the empty-selectFields ("all fields") list query
|
||||
// always scans every physical home of the attributes bag — the three legacy maps and the JSON
|
||||
// column — in any window and without consulting evolution metadata. consume.go merges them per
|
||||
// row, so a row's attributes surface whichever home they were written to.
|
||||
func TestListQuerySelectsAllAttributeHomes(t *testing.T) {
|
||||
releaseTime := time.Date(2025, 5, 22, 22, 0, 0, 0, time.UTC)
|
||||
rel := releaseTime.UnixMilli()
|
||||
day := int64(24 * time.Hour / time.Millisecond)
|
||||
@@ -57,15 +58,13 @@ func TestBulkAttributeColumnsAcrossWindows(t *testing.T) {
|
||||
b, _ := newBulkTestBuilder(t, releaseTime)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
startMs uint64
|
||||
endMs uint64
|
||||
wantJSON bool
|
||||
wantLegacyMap bool
|
||||
name string
|
||||
startMs uint64
|
||||
endMs uint64
|
||||
}{
|
||||
{"before rollout -> maps only", uint64(rel - 2*day), uint64(rel - day), false, true},
|
||||
{"after rollout -> json only", uint64(rel + day), uint64(rel + 2*day), true, false},
|
||||
{"straddle rollout -> both", uint64(rel - day), uint64(rel + day), true, true},
|
||||
{"before rollout", uint64(rel - 2*day), uint64(rel - day)},
|
||||
{"after rollout", uint64(rel + day), uint64(rel + 2*day)},
|
||||
{"straddling rollout", uint64(rel - day), uint64(rel + day)},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
@@ -79,12 +78,10 @@ func TestBulkAttributeColumnsAcrossWindows(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
selectList := stmt.Query[:strings.Index(stmt.Query, " FROM ")]
|
||||
|
||||
assert.Equal(t, tt.wantJSON, jsonAttrColRe.MatchString(stmt.Query),
|
||||
"json `attributes` column presence; select=%s", selectList)
|
||||
assert.Equal(t, tt.wantLegacyMap, strings.Contains(stmt.Query, "attributes_string"),
|
||||
"legacy map presence; select=%s", selectList)
|
||||
// resources_string stays a legacy map in every window (out of scope for this change).
|
||||
assert.Contains(t, stmt.Query, "resources_string")
|
||||
assert.Regexp(t, jsonAttrColRe, stmt.Query, "json `attributes` column; select=%s", selectList)
|
||||
for _, col := range []string{"attributes_string", "attributes_number", "attributes_bool", "resources_string"} {
|
||||
assert.Contains(t, stmt.Query, col, "select=%s", selectList)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,29 +352,6 @@ func lookupIntrinsicOrCalculatedField(name string) (telemetrytypes.TelemetryFiel
|
||||
}
|
||||
|
||||
// buildListQuery builds a query for list panel type.
|
||||
// bulkAttributeColumnNames returns the attribute columns the empty-selectFields list path scans:
|
||||
// the physical homes the attributes column-evolution resolves to for [start, end] (legacy maps,
|
||||
// the JSON column, or both across the rollout). resources_string is appended separately by the
|
||||
// caller and stays a legacy map until the resource column moves to JSON.
|
||||
func (b *traceQueryStatementBuilder) bulkAttributeColumnNames(ctx context.Context, start, end uint64) ([]string, error) {
|
||||
evolutions, err := b.metadataStore.GetColumnEvolutions(ctx, []*telemetrytypes.EvolutionSelector{{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
FieldContext: telemetrytypes.FieldContextAttribute,
|
||||
FieldName: telemetrytypes.EvolutionFieldNameAll,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cols, err := tracestelemetryschema.BulkAttributeColumns(evolutions, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, len(cols))
|
||||
for i, c := range cols {
|
||||
names[i] = c.Name
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (b *traceQueryStatementBuilder) buildListQuery(
|
||||
ctx context.Context,
|
||||
@@ -410,14 +387,18 @@ func (b *traceQueryStatementBuilder) buildListQuery(
|
||||
}
|
||||
|
||||
if isSelectFieldsEmpty {
|
||||
attrCols, err := b.bulkAttributeColumnNames(ctx, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, col := range attrCols {
|
||||
// The attributes bag is read whole: every physical home is selected
|
||||
// unconditionally — a row's attributes live in exactly one home (or both,
|
||||
// agreeing, during dual-write), and consume.go merges them per row with the
|
||||
// JSON column winning. No evolution lookup is needed for the bag (unlike
|
||||
// per-key reads, which pick typed homes per window for index/cost), and the
|
||||
// read stays correct for rows written by a maps-only exporter past the
|
||||
// rollout. The attributes JSON column exists since migration 1012, the same
|
||||
// assumption getColumn already makes for the resource/scope JSON columns.
|
||||
for _, col := range tracestelemetryschema.ContextualSpanColumns {
|
||||
sb.SelectMore(col)
|
||||
}
|
||||
sb.SelectMore(tracestelemetryschema.SpanResourcesStringColumn)
|
||||
sb.SelectMore(tracestelemetryschema.SpanAttributesColumn)
|
||||
}
|
||||
|
||||
// From table
|
||||
|
||||
@@ -2407,13 +2407,6 @@ func (t *telemetryMetaStore) fetchMeterSourceMetricsTemporalityAndType(ctx conte
|
||||
return temporalities, types, nil
|
||||
}
|
||||
|
||||
func (k *telemetryMetaStore) GetColumnEvolutions(ctx context.Context, selectors []*telemetrytypes.EvolutionSelector) ([]*telemetrytypes.EvolutionEntry, error) {
|
||||
if len(selectors) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return k.fetchEvolutionEntryFromClickHouse(ctx, selectors)
|
||||
}
|
||||
|
||||
func (k *telemetryMetaStore) fetchEvolutionEntryFromClickHouse(ctx context.Context, selectors []*telemetrytypes.EvolutionSelector) ([]*telemetrytypes.EvolutionEntry, error) {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("signal", "column_name", "column_type", "field_context", "field_name", "version", "release_time")
|
||||
|
||||
@@ -371,28 +371,6 @@ func (m *fieldMapper) resolveColumnExprs(
|
||||
return exprs, existExprs, columns, nil
|
||||
}
|
||||
|
||||
// BulkAttributeColumns returns the physical attribute columns a whole-bag (list-view) read must
|
||||
// scan over [startNs, endNs] given the attributes column-evolution entries: the three legacy maps
|
||||
// before the JSON rollout, the JSON column after it, and both across the rollout — the same
|
||||
// per-window home selection the per-key path uses, applied to the whole column. With no rollout
|
||||
// entry it stays on the legacy maps.
|
||||
func BulkAttributeColumns(evolutions []*telemetrytypes.EvolutionEntry, startNs, endNs uint64) ([]*schema.Column, error) {
|
||||
maps := []*schema.Column{
|
||||
indexV3Columns["attributes_string"],
|
||||
indexV3Columns["attributes_number"],
|
||||
indexV3Columns["attributes_bool"],
|
||||
}
|
||||
if len(evolutions) == 0 {
|
||||
return maps, nil
|
||||
}
|
||||
family := append([]*schema.Column{indexV3Columns["attributes"]}, maps...)
|
||||
cols, _, err := qbtypes.SelectEvolutionsForColumns(family, evolutions, startNs, endNs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cols, nil
|
||||
}
|
||||
|
||||
// attributeColumnEvolutionRegistered reports whether key carries an evolution entry for the given column.
|
||||
func attributeColumnEvolutionRegistered(key *telemetrytypes.TelemetryFieldKey, columnName string) bool {
|
||||
for _, e := range key.Evolutions {
|
||||
|
||||
@@ -4,10 +4,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// EvolutionFieldNameAll is the field_name of a column-wide (whole-signal-context) evolution
|
||||
// entry, as opposed to a per-field promotion entry.
|
||||
const EvolutionFieldNameAll = "__all__"
|
||||
|
||||
type EvolutionEntry struct {
|
||||
Signal Signal `json:"signal"`
|
||||
ColumnName string `json:"column_name"`
|
||||
|
||||
@@ -19,11 +19,6 @@ type MetadataStore interface {
|
||||
// GetKey returns a list of keys with the given name.
|
||||
GetKey(ctx context.Context, orgID valuer.UUID, fieldKeySelector *FieldKeySelector) ([]*TelemetryFieldKey, error)
|
||||
|
||||
// GetColumnEvolutions returns the column-evolution entries matching the selectors (including
|
||||
// __all__ family rows) without resolving concrete keys, so a whole-column read such as the
|
||||
// list-view attributes bag can pick its physical homes for the query window.
|
||||
GetColumnEvolutions(ctx context.Context, selectors []*EvolutionSelector) ([]*EvolutionEntry, error)
|
||||
|
||||
// GetRelatedValues returns a list of related values for the given key name
|
||||
// and the existing selection of keys.
|
||||
GetRelatedValues(ctx context.Context, orgID valuer.UUID, fieldValueSelector *FieldValueSelector) ([]string, bool, error)
|
||||
|
||||
@@ -163,14 +163,6 @@ func (m *MockMetadataStore) GetKey(ctx context.Context, _ valuer.UUID, fieldKeyS
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *MockMetadataStore) GetColumnEvolutions(_ context.Context, selectors []*telemetrytypes.EvolutionSelector) ([]*telemetrytypes.EvolutionEntry, error) {
|
||||
var evolutions []*telemetrytypes.EvolutionEntry
|
||||
for _, selector := range selectors {
|
||||
evolutions = append(evolutions, m.ColumnEvolutionMetadataMap[selector.QualifiedName()]...)
|
||||
}
|
||||
return evolutions, nil
|
||||
}
|
||||
|
||||
// GetRelatedValues returns a list of related values for the given key name and selection.
|
||||
func (m *MockMetadataStore) GetRelatedValues(ctx context.Context, _ valuer.UUID, fieldValueSelector *telemetrytypes.FieldValueSelector) ([]string, bool, error) {
|
||||
if fieldValueSelector == nil {
|
||||
|
||||
Reference in New Issue
Block a user