Database migration initialization#90
Conversation
|
|
||
| const ( | ||
| // Hardcoded path where the db_migrations live | ||
| defaultMigrationsPath = "./pkg/sqlite/db_migrations" |
There was a problem hiding this comment.
doesn't this mean that migrations only work if you have the operator-registry repo check out locally?
There was a problem hiding this comment.
Yep, that is true. Regardless you at the very least need the migrations folder. For the initializer, this is currently only run today from inside a container image that has the relevant files copied into it. We could also add a flag to the initializer that takes the migration folder as input, but in either case those files will need to be pulled down.
We could also, in theory, save the files themselves as source code so they could be compiled as part of the binary and then written to a directory before being read. That would make this more portable if we found that significantly valuable.
There was a problem hiding this comment.
A quick google led me to this: https://github.com/gobuffalo/packr - if this looks reasonable to add to this PR quickly (say <1hr?) can you go ahead and do that? I think the UX is much better if we don't have to ship migration files around.
If it looks like it would take longer than that, we don't have to bother with it now, and we can revisit the idea in the future.
There was a problem hiding this comment.
@ecordell I don't really like this tool because it makes a bunch of assumptions about the underlying filesystem of your files and also requires that you wrap go build inside their packr command. I just messed around with https://github.com/go-bindata/go-bindata and it seems like it's a little bit better for our specific use case -- it just generates a .go file with compressed binary versions of the files. We still need to use a binary tool to generate them, but we should be able to just add something to the makefile to download and run it when we need to generate the files.
I'm going to update this PR in a little bit with a second commit with an implementation that includes that.
There was a problem hiding this comment.
Awesome, thanks for finding that!
3545430 to
bc4ce2a
Compare
| } | ||
|
|
||
| sqlLoader, err := sqlite.NewSQLLiteLoader(dbName) | ||
| sqlLoader, err := sqlite.NewSQLLiteLoader(dbName, "") |
There was a problem hiding this comment.
We use some different patterns in other places that I think could clean this up.
Either a builder with good defaults
// uses the compiled migrations
sqlite.NewSQLLiteLoader(dbName)
// for tests
sqlite.NewSQLLiteLoader(dbName).WithMigrator(*testMigrator)or the variadic option pattern
sqlite.NewSQLLiteLoader(options.WithDBName(dbName), options.WithMigrator(default))There was a problem hiding this comment.
Yep, this is a good point. I went with the trivial implementation instead of using a builder because the constructor currently has actual parsing logic that requires the migrator to exist since there is currently no distinction between calling NewSQLLiteLoader and actually initializing the database -- I was attempting to retain that step as an atomic function call instead of splitting it into several steps.
But using the option pattern is a really good idea, I'll try to make this more digestible for the default case that way.
| t.Run(tt.description, func(t *testing.T) { | ||
| db := fmt.Sprintf("%d.db", rand.Int()) | ||
| store, err := NewSQLLiteLoader(db) | ||
| store, err := NewSQLLiteLoader(db, "./db_migrations") |
There was a problem hiding this comment.
We should probably be testing the compiled migrations, right?
There was a problem hiding this comment.
We do further down in the file:
migrator, err := NewSQLLiteMigrator(store.db, "")
But actually testing the values of the default migrations would involve either keeping the latest version in a configuration file or actually parsing the version. At that point, we would be maintaining test code that provides no functionality except to execute tests: IMO that is unnecessary overhead and technical debt.
That's why I implemented all the value checking using a set of test migrations that can be statically defined and their values referenced directly in the tests, and the actual check of the real data is just "does this throw an error when parsed".
Do you feel like there is significant value to be gained from ensuring something I'm missing?
There was a problem hiding this comment.
Ah, just realized this is for load_test.go, not the new test file I added. Yeah in this case you are right this should be updated to just use the correct default initialization.
bc4ce2a to
3b57071
Compare
|
@ecordell Updated. ptal |
ecordell
left a comment
There was a problem hiding this comment.
Made a couple of bookkeeping comments but this looks good!
If you're tired of looking at migrations, I think we can go ahead and merge this once the remaining comments are addressed.
...but, there's still something that's slightly bugging me. The current approach uses a framework that assumes the inputs are files, so we use go-bindata to get around that.
But mostly, migrations are just a bit of sql that could very easily fit in a constant in our binary. Additionally, some migrations may require non-sql code to run as well (doing backfills, etc) and I don't see a great way to do that with this.
For the most part, all we need is:
- A table that tracks the current version
- Some abstraction (in go) around a migration that allows you to go
upordown(and most of those are a singlesql.Execute) - An ordered list of those abstractions to walk through
I went back to the migrate lib you pulled in and found this as well: golang-migrate/migrate#15
Which led me to this package:
https://github.com/fnproject/fn/tree/master/api/datastore/sql/migratex
It seems simple enough to pull in or to write something similar ourselves.
What do you think about this?
| go build $(MOD_FLAGS) $(extra_flags) -o $@ ./cmd/$(shell basename $@) | ||
|
|
||
| build: clean $(CMDS) | ||
| build: clean generate-migration-bundle $(CMDS) |
There was a problem hiding this comment.
I don't think we want this as part of the build command so that CI will catch when we don't correctly commit the generated migration bundle to git.
| clean: | ||
| @rm -rf ./bin | ||
|
|
||
| @rm -f ./pkg/sqlite/migrations.go |
There was a problem hiding this comment.
like the rest of our codegen, we probably want to go ahead and commit these artifacts.
| github.com/grpc-ecosystem/grpc-health-probe v0.2.1-0.20181220223928-2bf0a5b182db | ||
| github.com/imdario/mergo v0.3.7 // indirect | ||
| github.com/mattn/go-sqlite3 v1.10.0 | ||
| github.com/operator-framework/operator-lifecycle-manager v3.11.0+incompatible // indirect |
There was a problem hiding this comment.
Where did this come from? We recently did some work to remove the dependency on OLM.
(also this is picking up the 3.11 version which is like 0.7.0 I think)
| @@ -0,0 +1,23 @@ | |||
| package sqlite | |||
|
|
|||
| type DbOptions struct { | |||
|
Discussed offline: We'll stick with the current implementation and revisit in the future if/when we need migrations that require more than SQL. Just need those dependencies fixed up and we can merge @kevinrizza ! |
3b57071 to
4621b4f
Compare
| var _ registry.Load = &SQLLoader{} | ||
|
|
||
| func NewSQLLiteLoader(outFilename string) (*SQLLoader, error) { | ||
| func NewSQLLiteLoader(outFilename, migrationsPath string) (*SQLLoader, error) { |
There was a problem hiding this comment.
Did I hallucinate a change to this API? I thought this became variadic with config options?
There was a problem hiding this comment.
you did not, i rebased poorly :) latest should contain the right bits.
This commit introduces a day 0 set of building blocks to implement a database migration framework into the sqlite database. Adding the following changes: - Introduce the `golang-migrate` project as a dependency and vendor it - Introduce a `db_migrations` folder with an initial no-op migration to bootstrap the initial database version for newly created databases - Update the initialization of the database when creating the initial schema to hard set the database migration version on db creation before loading any data This commit does not introduce a method of actually running future migrations against existing databases -- that is left for future implementation based on this groundwork when a set of commands that updates the database in place exists and requires the migration. The intention of this commit is also to ensure that future initialized registry databases have an initial version that can be used for migrations in future releases.
Use the go-bindata tool to generate a golang migrations file to compile the content of the db_migrations folder into the bundle. This ensures that the compiled binaries are portable and do not require around the migrations themselves along with the binary. Also updated the makefile to pull the go-bindata file and generate the `migration.go` file as part of `make build`
4621b4f to
fcf1ff1
Compare
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: ecordell, kevinrizza The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
This commit introduces a day 0 set of building blocks to implement a
database migration framework into the sqlite database. Adding the
following changes:
golang-migrateproject as a dependency and vendor itdb_migrationsfolder with an initial no-op migration tobootstrap the initial database version for newly created databases
schema to hard set the database migration version on db creation
before loading any data
This commit does not introduce a method of actually running future
migrations against existing databases -- that is left for future
implementation based on this groundwork when a set of commands that
updates the database in place exists and requires the migration.
The intention of this commit is also to ensure that future initialized
registry databases have an initial version that can be used for
migrations in future releases.