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

Add custom migration implementation #2878

Merged
merged 4 commits into from
Mar 11, 2022

Conversation

matthewmcgarvey
Copy link
Contributor

@matthewmcgarvey matthewmcgarvey commented Feb 9, 2022

Fixes #2620

Instead of adding a migration library that is unmaintained, restrictive, or we'd have to tweak anyways, why not make our own? As you can see it really doesn't take that much code to get a minimal version working.

This implementation has things to point out:

  • Migrations are versioned, that version is provided inside the class (filename is ignored but useful for file ordering, could be changed in the future)
  • Migrations are run in the order of the versions, but ALL unapplied migrations are run. So if version 8 has run, but version 6 hasn't it will still be applied the next time migrations are run. This is useful if you have a migration you always want to run last but add migrations after that one. It can be version 9999 but not force later migrations to be 10000.
  • No rollback capability, maybe the concept is useful for local dev but it can be added later if we need it
  • No CLI
  • No ability to create or drop the database
  • Each individual migration is run within a transaction so if any of the sql statements fail, the migration will not bel applied

Feel free to push back on this, my feelings won't be hurt 😄

src/invidious/migrations/0007_create_playlists_table.cr Outdated Show resolved Hide resolved
@@ -0,0 +1,38 @@
abstract class Invidious::Migration
macro inherited
Invidious::Migrator.migrations << self
Copy link
Contributor Author

Choose a reason for hiding this comment

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

migrations are automatically added to this list when the file is required which is why there's no need to do anything with migrations after they're defined.

Comment on lines 24 to 28
@db.transaction do |txn|
up(txn.connection)
track(txn.connection)
@completed = true
end
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 is the core code that runs the migration

@matthewmcgarvey matthewmcgarvey marked this pull request as ready for review February 9, 2022 07:09
@matthewmcgarvey matthewmcgarvey requested a review from a team as a code owner February 9, 2022 07:09
@matthewmcgarvey matthewmcgarvey requested review from SamantazFox and removed request for a team February 9, 2022 07:09
@unixfox
Copy link
Member

unixfox commented Feb 9, 2022

src/invidious/migrator.cr Outdated Show resolved Hide resolved
src/invidious/migrator.cr Outdated Show resolved Hide resolved
src/invidious/migrator.cr Outdated Show resolved Hide resolved
src/invidious/migrations/0007_create_playlists_table.cr Outdated Show resolved Hide resolved
src/invidious/migration.cr Outdated Show resolved Hide resolved
src/invidious/migrations/0000_create_channels_table.cr Outdated Show resolved Hide resolved

def up(conn : DB::Connection)
conn.exec <<-SQL
CREATE UNLOGGED TABLE IF NOT EXISTS public.videos
Copy link
Member

Choose a reason for hiding this comment

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

Given that UNLOGGED was a recent addition by @unixfox, most tables on the different instances haven't been altered. It would be preferrable to have a migration for that (ALTER TABLE ...).

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 left this as it was and made a new migration altering the table

src/invidious/migrations/0007_create_playlists_table.cr Outdated Show resolved Hide resolved
src/invidious.cr Outdated
@@ -111,6 +112,8 @@ end
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level)

# Run migrations
Invidious::Database::Migrator.new(PG_DB).migrate
Copy link
Contributor Author

Choose a reason for hiding this comment

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

One thing to keep in mind with this right now is... if someone is deploying multiple instances of invidious at the same time, this could cause issues. I think we could have this code lock the migrations table so one instance will claim the migration rollout but I'm not sure if that will work. I've not personally seen it done that way. Typically migrations are run before the deploy. We could move this line to a separate file that people could run before deploying if they want? Is it even necessary?

Copy link
Member

Choose a reason for hiding this comment

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

We could do the following:

  • Check for migrations
  • If migrations have to be run, exit with an informational message (and a specific error code)
  • If --migrate-db is passed, run migrations

That way, it's at the discretion of the instance owner to do as they want: if invidious exits with specified error code, they can hook up a wrapper script to stop all other instances, run a backup, and then migrate.

Copy link
Member

Choose a reason for hiding this comment

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

Mmmh this definitively need to be tackled properly. I'm running 10 instances of Invidious at the same time, so I don't want this to become an issue haha.

SQL
end

private def privacy_type_exists?(conn : DB::Connection) : Bool
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead of the weird pgsql query, I extracted out this check to look for the privacy type and only creating it if this method returns false.

Copy link
Member

@SamantazFox SamantazFox left a comment

Choose a reason for hiding this comment

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

Looks good to me!

Comment on lines +105 to +108
parser.on("--migrate", "Run any migrations") do
Invidious::Database::Migrator.new(PG_DB).migrate
exit
end
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 was a good idea, only issue is that it really messes with my flow of using docker-compose

Copy link
Member

Choose a reason for hiding this comment

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

Oh, right. We should probably make a wrapper script for that?

Also, we probably shouldn't exit if migrations are run. Passing --migrate would always bump the DB no matter what, without breaking the execution process. And we could make that the default on Docker, so owners of small instances aren't too surprised.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I'm concerned that we're going to roll out a breaking change to everyone with this PR. Everyone will update as normal and then find out that they can't run the service and will have to update their command, and I'm not sure the best way to roll that out.

What I'm going to do is leave this as it is, and remove the pending migration check. That way we can merge this change, it won't break anyone, and people can test out the migrations process. Then we can make a follow-up PR with the implementation that requires the migrations.

Choose a reason for hiding this comment

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

I am struggling to run these CLI arguments, when I type sudo docker exec -it invidious-invidious-1 invidious -v I get

OCI runtime exec failed: exec failed: unable to start container process: exec: "invidious": executable file not found in $PATH: unknown

src/invidious.cr Outdated
# Run migrations
if Invidious::Database::Migrator.new(PG_DB).pending_migrations?
puts "There are pending migrations. Run `invidious --migrate` to apply the migrations."
exit 46
Copy link
Contributor Author

Choose a reason for hiding this comment

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

You said use a specific error code 😛. But for real... what error code should I return?

Copy link
Member

@SamantazFox SamantazFox Feb 12, 2022

Choose a reason for hiding this comment

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

I don't know xD I'll have to double check that, to ensure that we don't use something that either kemal or crystal already do. (Also I'd have expected that you'd go with 42 xD)

This is so that we don't break deploys with this PR.
Instead we only ship the 'invidious --migrate' cli command
and let people test that. Maybe even ship a new migration that wouldn't break
apps that don't run the migrations. Then we roll out the functionality
that requires migrations.
@unixfox
Copy link
Member

unixfox commented Feb 12, 2022

@SamantazFox @TheFrenchGhosty Please don't merge this PR without extensive testing. I would even consider asking some people to test before merging that to the public.
Don't merge before I've tested it too, I really don't want to break yewtu.be.

@unixfox unixfox added blocked require something else first need-testing This feature needs to be deployed and tested to see if it's working, and doesn't break something labels Feb 12, 2022
@SamantazFox
Copy link
Member

@unixfox Sure!

Comment on lines 119 to 123
# Run migrations
if Invidious::Database::Migrator.new(PG_DB).pending_migrations?
puts "There are pending migrations. Run `invidious --migrate` to apply the migrations."
exit 46
end
Copy link
Member

Choose a reason for hiding this comment

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

"This is so that we don't break deploys with this PR."

Well, when we'll make any DB changes (like #2469), the code won't correspond to the DB anymore, so invidious will still be broken until the migration is done.

Copy link
Member

Choose a reason for hiding this comment

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

As an in-between, we could keep the check + message, but not exit (so it's easier to trace why invidious is broken).

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 made this change because it seems like the rollout of requiring migrations to have run needs more thought which would could give more attention in a followup PR. By removing that, we can merge in the migration code without worrying about breaking users

@matthewmcgarvey
Copy link
Contributor Author

@SamantazFox Is there a blocker for this anymore now that it won't run migrations unless the CLI command is used? If this is merged we could talk further about how we want migrations to be done by end users

@SamantazFox
Copy link
Member

I don't see any. I was waiting on @unixfox approval, as per his previous message.

@unixfox
Copy link
Member

unixfox commented Mar 11, 2022

I'm not sure if I'll have time to try, you may merge anyway as it is not automatic yet.

@SamantazFox SamantazFox merged commit 55da1e3 into iv-org:master Mar 11, 2022
@TheFrenchGhosty
Copy link
Member

@SamantazFox Does this mean we can finally merge PRs that implement database changes? (after they're changed to work, obviously)

@unixfox
Copy link
Member

unixfox commented Mar 11, 2022

@SamantazFox Does this mean we can finally merge PRs that implement database changes? (after they're changed to work, obviously)

No because it's not automatic yet. Merging them will brake invidious.

@matthewmcgarvey matthewmcgarvey deleted the migrations branch March 11, 2022 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked require something else first need-testing This feature needs to be deployed and tested to see if it's working, and doesn't break something
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Discussion] Database migrations framework
5 participants