Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Hey folks,
So this is a semi-large PR for functionality that was requested in #45. We ran into the exact same need -- our applications run in docker containers on an ephemeral set of nodes and perform in-app migrations. The sql-migrate lib is fantastic, but when used concurrently, you are likely to run into migration collisions. Someone mentioned that this should be handled at a different layer and while that can be done, we have little control over the ops side of things and adding "cluster awareness" to each different service seems .. hacky and difficult to scale (if we're talking about microservices).
#45 suggested to use vendor-specific lock implementations -- while probably doable (and "proper"), it would be a pretty large undertaking. I went with the model of using a db-based mutex instead (ie. a lock record in a
gorp_lock
table), that all "migrators" attempt to insert, but only one wins. This ensures that it should work across most db's, without using any vendor-specific lock code (except forsqlite
, more on that later).Implementation and notes:
ExecWithLock()
andExecMaxWithLock()
which will utilize a db mutex to ensure only one 'master' migrator is performing migrationssql-migrate
instances that were not able to acquire the lock go into a wait-state, periodically checking to see if the lock disappears.waitTime
is up - the 'wait state' migrators finish cleanly, with no errors (and0
completed migrations)waitTime
is up - the additionalsql-migrate
instances will error with aExceeded lock clearance wait time
message. You can then tune thewaitTime
accordingly (it's a param passed toExecWithLock()
)sqlite
due to concurrent access causingdatabase is locked
errors. It's totally possible that I just missed something obvious, but the easiest route seemed to be to just enable MySQL based integration tests, so I introduced another test suite (MySQLMigrateSuite
), which thoroughly tests the lock implementation.go test
called-enable-mysql
-disable-sqlite
(default: false); this was done to speed up testing (as the sqlite tests seemed to be pretty slow on my machine)With that said, please let me know if you've got any questions -- this functionality is really crucial for our team and I would much rather prefer running off of upstream than our little fork :-)
TIA!