-
Notifications
You must be signed in to change notification settings - Fork 93
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
Support for database/sql
in migrations + framework for multi-driver River
#98
Conversation
5367858
to
78ceba1
Compare
go.mod
Outdated
|
||
replace github.com/riverqueue/river/riverdriver/riverpgxv5 => ./riverdriver/riverpgxv5 | ||
|
||
replace github.com/riverqueue/river/riverdriver/riversql => ./riverdriver/riversql |
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.
See note in PR description, but these replaces are needed for now to show that the PR works. If we bring it in, the change will be broken up into three chunks and all the replaces removed before final release.
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.
Also note: lint's going to fail for this PR because of these, but the other checks are all working, so safe to ignore for now.
|
||
// Executor provides River operations against a database. It may be a database | ||
// pool or transaction. | ||
type Executor interface { |
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 executor will eventually contain all possible operations against a River schema. Notably, it looks quite similar to an adapter, and long term, will probably replace it.
78ceba1
to
1c0d3f5
Compare
) | ||
|
||
// Verify interface compliance. | ||
var _ Driver[pgx.Tx] = &riverpgxv5.Driver{} |
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 check moves into each driver as they verify their own interface compliance.
1c0d3f5
to
2e4c2d5
Compare
@bgentry Arg, sorry this one's kind of big, but I figured it was better to build a bit more of a solid platform for the drivers rather than introduce some new backdoor to make them usable with |
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.
Fantastic work on this, it's a nice step toward broader driver support 🎉 🚀
There are some complications here to figure out like
LISTEN
/NOTIFY
though, and I'm not clear whetherdatabase/sql
could ever become a
fully functional driver as it might be missing some needed functionality
(e.g. subtransactions are still not supported after talking about them
for ten f*ing years [2]. However, even if it's not, the system would let
us support other fully functional packages or future major versions of
pgx (or even past ones likepgx/v4
if there's demand).
Yeah, the listener implementation is deeply tied to pgxv5 and might either be very difficult or impossible to implement in other drivers. My current thinking is that all the drivers will need to have a dependency on pgxv5 and provide a way to retrieve a *pgx.ConnConfig
that the notifier
can use to connect to the DB with pgxv5. That or we pull the notifier into the driver interface, and they'll all still be implemented with pgxv5 under the covers even if another driver is also being used for other queries.
riverdriver/riversql/go.mod
Outdated
@@ -0,0 +1,25 @@ | |||
module github.com/riverqueue/river/riverdriver/riversql |
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 have some qualms with this package name. Primarily worried that it could be confusing or take away a name that we might want to use for other purposes. However the best alternative I can come up with is riverdbsql
which I don't love either 😕
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'm -1 on riverdbsql
just because it doesn't really reflect the actual naming of database/sql
very well (i.e. abbreviation in one but not the other).
I just pushed a change that renames it to riverdatabasesql
— it's a little bit of a mouthful (one reason I originally avoided it), but I figure that this package will be used so rarely that it probably doesn't matter. What do you think?
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 works.
queries: | ||
- ../../../riverpgxv5/internal/dbsqlc/river_migration.sql | ||
schema: | ||
- ../../../riverpgxv5/internal/dbsqlc/river_migration.sql |
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.
Is the thought that in general we should be able to share queries between drivers and let sqlc abstract away the differences? More generated code to execute or compile, but less code to actually maintain?
After reading your comment about how each driver would need to implement the full set of query methods I had some concerns about the amount of duplication we'd end up with, but if we can share the underlying schema and query definitions then that should help considerably.
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.
Yeah, that's the idea — more compiled code, but the SQL would be entirely shared. So far I haven't seen anything to suggest that sharing the sqlc SQL sources won't be possible, so I think it should work reasonably well.
// mapSlice manipulates a slice and transforms it to a slice of another type. | ||
func mapSlice[T any, R any](collection []T, mapFunc func(T) R) []R { | ||
result := make([]R, len(collection)) | ||
|
||
for i, item := range collection { | ||
result[i] = mapFunc(item) | ||
} | ||
|
||
return result | ||
} |
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 assume this is included in the pgxv5 driver to avoid a circular dependency on internal/util/sliceutil
?
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.
Yeah :/ We could add another package that both top-level river
and the drivers could both use, but doesn't feel worth it. We could put it in riverdriver
to share, but that feels pretty weird. I suspect this is the main function that'll be useful so there won't be too much more duplication.
docs/development.md
Outdated
git tag riverdriver/VERSION -m "release riverdriver/VERSION" | ||
git tag riverdriver/riverpgxv5/$VERSION -m "release riverdriver/riverpgxv5/$VERSION" | ||
git tag riverdriver/riversql/$VERSION -m "release riverdriver/riversql/$VERSION" |
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.
Getting closer to the point where this might warrant a little bit of effort to automate.
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.
Yeah, major +1.
9b78294
to
5c299aa
Compare
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
4cf1e83
to
7dd9d6f
Compare
@bgentry Alright, it seems like #102 is also bad, so back to the original plan. What I'd do for release is:
I also had to bring this back in gomoddirectives:
replace-local: true We'd remove that again as part of step (3). This seems pretty painful for sure, but I don't think there's another way? We could automate most of this, but we'd have to unprotect the master branch because it'd require creating multiple commits in succession. Does this sound right you? |
d3cd47b
to
a27cf5d
Compare
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
a27cf5d
to
1933931
Compare
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
1933931
to
e868602
Compare
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
… River Here, add a new minimal driver called `riverdriver/riversql` that supports Go's built-in `database/sql` package, but only for purposes of migrations. The idea here is to fully complete #57 by providing a way of making `rivermigrate` interoperable with Go migration frameworks that support Go-based migrations like Goose, which provides hooks for `*sql.Tx` [1] rather than pgx. `riverdriver/riversql` is not a full driver and is only meant to be used with `rivermigrate`. We document this clearly in a number of places. To make a multi-driver world possible with River, we have to start the work of building a platform that does more than `riverpgxv5`'s "cheat" workaround. This works by having each driver implement specific database operations like `MigrationGetAll`, which target their wrapped database package of choice. This is accomplished by having each driver bundle in its own sqlc that targets its package. So `riverpgxv5` has an `sqlc.yaml` that targets `pgx/v5`, while `riversql` has one that targets `database/sql`. There's some `sqlc.yaml` duplication involved here, but luckily both drivers can share a `river_migration.sql` file that contains all queries involved, so you only need to change one place. `river_migration.sql` also migrates entirely out of the main `./internal/dbsqlc`. The idea here is that eventually `./internal/dbsqlc` will disappear completely, usurped entirely by driver-specific versions. As this is done, all references to `pgx` will disappear from the top-level package. There are some complications here to figure out like `LISTEN`/`NOTIFY` though, and I'm not clear whether `database/sql` could ever become a fully functional driver as it might be missing some needed functionality (e.g. subtransactions are still not supported after talking about them for ten f*ing years [2]. However, even if it's not, the system would let us support other fully functional packages or future major versions of pgx (or even past ones like `pgx/v4` if there's demand). `river/riverdriver` becomes a package as it now has types in it that need to be referenced by driver implementations, and this would otherwise not be possible without introducing a circular dependency. [1] https://github.com/pressly/goose#go-migrations [2] golang/go#7898
e868602
to
d9f4904
Compare
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
Background: While trying to figure out how to safely release [1] (which includes a new `riverdriver` submodule), I threw myself through a loop and realized that it'd be practically impossible to do without a whole bunch of manual finnagling on the release tags/lint settings in the repo, or breaking the master build, or most likely, both. This got me thinking: maybe we should, maybe we _should_ be using `replace` directives to make this less painful/brittle, which were originally removed in #35 because using `replace` makes it impossible to use `go install ...@latest`. A change that we made recently was the addition of a new Go migration API in #65 (`rivermigrate`), and a side effect of that change is that the API being used by the River CLI became entirely public (previously, it'd depended on packages in River's `internal`). By extension, that means it's now possible to make the River CLI its own Go module, something that was infeasible before because of the use of `internal`. So my thinking currently: maybe we should go back to trying to see if we can make `replace` in use by most of River's core packages, and keep the River CLI as its own module without `replace` so that it can still be installed with `go install ...@latest`. I'm not entirely sure we won't run into another problem, but it might be the easiest thing in the meantime. As the River CLI expands, we'll need to make sure to only use public APIs, but that might not be the worst thing anyway -- we could adopt the philosophy that any function the CLI accesses should also be accessible by the Go API. Here, notably we still use a `replace`, which I'm finding that I need to have a passing build for now, and which I think will have to temporarily stay until we cut a new release. Trying to build the new submodule without results in this error that I was unable to otherwise find a way around: $ go test . ambiguous import: found package github.com/riverqueue/river/cmd/river in multiple modules: github.com/riverqueue/river v0.0.12 (/Users/brandur/Documents/go/pkg/mod/github.com/riverqueue/[email protected]/cmd/river) github.com/riverqueue/river/cmd/river (/Users/brandur/Documents/projects/river/cmd/river) [1] #98
Here, add a new minimal driver called
riverdriver/riversql
thatsupports Go's built-in
database/sql
package, but only for purposes ofmigrations. The idea here is to fully complete #57 by providing a way of
making
rivermigrate
interoperable with Go migration frameworks thatsupport Go-based migrations like Goose, which provides hooks for
*sql.Tx
[1] rather than pgx.riverdriver/riversql
is not a full driver and is only meant to be usedwith
rivermigrate
. We document this clearly in a number of places.To make a multi-driver world possible with River, we have to start the
work of building a platform that does more than
riverpgxv5
's "cheat"workaround. This works by having each driver implement specific database
operations like
MigrationGetAll
, which target their wrapped databasepackage of choice.
This is accomplished by having each driver bundle in its own sqlc that
targets its package. So
riverpgxv5
has ansqlc.yaml
that targetspgx/v5
, whileriversql
has one that targetsdatabase/sql
. There'ssome
sqlc.yaml
duplication involved here, but luckily both drivers canshare a
river_migration.sql
file that contains all queries involved,so you only need to change one place.
river_migration.sql
also migratesentirely out of the main
./internal/dbsqlc
.The idea here is that eventually
./internal/dbsqlc
will disappearcompletely, usurped entirely by driver-specific versions. As this is
done, all references to
pgx
will disappear from the top-level package.There are some complications here to figure out like
LISTEN
/NOTIFY
though, and I'm not clear whether
database/sql
could ever become afully functional driver as it might be missing some needed functionality
(e.g. subtransactions are still not supported after talking about them
for ten f*ing years [2]. However, even if it's not, the system would let
us support other fully functional packages or future major versions of
pgx (or even past ones like
pgx/v4
if there's demand).river/riverdriver
becomes a package as it now has types in it thatneed to be referenced by driver implementations, and this would
otherwise not be possible without introducing a circular dependency.
Notably, this development branch has to use somego.mod
replace
directives to demonstrate that it works correctly. If we go this
direction, we'll need to break it into chunks to release it without
them:
Break out changes toriver/riverdriver
. Tag and release it.Break out changes toriverdriver/river*
drivers. Have them targetthe release in (1), comment out
replace
s, then tag and release them.Target the remaining River changes to the releases in (1) and (2),comment out
replace
s, then tag and release the top-level driver.Unfortunately future deep incisions to drivers will require similargymnastics, but I don't think there's a way around it (we already have
this process except it's currently two steps instead of three). The hope
is that these will change relatively rarely, so it won't be too painful.
[1] https://github.com/pressly/goose#go-migrations
[2] golang/go#7898