-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Here, follow up #429 to add a basic plugin system for River clients which allows a driver to add maintenance and non-maintenance services to a client before it starts up. The plugin interface is implemented by the drivers themselves, and looks like this: type driverPlugin[TTx any] interface { // PluginInit initializes a plugin with an archetype and client. It's // invoked on Client.NewClient. PluginInit(archetype *baseservice.Archetype, client *Client[TTx]) // PluginMaintenanceServices returns additional maintenance services (will // only run on an elected leader) for a River client. PluginMaintenanceServices() []startstop.Service // PluginServices returns additional non-maintenance services (will run on // all clients) for a River client. PluginServices() []startstop.Service } The change is fairly straightforward, and we make sure to bring in some test cases verifying the plugin services were indeed added correctly.
- Loading branch information
Showing
8 changed files
with
169 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,25 @@ | ||
package river | ||
|
||
import ( | ||
"log/slog" | ||
|
||
"github.com/riverqueue/river/rivertype" | ||
"github.com/riverqueue/river/rivershared/baseservice" | ||
"github.com/riverqueue/river/rivershared/startstop" | ||
) | ||
|
||
type Pluginable[TTx any] interface { | ||
Plugin(client *Client[TTx], logger *slog.Logger) rivertype.Plugin | ||
// A plugin API that drivers may implemented to extend a River client. Driver | ||
// plugins may, for example, add additional maintenance services. | ||
// | ||
// This should be considered a River internal API and its stability is not | ||
// guaranteed. DO NOT USE. | ||
type driverPlugin[TTx any] interface { | ||
// PluginInit initializes a plugin with an archetype and client. It's | ||
// invoked on Client.NewClient. | ||
PluginInit(archetype *baseservice.Archetype, client *Client[TTx]) | ||
|
||
// PluginMaintenanceServices returns additional maintenance services (will | ||
// only run on an elected leader) for a River client. | ||
PluginMaintenanceServices() []startstop.Service | ||
|
||
// PluginServices returns additional non-maintenance services (will run on | ||
// all clients) for a River client. | ||
PluginServices() []startstop.Service | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package river | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/jackc/pgx/v5" | ||
"github.com/jackc/pgx/v5/pgxpool" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/riverqueue/river/internal/riverinternaltest" | ||
"github.com/riverqueue/river/riverdriver/riverpgxv5" | ||
"github.com/riverqueue/river/rivershared/baseservice" | ||
"github.com/riverqueue/river/rivershared/riversharedtest" | ||
"github.com/riverqueue/river/rivershared/startstop" | ||
) | ||
|
||
func TestClientDriverPlugin(t *testing.T) { | ||
t.Parallel() | ||
|
||
ctx := context.Background() | ||
|
||
type testBundle struct { | ||
pluginDriver *TestDriverWithPlugin | ||
} | ||
|
||
setup := func(t *testing.T) (*Client[pgx.Tx], *testBundle) { | ||
t.Helper() | ||
|
||
pluginDriver := newDriverWithPlugin(t, riverinternaltest.TestDB(ctx, t)) | ||
|
||
client, err := NewClient(pluginDriver, newTestConfig(t, nil)) | ||
require.NoError(t, err) | ||
|
||
return client, &testBundle{ | ||
pluginDriver: pluginDriver, | ||
} | ||
} | ||
|
||
t.Run("ServicesStart", func(t *testing.T) { | ||
t.Parallel() | ||
|
||
client, bundle := setup(t) | ||
|
||
startClient(ctx, t, client) | ||
|
||
riversharedtest.WaitOrTimeout(t, startstop.WaitAllStartedC( | ||
bundle.pluginDriver.maintenanceService, | ||
bundle.pluginDriver.service, | ||
)) | ||
}) | ||
} | ||
|
||
var _ driverPlugin[pgx.Tx] = &TestDriverWithPlugin{} | ||
|
||
type TestDriverWithPlugin struct { | ||
*riverpgxv5.Driver | ||
maintenanceService startstop.Service | ||
service startstop.Service | ||
} | ||
|
||
func newDriverWithPlugin(t *testing.T, dbPool *pgxpool.Pool) *TestDriverWithPlugin { | ||
t.Helper() | ||
|
||
newService := func(name string) startstop.Service { | ||
return startstop.StartStopFunc(func(ctx context.Context, shouldStart bool, started, stopped func()) error { | ||
if !shouldStart { | ||
return nil | ||
} | ||
|
||
go func() { | ||
started() | ||
defer stopped() // this defer should come first so it's last out | ||
|
||
t.Logf("Test service started: %s", name) | ||
|
||
<-ctx.Done() | ||
}() | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
return &TestDriverWithPlugin{ | ||
Driver: riverpgxv5.New(dbPool), | ||
maintenanceService: newService("maintenance service"), | ||
service: newService("other service"), | ||
} | ||
} | ||
|
||
func (d *TestDriverWithPlugin) PluginInit(archetype *baseservice.Archetype, client *Client[pgx.Tx]) {} | ||
|
||
func (p *TestDriverWithPlugin) PluginMaintenanceServices() []startstop.Service { | ||
return []startstop.Service{p.maintenanceService} | ||
} | ||
|
||
func (p *TestDriverWithPlugin) PluginServices() []startstop.Service { | ||
return []startstop.Service{p.service} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,16 @@ | ||
module github.com/riverqueue/river/rivertype | ||
|
||
go 1.21.4 | ||
go 1.22.5 | ||
|
||
replace github.com/riverqueue/river/rivershared => ../rivershared | ||
|
||
require github.com/stretchr/testify v1.9.0 | ||
|
||
require github.com/kr/text v0.2.0 // indirect | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/riverqueue/river/rivershared v0.0.0-20240707170519-d0685f5e0a5d | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,20 @@ | ||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= | ||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= | ||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= | ||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= | ||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters