-
Notifications
You must be signed in to change notification settings - Fork 517
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
Adding a goose provider #379
Comments
Since I proposed a new function in #599, this might be relevant to this discussion. In the initial post, you proposed I would suggest
goose.NewProvider(dbDialect, db, os.DirFS("/path/to/migrations"))
// if the fs is provided and you want a subfolder, use fs.Sub
fsys, err := fs.Sub(fsys, "migration/sub_folder")
// todo: handle error
goose.NewProvider(dbDialect, db, fsys)
// simple usage
goose.NewProvider(dbDialect, db, os.DirFS("/path/to/migrations"))
// advanced usage
goose.NewProvider(dbDialect, db, os.DirFS("/path/to/migrations"), goose.WithTableName("my_migrations"), goose.ApplyOutOfOrder, goose.Verbose(true)) What do you think? |
Thanks for the suggestions @oliverpool. I've gone back and forth on option raw struct vs struct with getters vs functional options and everything in between. tl;dr I think functional options are the way to go mainly for consistency and future-proofing. Some notes I had:
Taking a fs.FS isn't a bad idea. The original thinking was to have a |
Alright, I kind of like the the 2 suggestions above @oliverpool 👍 Going to settle on this API, merge it and continue to implement it. func NewProvider(
dialect Dialect,
db *sql.DB,
fsys fs.FS,
opts ...ProviderOption,
) (*Provider, error) {
...
} And I did like the original design because it had Setter which could be documented, another popular pattern. But it comes with limitations as well. Either way, I do like documenting the behaviour with the function instead of on the struct fields.
|
Stubbed out a Will start porting over some of the implementations from #507 (unmerged) And would also like to introduce a |
This functionality has been added in v3.16.0 and higher. A bit more detail in this blog post: https://pressly.github.io/goose/blog/2023/goose-provider/#database-locking Thank you to everyone for the feedback. |
Target date port Provider code into main branch: November 18, 2023
WIP Updates
October 7, 2023 - add a
*goose.Provider
with unimplemented methods. See #596October 9, 2023 - add
package lock
which contains aSessionLocker
interface with a postgres exclusive session-level advisory lock implementation. See #606October 14, 2023 - add collecting sources and merging migrations (internal). This new implementation enables 2 highly requested features and fixes an existing limitation. See #615, #616 (refactor)
October 16, 2023 - collapse all provider-related logic into
package provider
, add migration logic and start adding integration tests (~85% coverage). Also, add parallel test for postgres to exercise theSessionLocker
. #617October 26, 2023 - add
package database
with aStore
interface. #623October 30, 2023 - bug fixes, resolve naming collisions. Settle on Store implementation. #624, #626
Initial description
For a long time, one of the most requested features was the ability to have multiple instances of goose within the same runtime. E.g., #114, #351, etc.
The problem with the current
goose
package is the global state. Which works just fine for a single instance when used to create a single binary.This issue tracks the work that adds a
Provider
abstraction to the goose library. This also gives us a chance to improve package ergonomics so the goose library feels idiomatic to your average Go developer. Remember, goose is a 10-year-old project and we (read Gophers) have learned a lot about best practices for writing idiomatic Go code.Provider
At a high level, the idea is to have a
goose.NewProvider
entry point.The first 3 args are mandatory.
As a library, goose does not care about which driver the caller is using, only the SQL dialect. It is now the caller's responsibility to create a
*sql.DB
session, match it to a dialect and pass it down. The dialect is now a well-defined type, making it much harder to do the wrong thing.See Doc comments for more details.
This design future-proofs out ability to extend functionality.
Improvements
As a library, goose has too many
log.Print
statements. And we might want to keep that, but only if-verbose
is enabled.For example,
goose.Run
andgoose.RunWithOptions
are CLI concerns and do not belong as functions in the core library. These functions take the command name and the args, so it's impossible to know what they do without reading all ofmain.go
. The library should not know anything about CLI commands.context.Context
support for all methods that run migrationsThe text was updated successfully, but these errors were encountered: