Skip to content

Commit c2fbaa4

Browse files
authored
Don't error on cancelled startup to make it not necessary to export ErrShutdown (#841)
This change's driven by the proposal in #830. When client start up is cancelled by a stop, we'd return `ErrShutdown` as a special signal. The problem is that `ErrShutdown` was never exported, so this wasn't really usable by outside callers. In #830 it was proposed to export `ErrShutdown`. However, after some discussion, we suspect that it might be better to leave it unexported, and then have `Start` return no error on cancelled startup. This minimizes the number of startup conditions callers have to handle. Because `ErrShutdown` wasn't previously exported, we should be able to make this change safely without breaking anyone. @mastercactapus did bring up a fair point that the alternative of exporting `ErrShutdown` would allow a caller to definitively tell the difference between the client successfully starting and backgrounding itself versus one whose start was cancelled. I think we're okay not to require that differentiation though because if `ErrShutdown` would've be returned, then the caller would have called `Stop`, so presumably they'd know to expect an successful start. I figure that in case a strong case for wanting to differentiate the cases comes through, we could always resurrect the error. Also, rename the internal `ErrShutdown` to `ErrStop` to be more consistent with the naming of `Client.Stop` (which had previously been called `Shutdown`). This won't make any difference, but will be a little better in case we ever do export it and then forget to rename it at that time.
1 parent 6a01986 commit c2fbaa4

4 files changed

Lines changed: 12 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Client no longer returns an error if stopped before startup could complete (previously, it returned the unexported `ErrShutdown`). [PR #841](https://github.com/riverqueue/river/pull/841).
13+
1014
## [0.20.2] - 2025-04-08
1115

1216
### Added

client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ func (c *Client[TTx]) Start(ctx context.Context) error {
888888
}(); err != nil {
889889
defer stopped()
890890
if errors.Is(context.Cause(fetchCtx), startstop.ErrStop) {
891-
return rivercommon.ErrShutdown
891+
return nil
892892
}
893893
return err
894894
}
@@ -928,7 +928,7 @@ func (c *Client[TTx]) Start(ctx context.Context) error {
928928
startstop.StopAllParallel(producersAsServices()...)
929929
c.baseService.Logger.DebugContext(ctx, c.baseService.Name+": All producers stopped")
930930

931-
c.workCancel(rivercommon.ErrShutdown)
931+
c.workCancel(rivercommon.ErrStop)
932932

933933
// Stop all mainline services where stop order isn't important.
934934
startstop.StopAllParallel(append(
@@ -985,7 +985,7 @@ func (c *Client[TTx]) Stop(ctx context.Context) error {
985985
// instead.
986986
func (c *Client[TTx]) StopAndCancel(ctx context.Context) error {
987987
c.baseService.Logger.InfoContext(ctx, c.baseService.Name+": Hard stop started; cancelling all work")
988-
c.workCancel(rivercommon.ErrShutdown)
988+
c.workCancel(rivercommon.ErrStop)
989989

990990
shouldStop, stopped, finalizeStop := c.baseStartStop.StopInit()
991991
if !shouldStop {

client_test.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,7 @@ func Test_Client(t *testing.T) {
344344
err := client.Queues().Add(fmt.Sprintf("new_queue_%d_%d_before", workerNum, j), QueueConfig{MaxWorkers: 1})
345345
require.NoError(t, err)
346346

347-
err = client.Start(ctx)
348-
if !errors.Is(err, rivercommon.ErrShutdown) {
349-
require.NoError(t, err)
350-
}
347+
require.NoError(t, client.Start(ctx))
351348

352349
err = client.Queues().Add(fmt.Sprintf("new_queue_%d_%d_after", workerNum, j), QueueConfig{MaxWorkers: 1})
353350
require.NoError(t, err)
@@ -1104,7 +1101,7 @@ func Test_Client(t *testing.T) {
11041101

11051102
clientWithStop := &clientWithSimpleStop[pgx.Tx]{Client: client}
11061103

1107-
startstoptest.StressErr(ctx, t, clientWithStop, rivercommon.ErrShutdown)
1104+
startstoptest.Stress(ctx, t, clientWithStop)
11081105
})
11091106
}
11101107

@@ -1405,7 +1402,6 @@ func Test_Client_StopAndCancel(t *testing.T) {
14051402
t.Logf("Job waiting for context cancellation")
14061403
defer t.Logf("Job finished")
14071404
<-ctx.Done()
1408-
require.ErrorIs(t, context.Cause(ctx), rivercommon.ErrShutdown)
14091405
t.Logf("Job context done, closing chan and returning")
14101406
close(jobDoneChan)
14111407
return nil

internal/rivercommon/river_common.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ const (
1818

1919
type ContextKeyClient struct{}
2020

21-
// ErrShutdown is a special error injected by the client into its fetch and work
21+
// ErrStop is a special error injected by the client into its fetch and work
2222
// CancelCauseFuncs when it's stopping. It may be used by components for such
23-
// cases like avoiding logging an error during a normal shutdown procedure. This
24-
// is internal for the time being, but we could also consider exposing it.
25-
var ErrShutdown = errors.New("shutdown initiated")
23+
// cases like avoiding logging an error during a normal shutdown procedure.
24+
var ErrStop = errors.New("stop initiated")

0 commit comments

Comments
 (0)