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

[reconfigurator] Introduce IdMap and use it in BlueprintZonesConfig #7327

Merged
merged 11 commits into from
Jan 14, 2025

Conversation

jgallagher
Copy link
Contributor

@andrewjstone mentioned being bothered by our BTreeMap<Id, ValueWithAnId> maps on #7315:

The key to the map must always match the value in BlueprintZoneConfig. It's a shame a mismatch is now representable, and we can go about trying to change that if necessary. We can also just make it part of blippy for now. FWIW, this matches the pattern in BlueprintDatasetsConfig.

This has bugged me too, and adding a blippy check crossed my mind. But I think it would be better to make this just not possible, so this PR introduces a newtype wrapper around BTreeMap<T::Id, T> that enforces "key must be value's ID" via a mix of compile time API constraints (e.g., insert only takes the value and generates the key on its own) and runtime checks (e.g., when mutating a value stored in the map, it will panic if that mutation changes the ID of the value).

The new code is all in id_map.rs, and the rest of the changes are replacing the BlueprintZonesConfig map with it. If this looks reasonable to folks, followup PRs to change the other similar BTreeMaps to use IdMap should be straightforward.

{
"$ref": "#/components/schemas/IdMapBlueprintZoneConfig"
}
]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure why this changed, exactly. Does JsonSchema do something special for BTreeMap that it doesn't know how to do for IdMap?

I think this is semantically equivalent, though; allOf says to apply all of the inner subschemas, and since this subschema is the same as what the prior schema was, this is basically undoing an inlining?

Copy link
Contributor

@sunshowers sunshowers Jan 11, 2025

Choose a reason for hiding this comment

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

hmm, to me this looks like the same kind of semantically equivalent reference as all the newtype UUID kinds that show up.

I think if you want to avoid this you can use
https://graham.cool/schemars/implementing/#alwaysinlineschema-optional. Whether to avoid or not is your call -- having it is equivalent to a newtype in client-generated code I believe.

If you have it, though, it might be nice for clients to reuse the same IdMappable struct. Which actually suggests putting it in a crate on crates.io, similar to newtype-uuid.

... and now that I'm thinking about it, this seems like a really nice thing to have available more generally! I feel like this specific thing (wanting to store data by key, but also putting the key in the data) shows up repeatedly, and having a wrapper which enforces consistency seems extraordinarily useful! I know I'd have used it in a little hobby thing I was doing just a couple months ago.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Keeping the newtype seems fine for internal stuff just like for newtype-uuid, I think!

this seems like a really nice thing to have available more generally! I feel like this specific thing (wanting to store data by key, but also putting the key in the data) shows up repeatedly, and having a wrapper which enforces consistency seems extraordinarily useful!

Fair enough! I also need to split out and publish slog-inline-chain which is similarly small and externally useful. I'm inclined to merge this and open issues for splitting those out - seem reasonable?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah sounds great.

Copy link
Contributor

@andrewjstone andrewjstone left a comment

Choose a reason for hiding this comment

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

Looks great. Thanks for all the work here @jgallagher.

I have no idea about the JsonSchema question. Maybe @ahl does.

@@ -794,7 +795,7 @@ impl DataStore {
e.to_string()
))
})?;
sled_zones.zones.insert(zone.id, zone);
sled_zones.zones.insert(zone);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

Self { original_id: borrowed.id(), borrowed: Some(borrowed) }
}

pub fn into_ref(mut self) -> &'a T {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice closing of this loophole too!

@jgallagher jgallagher merged commit 4b787d9 into main Jan 14, 2025
17 checks passed
@jgallagher jgallagher deleted the john/blueprint-id-map branch January 14, 2025 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants