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

[schema][test] Add a data migration validation test #4783

Merged
merged 3 commits into from
Jan 9, 2024
Merged

Conversation

smklein
Copy link
Collaborator

@smklein smklein commented Jan 9, 2024

Adds a schema test with "before" / "after" hooks, and adds an example specifically for the "23.0.0" migration.

My intent is that this can be used for any other schema migrations that would like to execute arbitrary SQL checks
against the new schema too.

Fixes #4747

Comment on lines +167 to +171
#[derive(PartialEq, Clone, Debug)]
struct SqlEnum {
name: String,
variant: String,
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added some types and type conversions here so that I could easily read rows from the database in the "checks after migration" step.

This is necessary because we're using tokio_postgres instead of Diesel to access the DB here. More on that below.

// TODO: This isn't exhaustive, feel free to add more.
//
// These should only be necessary for rows where the database schema changes also choose to
// populate data.
}

impl From<bool> for AnySqlType {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These From impls are so that I can pass anything that Into<AnySqlType> when checking the values of columns from rows pulled out of the DB.

@@ -218,31 +269,53 @@ impl<'a> tokio_postgres::types::FromSql<'a> for AnySqlType {
if f32::accepts(ty) {
return Ok(AnySqlType::Float4(f32::from_sql(ty, raw)?));
}
if serde_json::value::Value::accepts(ty) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Urgh. So I feel like I'm basically implementing OID checking here, which I'd rather do via a match statement? But reading https://docs.rs/tokio-postgres/0.7.10/tokio_postgres/types/index.html , I'm struggling to find a way to match on the Type type, rather than have this chain of if-statements.

Frustratingly, this info does exist in the postgres_types crate: https://docs.rs/postgres-types/0.2.6/src/postgres_types/type_gen.rs.html#207 but it isn't exposed publically.

Comment on lines +285 to +290
Kind::Enum(_) => {
Ok(AnySqlType::Enum(SqlEnum {
name: ty.name().to_string(),
variant: std::str::from_utf8(raw)?.to_string(),
}))
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the laziest possible de-serialization of a user-defined type possible.

"If it's a user-defined type, just throw it into some strings, and do no further checks".

The tests below can perform this validation if they want it -- we don't need compile-time checks on old versions of the DB.

struct NamedSqlValue {
// It's a little redunant to include the column name alongside each value,
// but it results in a prettier diff.
struct ColumnValue {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Only tangentially related to this change, but IMO makes the usage a little more clear.

nexus/tests/integration_tests/schema.rs Outdated Show resolved Hide resolved
Comment on lines +983 to +986
ColumnValue::new("ip_pool_id", POOL1),
ColumnValue::new("resource_type", type_silo.clone()),
ColumnValue::new("resource_id", SILO1),
ColumnValue::new("is_default", true),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note that by using weakly-typed access to SQL here, this will continue to work, even if the "new version" of the SQL table changes (Same above during the insertions -- those will be operating on the 23.0.0 version, even if we later upgrade to 30.0.0 and keep changing all these tables).

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.

This is great. Thank you Sean. It's clarifying to see how this all fits together and that I would indeed just use sql directly from the client as I started to play with yesterday.

nexus/tests/integration_tests/schema.rs Outdated Show resolved Hide resolved
@smklein
Copy link
Collaborator Author

smklein commented Jan 9, 2024

I have high confidence the failures here are due to #4779 , going to merge even though we're seeing limited timeouts.

@smklein smklein merged commit e4522e5 into main Jan 9, 2024
19 of 20 checks passed
@smklein smklein deleted the validate-migration branch January 9, 2024 22:36
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.

Want a way to test data migrations
2 participants