-
Notifications
You must be signed in to change notification settings - Fork 280
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 --strict flag #441
Add --strict flag #441
Conversation
This code change looks good to me. From a UX perspective I find It's also wort noting that my PR #438 would allow you to select a range of migrations, such as the range that doesn't include some older migrations- so a slightly more manual version of this. For me it seems like it would work because this use case wouldn't come up a lot. |
@amacneil proposed the name I also do not see |
Definitely not in conflict. We don't have to bikeshed about the name- it's fine as is- but just thought I would throw a perspective out there. |
My linked comment was 3 years ago, but I think probably just I think this should be a flag on the verb, rather than a global flag. It only applies to the |
Limits the ´--strict-order´ flag to the up, migrate and status verb.
main.go
Outdated
@@ -164,6 +176,11 @@ func NewApp() *cli.App { | |||
Name: "status", | |||
Usage: "List applied and pending migrations", | |||
Flags: []cli.Flag{ | |||
&cli.BoolFlag{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a precedent for duplication with the verbose flag, but it seems like it would be better to define the flag just once
strictFlag := cli.BoolFlag{...}
...
flags: []cli.Flag{&strictFlag}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could really tidy up the duplicate flag usage. I was already a little annoyed writing that part but I tried to do it similar to the verbose flag. I will change it 👍
I think there would be some benefit to instead of just ignoring migrations to have another state in the UI to show that migrations were cutoff due to the strict setting. Users may set this as an env variable and then get confused as to why migrations aren't showing up. That being said, users that set this will probably be more advanced and I don't have an answer as to what exactly to show users (just that there is a strict cutoff, or a different kind of marking instead of applied/unapplied). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good to me.
For the record, I would never expect something called "strict" to ever ignore anything. "--ignore-outdated" may be more verbose but I would feel more confident guessing exactly how the expected behavior of that option would modify execution, vs. "strict" which I would expect would result in a fatal error if there were unapplied migrations or anything else that would not pass a "strict" test.
But, if the consensus is that "strict" is unambiguous and the implemented behavior matches the expectation of others aside from me, then I'm okay with it.
The more I think about the flag name the more I am bothered by it. @gregwebs and @dossy raise good points about the unambiguousness of the flag, both on the user level and in the code itself too. A more verbose name would definitely not hurt and would make a condition like |
Actually, I didn't fully comprehend the PR description, but I was expecting this behavior too:
Would that be a better solution, rather than simply ignoring out of order migrations? If any migrations have a lower version number than the current "latest" version, then throw an error and exit 1? Combined with something like #434 the user could then resolve the issue by manually applying migrations. |
Is this strict feature planned to be merged anytime soon? We are eager to use it once available. |
I think it would be great if when this is merged, a |
Sounds reasonable. I've made the appropriate changes. Strict mode throws if a pending migration is not strictly in numerical order with already applied migrations. Migrations with the same version number are not yet properly checked though. Dbmate only saves the version number of applied migrations and not the full filename. Therefore we cannot properly compare pending migrations with already applied migrations. |
In my opinion, version numbers should be unique, and two files with the same version number should raise an error and halt. This change would break backwards-compatibility, unfortunately. What do others think? |
I think two files with the same version number should be considered an error always. We could introduce it behind a different flag and enable it by default in the next major version bump. However, I think this is an unrelated problem to the current PR. |
pkg/dbmate/db.go
Outdated
@@ -441,6 +453,10 @@ func (db *DB) FindMigrations() ([]Migration, error) { | |||
migration.Applied = true | |||
} | |||
|
|||
if db.Strict && !migration.Applied && migration.Version < latestAppliedMigration { | |||
return nil, fmt.Errorf("cannot apply migration `%s` after `%s` in --strict mode, migrations have to be in numerical order", migration.Version, latestAppliedMigration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to my other comment, I think this error message could be a bit more helpful. we can mirror whatever words are selected for the flag description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the error message to the following:
migration `%s` is out of order with already applied migrations, the version number has to be higher than the applied migration `%s` in --strict mode
I'm 👍 on the current functionality of this PR, after we resolve my comment about the words used to describe the feature and error message. |
pkg/dbmate/db.go
Outdated
@@ -441,6 +453,10 @@ func (db *DB) FindMigrations() ([]Migration, error) { | |||
migration.Applied = true | |||
} | |||
|
|||
if db.Strict && !migration.Applied && migration.Version < latestAppliedMigration { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the right place for this check to live. FindMigrations()
should not fail regardless of what migrations have or haven't been applied.
The check should be implemented in the Migrate()
function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without an additional db query the check needs access to a list of applied migrations to find the applied migration with the highest version number. FindMigrations()
already queries those to check if a migration is already applied. There are several ways to do the check outside of FindMigrations()
but I did not want to make too many unrelated changes which might be in conflict with other PRs, e.g. changing the return values of FindMigrations()
would be in conflict with PR #438.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put the strict version check in Migrate()
and applied some small changes. FindMigrations()
already returns a sorted list of migrations so we only need to check the first pendig migration with the highest version number from all applied migrations.
FindMigrations returns a sorted list so we only need to check the first pending migration.
A return error is not in line with tests.
@amacneil my last changes are from 3 months ago without any feedback/review so far, is there anything else open about this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks good to me now, the only thing left is to add a few new test cases to pkg/dbmate/db_test.go
:
- while strict=false, allows applying migration out of order, the expected behavior before this change was introduced
- while strict=true, allows applying migration in order when no previous migrations have been applied yet
- while strict=true, allows applying migration in order when at least one previous migration has been applied and the new migration is in the correct order
- while strict=true, raises an error when trying to apply a migration out of order
If you need help implementing the tests, let me know and I can help.
@dossy I've added 2 tests which should cover the 4 cases. Can you please check them? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code reviewed, tests passing: unless someone objects, I think this is good to merge now.
@amacneil If there's something still unresolved that you'd like to see changed in this PR, could you add a note here letting us know? Otherwise, I'd like to clear your change requested bit on this PR and merge it. |
Concerns have been addressed, PR is otherwise ready to merge.
This adds the
--strict-order
flag which ignores all out of order pending migrations. With the flag setdbmate up
anddbmate migrate
only apply pending migrations which succeed the latest applied migration - in other words, only pending migrations with a higher version number than the latest applied migration will be used.dbmate --strict-order status
also only lists in order pending migrations. Rollbacks work as usual.Refs: #159