You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
avro: field aliases as a resolution rung — measured, and unsound as a default (crossed-alias counterexample) #104
Schema.Field.aliases() is Avro's own field-rename mechanism, and eo's main sources never read it. It looks like the obvious next rung after #98: an alias-aware nominalIndex resolves the aliased rename+swap that #98's residual documents as unreachable.
It was implemented and measured, and it is not safe as a default. Filing so the counterexample is on record before someone re-proposes it.
What works
Patching nominalIndex (AvroWalk.scala:541) to consult field.aliases() after the exact-name and normalised-name attempts does resolve the aliased residual — the case where a schema renames occurredAt→ts and seqNo→sequence, swaps their positions, and records the old names as aliases.
Why it is unsound as a default
An alias names a historical wire name, not a Scala field name. The two can be anti-correlated by an ordinary migration. The counterexample is a mislabel correction — someone discovers two columns were named backwards and fixes it, recording the old (wrong) names as aliases:
finalcaseclassP2Fix(a: Long, b: Long)
givenCodec[P2Fix] =Codec.record[P2Fix]("Fix", "eo.alias") { f =>
(
f("a_fixed", _.a, aliases =Seq("b")), // was called "b", wrongly
f("b_fixed", _.b, aliases =Seq("a")), // was called "a", wrongly
).mapN((x, y) =>P2Fix(x, y))
}
Case field a matches schema field b_fixedby alias and a_fixed by position and by normalised name. The alias rung believes the alias and mis-targets. Measured on the patched build: .field(_.a) returns Some(22) where the truth is 11.
That is a case #98 gets right today, silently broken by the new rung — the exact failure class #98 exists to eliminate.
All 212 avro tests stayed green with and without the patch, because no fixture covers crossed aliases. Any future attempt needs this as a pinned negative fixture first.
If it is ever revisited
Under the same all-or-nothing discipline #98 established: consult aliases only when the alias map and the name/position map agree, and abstain whenever they disagree. Disagreement is exactly the mislabel-correction signal, and abstaining there costs nothing — position already handles it.
Worth noting the alias rung is strictly dominated by the differential codec probe (#100), which resolves both the aliased and the un-aliased rename+swap without consulting schema history at all, and scored 7 true positives / 0 false positives / 1 false negative on the full DivergentCodecs + ResolutionResidualSpec matrix.
Activity
test(avro): nominal-resolution doctrine oracle — kills 12 mutants in …
test(avro): nominal-resolution doctrine oracle — kills 12 mutants in …
docs(avro): re-aim the doctrine spec's covers-block at post-#107 Avro…
test(avro): pin the nominal-resolution doctrine — 391→431 mutants kil…