Skip to content

Commit

Permalink
Add benchmarks for "insert many" operations
Browse files Browse the repository at this point in the history
A couple quick benchmarks for the two "insert many" variants to help vet
whether or not we should keep the "fast" route.

There is definitely a speed difference, with the fast variant being
about 25% faster as it can skip returning rows:

    go test -bench=BenchmarkClient
    goos: darwin
    goarch: arm64
    pkg: github.com/riverqueue/river
    cpu: Apple M1
    BenchmarkClient/JobInsertMany-8                      312           4156647 ns/op
    BenchmarkClient/JobInsertManyFast-8                  417           3054985 ns/op
    PASS
    ok      github.com/riverqueue/river     15.709s

Can't 100% decide where that leaves us. There's definitely a speed
advantage, but it definitely congests the API to a degree, and having
more functions make implementing features like middleware trickier.
  • Loading branch information
brandur committed Sep 15, 2024
1 parent 67c8a8a commit 999ff03
Showing 1 changed file with 53 additions and 6 deletions.
59 changes: 53 additions & 6 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ func (c *clientWithSimpleStop[TTx]) Stop() {
_ = c.Client.Stop(context.Background())
}

func newTestConfig(t *testing.T, callback callbackFunc) *Config {
t.Helper()
func newTestConfig(tb testing.TB, callback callbackFunc) *Config {
tb.Helper()
workers := NewWorkers()
if callback != nil {
AddWorker(workers, &callbackWorker{fn: callback})
Expand All @@ -126,7 +126,7 @@ func newTestConfig(t *testing.T, callback callbackFunc) *Config {
return &Config{
FetchCooldown: 20 * time.Millisecond,
FetchPollInterval: 50 * time.Millisecond,
Logger: riversharedtest.Logger(t),
Logger: riversharedtest.Logger(tb),
MaxAttempts: MaxAttemptsDefault,
Queues: map[string]QueueConfig{QueueDefault: {MaxWorkers: 50}},
TestOnly: true, // disables staggered start in maintenance services
Expand All @@ -136,11 +136,11 @@ func newTestConfig(t *testing.T, callback callbackFunc) *Config {
}
}

func newTestClient(t *testing.T, dbPool *pgxpool.Pool, config *Config) *Client[pgx.Tx] {
t.Helper()
func newTestClient(tb testing.TB, dbPool *pgxpool.Pool, config *Config) *Client[pgx.Tx] {
tb.Helper()

client, err := NewClient(riverpgxv5.New(dbPool), config)
require.NoError(t, err)
require.NoError(tb, err)

return client
}
Expand Down Expand Up @@ -5495,3 +5495,50 @@ func TestDefaultClientIDWithHost(t *testing.T) {
require.Equal(t, strings.Repeat("a", 60)+"_2024_03_07T04_39_12_123456", defaultClientIDWithHost(startedAt, strings.Repeat("a", 60)))
require.Equal(t, strings.Repeat("a", 60)+"_2024_03_07T04_39_12_123456", defaultClientIDWithHost(startedAt, strings.Repeat("a", 61)))
}

func BenchmarkClient(b *testing.B) {
ctx := context.Background()

type testBundle struct{}

setup := func(b *testing.B) (*Client[pgx.Tx], *testBundle) {
b.Helper()

dbPool := riverinternaltest.TestDB(ctx, b)
config := newTestConfig(b, nil)

return newTestClient(b, dbPool, config), &testBundle{}
}

b.Run("JobInsertMany", func(b *testing.B) {
client, _ := setup(b)

params := make([]InsertManyParams, 100)
for i := range params {
params[i].Args = noOpArgs{}
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
_, err := client.InsertMany(ctx, params)
require.NoError(b, err)
}
})

b.Run("JobInsertManyFast", func(b *testing.B) {
client, _ := setup(b)

params := make([]InsertManyParams, 100)
for i := range params {
params[i].Args = noOpArgs{}
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
_, err := client.InsertManyFast(ctx, params)
require.NoError(b, err)
}
})
}

0 comments on commit 999ff03

Please sign in to comment.