Commit d9a9621
[OPIK-7772] [BE] test: guard traces DDL across pre/post-cutover topologies in CI (#7951)
* [OPIK-7772] [BE] test: guard traces DDL across pre/post-cutover topologies in CI
The cutover to the partitioned, sharding-ready trace table is produced by the
operator runbook, not by Liquibase, so the changelog and the runtime topology
diverge the moment an install cuts over — and stay diverged for as long as the
fleet is mixed. A `traces` schema change must therefore be correct against two
physical layouts, and both failure modes are silent: post-cutover a shard-only
ADD COLUMN applies without error but is unreadable through the Distributed
wrapper, and a migration that alters `traces` but forgets the shadow leaves the
next cutover copying a table that no longer matches.
Adds the CI guard that turns both into merge-blocking failures:
* TracesSchemaParityPreCutoverTest applies the changelog the way a fresh install
does and asserts three-way parity — `traces`, the `traces_local_v2` shadow,
and the shipped cutover backfill's INSERT column list, which is read from the
reference SQL rather than restated so it cannot drift.
* TracesSchemaParityPostCutoverTest stops the changelog after the shadow-table
migration (000114), splices in the runbook's EXCHANGE + Distributed wrap, and
resumes, so every later migration runs against the live post-cutover layout.
It asserts the changelog applies with nothing left unrun, and that the wrapper
exposes exactly the shard's columns.
* Six negative tests inject the drift a careless migration would produce — a
column or skip index on one table alone, a preserved column missing from the
backfill list, a shard-only and a wrapper-only column — so no parity leg can
silently stop firing. The shard-only case also pins the unreadability itself,
and its counterpart pins the remedy, giving the "read-facing changes go to
both" rule an executable demonstration.
Parity compares the aspects a schema change moves (column sets, insertable
columns, skip indices, projections, sorting/primary keys) and enumerates the
shadow's deliberate extras; the baseline type/codec/partition differences stay
owned by TracesLocalV2TableTest, TracesLocalV2BenchmarkTest and
TracesLocalV2PartitioningTest.
No shipped migration is edited.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* [OPIK-7772] [BE] test: extend traces parity to select definitions; address review
Review feedback on the topology guard. Parity here means column names and types
and the select/expression definitions built on them — not the data, and not data
lifecycle.
Strengthened:
* Projections are compared by full definition rather than by name. Two
projections sharing a name but not a query would leave the successor keeping
the name and losing the meaning, which a name-only check cannot see.
* Post-cutover, columns are compared on their DEFAULT/MATERIALIZED expression as
well as type and default kind. The wrapper is created AS the shard, so it
starts an exact copy and any divergence is drift — a materialized column added
to each side with a different expression previously satisfied every name and
type assertion while computing something different on each.
* The backfill's INSERT column list is now an ordered list with duplicates
rejected, and is checked against its SELECT projection position by position.
ClickHouse pairs the two by position and not by name, so a column added to one
list and not the other sends every later value to the wrong destination column
— no syntax error, and both tables stay perfectly consistent with each other,
so no table-to-table comparison can see it. Each projection entry must name its
destination (bare column, or an `AS <column>` alias), which the shipped SQL
already does.
Three new negative tests, and each new assertion was verified to fail before
being committed green:
* same-named projections with different queries. Worth noting: both trace tables
are ReplacingMergeTree, which refuses ADD PROJECTION outright while
deduplicate_merge_projection_mode is at its default (code 344) — so a real
projection needs a deliberate per-table setting change. The test relaxes and
restores it to make the leg reachable.
* a materialized column whose expression differs between shard and wrapper.
* the positional select check was mutation-tested by transposing two adjacent
entries of the shipped SELECT projection, which it caught and every set-based
comparison passed.
Test hygiene, also from review:
* The two one-sided column-drift tests are one @ParameterizedTest over (table,
column); the flow was identical and only the target differed.
* The shard-only-column pair no longer hands mutated schema between two @ordered
tests. One test now owns the column from ADD to DROP, since the "after" half
only means anything on the state the "before" half leaves behind.
Deliberately not added: table TTL and storage policy. They are neither names,
types nor selects, the changelog sets neither on the trace tables, and the
tiered-storage policy is attached by an environment-gated migration outside this
changelog — so a guard over the changelog could not meaningfully assert it. The
scope boundary is now documented on TracesSchemaParity rather than left implicit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* [OPIK-7772] [BE] test: add column-type parity and pin the Distributed wrap target
Second round of review on the topology guard. Both accepted findings were right,
and the first corrects an overreach in my earlier reasoning.
**Column types are now compared pre-cutover.** I had excluded per-column types
wholesale on the grounds that the shadow deliberately differs — but that
conflated types with codecs and defaults. Measured against the real schemas, only
6 of 31 shared columns differ in type (start_time, created_at, end_time, ttft,
duration, id_at), so the exemption is a short, enumerable list rather than most
of the table. BASELINE_TYPE_DIFFERENCES names those six with the reason each one
differs, and every other shared column is now type-checked. That catches a
precision narrowed on one table only, or a String quietly becoming
LowCardinality(String) on one side — both of which the name-set comparisons pass.
The allowlist is held honest in both directions: an entry whose columns no longer
differ fails as a stale entry, so it cannot decay into a blanket exemption for a
column nothing checks.
**The Distributed wrap target is pinned.** isDistributed() only checked the engine
prefix, so a wrapper over a different cluster, database, shard table or sharding
key would expose the same column list and pass everything. The engine is now
asserted to front `traces_local` in the same database on '{cluster}' with
sipHash64(project_id) — which also keeps the spliced statements honest against the
shipped 000003_exchange_and_wrap.sql they mirror.
Two negative tests, both verified to fail before being committed green: a
one-sided MODIFY COLUMN to LowCardinality(String), and a stale allowlist entry
produced by making the shadow's ttft Nullable again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* [OPIK-7772] [BE] test: pin both sides of each allowlisted type difference
Review feedback. The allowlist excused six columns from type parity and then only
checked that their types still *differed*, so either side could drift to an
unrelated type — `traces.start_time` becoming `String` — while still "differing"
and so still being excused.
Each entry is now a BaselineTypeDifference pinning the expected type on both
`traces` and the shadow alongside the reason, and both are asserted. That closes
the drift and makes the allowlist self-documenting: the entry states exactly what
the difference is, not just that one exists. A converged pair still fails, so a
dead exemption cannot linger over a column nothing checks.
Two negative tests, one per side: the shadow's `ttft` made Nullable again (which
also removes the difference), and `traces.start_time` narrowed to DateTime64(3)
— the case a "they must differ" check would pass.
Also two Javadoc grammar fixes from the same review.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 684a1ce commit d9a9621
5 files changed
Lines changed: 1349 additions & 0 deletions
File tree
- apps/opik-backend/src/test/java/com/comet/opik
- api/resources/utils
- db
Lines changed: 80 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
| 6 | + | |
4 | 7 | | |
5 | 8 | | |
6 | 9 | | |
| |||
14 | 17 | | |
15 | 18 | | |
16 | 19 | | |
| 20 | + | |
17 | 21 | | |
18 | 22 | | |
19 | 23 | | |
| |||
48 | 52 | | |
49 | 53 | | |
50 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
51 | 131 | | |
52 | 132 | | |
53 | 133 | | |
| |||
Lines changed: 187 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
0 commit comments