Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

thema: Fix backwards compatibility checking bugs #193

Merged
merged 1 commit into from Aug 14, 2023

Conversation

sdboyer
Copy link
Contributor

@sdboyer sdboyer commented Aug 14, 2023

I'm not sure i've ever been so delighted to put up a PR as this one!

We had three cases where the way we were invoking and constructing arguments to cue.Value.Subsume() was not behaving as expected - removing required fields, removing optional fields, and changing a default value. Clearly, these are all significant cases that we should expect folks will routinely encounter.

All three of these are now fixed. The only remaining exception cases involve additional lineage invariants that Thema is adding - nothing related to backwards compatibility.

The entire fix here was tweaking two lines:

  • Checking the closed def (_#schema) instead of the original, typically open schema field
  • Getting rid of cue.Schema() and cue.Final() as options to Subsume(), because they weren't doing what we thought they did

Then all the backwards compatibility checks just started working, exactly like we'd originally hoped! Even for defaults, which i'd written off months ago as something that would require some core CUE changes.

In retrospect, this makes a lot of sense - subsumption over open structs is always going to be weird to check, because openness is effectively like a ..._ at the end of each struct, and _ subsumes everything.

We were trying to compensate for this by passing cue.Final() to Subsume(). I don't know why that never worked as expected, but openness/closedness is really weird and subtle, so it's not hard to imagine there being something quirky there, even without bugs. But the crispness of the _#schema field seems to have cut through that noise. I introduced it solely to give an internal-only handle that's always closed for Thema's Go runtime to work with as part of Validate(), but now it's paying even more dividends.

There's more edge case probing we should do here with additional test cases, but that's good for a follow-up.

All this said, the actual errors being emitted by the subsumption checks are quite confusing, more often than not. They're going to need even more love than validation errors. In the short term, it's more important that the checks are correct than that they're understandable - but we can't rest on that for long.

The entire fix here was:

- Checking a closed def (`_#schema`) instead of the original, typically
  open `schema` field
- Getting rid of cue.Schema() and cue.Final() as options to Subsume(),
  because they weren't doing what we thought they did

Then all the backwards compatibility checks just started working,
exactly like we'd originally hoped.
@sdboyer sdboyer added enhancement New feature or request invariants Involves the definition or enforcement of a key system invariant prio/high High priority readiness/minimum Requisite for achieving a minimum readiness labels Aug 14, 2023
@sdboyer sdboyer self-assigned this Aug 14, 2023
@sdboyer sdboyer requested review from spinillos, joanlopez and a team August 14, 2023 03:24
Comment on lines +49 to +51
field aunion not present in {aunion:*"foo" | "bar" | "baz"}:
/cue.mod/pkg/github.com/grafana/thema/lineage.cue:234:12
missing field "aunion"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error absolutely sucks - the aunion field itself isn't missing, just some parts of its value!

But, it's still unambiguously better to be in a world where we have a true negative with a confusing error than a false positive with no error.

@sdboyer
Copy link
Contributor Author

sdboyer commented Aug 14, 2023

@AgnesToulet i'm really curious if the basic approach here could resolve the problems you encountered with IsAppendOnly()

}]
-- out/bindfail --
schema 0.1 is not backwards compatible with schema 0.0:
field concreteCross not present in {concreteCross:"foo" | "bar" | 42,concreteString:"foo" | "bar" | "baz",crossKind3:string | >=-2147483648 & <=2147483647 & int | bytes,crossKind2:string | >=-2147483648 & <=2147483647 & int}:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clearly we'll be reusing some of what we wrote to improve Validate() errors here, too

@@ -29,4 +29,6 @@ lin: lenses: [{
}
}]
-- out/bindfail --
schema 0.1 must be backwards compatible with schema 0.0
schema 0.1 is not backwards compatible with schema 0.0:
field not allowed in closed struct: getsRemoved
Copy link
Contributor

@joanlopez joanlopez Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I guess we'll also need to pay attention when working on messages clarity, as closedness is probably something not explicitly managed by the schema author, thus even less familiar for a non-author user.

@joanlopez
Copy link
Contributor

There's more edge case probing we should do here with additional test cases, but that's good for a follow-up.

Could you list which edge cases do you have in mind? I'd be happy to help preparing such additional edge cases.

Copy link
Contributor

@joanlopez joanlopez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good! Indeed, I guess there's no much to review, other than the magic underlying the Subsume operation, and the different options, as you tried to describe.

Follow-up: more test cases & more correct and clear error messages.

@sdboyer sdboyer merged commit 0e55755 into main Aug 14, 2023
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request invariants Involves the definition or enforcement of a key system invariant prio/high High priority readiness/minimum Requisite for achieving a minimum readiness
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants