-
Notifications
You must be signed in to change notification settings - Fork 253
fix: correct pubsub provider lifecycle #3038
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,7 +106,6 @@ type ( | |
| prometheusEngineMetrics *rmetric.EngineMetrics | ||
| connectionMetrics *rmetric.ConnectionMetrics | ||
| instanceData InstanceData | ||
| pubSubProviders []datasource.Provider | ||
| traceDialer *TraceDialer | ||
| connector *grpcconnector.Connector | ||
| circuitBreakerManager *circuit.Manager | ||
|
|
@@ -716,6 +715,12 @@ type graphMux struct { | |
| otelCacheMetrics *rmetric.CacheMetrics | ||
| streamMetricStore rmetric.StreamMetricStore | ||
| prometheusMetricsExporter *graphqlmetrics.PrometheusMetricsExporter | ||
|
|
||
| // pubSubProviders are the EDFS providers built for this mux. They are owned by | ||
| // the mux (not the server) so that a mux reused by the next server keeps its | ||
| // providers alive: Shutdown skips reused muxes, so their providers are not torn | ||
| // down until the mux itself is finally discarded. | ||
| pubSubProviders []datasource.Provider | ||
| } | ||
|
|
||
| // buildOperationCaches creates the caches for the graph mux. | ||
|
|
@@ -1027,6 +1032,15 @@ func (s *graphMux) Shutdown(ctx context.Context) error { | |
| } | ||
| } | ||
|
|
||
| // Shut down the pubsub providers owned by this mux. A reused mux never reaches | ||
| // here (Shutdown skips it), so its providers stay alive for the next server. | ||
| const defaultShutdownTimeout = 5 * time.Second | ||
| if pErr := providersActionWithTimeout(ctx, s.pubSubProviders, func(ctx context.Context, provider datasource.Provider) error { | ||
| return provider.Shutdown(ctx) | ||
| }, defaultShutdownTimeout, "pubsub provider shutdown timed out"); pErr != nil { | ||
| err = errors.Join(err, pErr) | ||
| } | ||
|
Comment on lines
+1037
to
+1042
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idea: Have start/stop methods for pubsub providers on graph muxes, which are called here instead of inlined code. Matches what |
||
|
|
||
| if err != nil { | ||
| return fmt.Errorf("shutdown graph mux: %w", err) | ||
| } | ||
|
|
@@ -1540,10 +1554,11 @@ func (s *graphServer) buildGraphMux( | |
| }) | ||
| } | ||
|
|
||
| s.pubSubProviders = providers | ||
| if pubSubStartupErr := s.startupPubSubProviders(s.graphServerCtx); pubSubStartupErr != nil { | ||
| if pubSubStartupErr := s.startupPubSubProviders(s.graphServerCtx, providers); pubSubStartupErr != nil { | ||
| return nil, pubSubStartupErr | ||
| } | ||
| // Providers are owned by the mux so they follow its reuse/shutdown lifecycle. | ||
| gm.pubSubProviders = providers | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| operationProcessor := NewOperationProcessor(OperationProcessorOptions{ | ||
| Executor: executor, | ||
|
|
@@ -2266,10 +2281,7 @@ func (s *graphServer) Shutdown(ctx context.Context) error { | |
| subgraphTransport.CloseIdleConnections() | ||
| } | ||
|
|
||
| // Shutdown pubsub providers | ||
| if err := s.shutdownPubSubProviders(ctx); err != nil { | ||
| finalErr = errors.Join(finalErr, err) | ||
| } | ||
| // Pubsub providers are shut down per-mux in the loop above (skipping reused muxes). | ||
|
|
||
| if s.connector != nil { | ||
| s.logger.Debug("Stopping old plugins") | ||
|
|
@@ -2284,36 +2296,24 @@ func (s *graphServer) Shutdown(ctx context.Context) error { | |
| // startupPubSubProviders starts the given pubsub providers | ||
| // It returns an error if any of the providers fail to start | ||
| // or if some providers takes to long to start | ||
| func (s *graphServer) startupPubSubProviders(ctx context.Context) error { | ||
| func (s *graphServer) startupPubSubProviders(ctx context.Context, providers []datasource.Provider) error { | ||
| // Default timeout for pubsub provider startup | ||
| const defaultStartupTimeout = 5 * time.Second | ||
|
|
||
| return s.providersActionWithTimeout(ctx, func(ctx context.Context, provider datasource.Provider) error { | ||
| return providersActionWithTimeout(ctx, providers, func(ctx context.Context, provider datasource.Provider) error { | ||
| return provider.Startup(ctx) | ||
| }, defaultStartupTimeout, "pubsub provider startup timed out") | ||
| } | ||
|
|
||
| // shutdownPubSubProviders shuts down all pubsub providers | ||
| // It returns an error if any of the providers fail to shutdown | ||
| // or if some providers takes to long to shutdown | ||
| func (s *graphServer) shutdownPubSubProviders(ctx context.Context) error { | ||
| // Default timeout for pubsub provider shutdown | ||
| const defaultShutdownTimeout = 5 * time.Second | ||
|
|
||
| return s.providersActionWithTimeout(ctx, func(ctx context.Context, provider datasource.Provider) error { | ||
| return provider.Shutdown(ctx) | ||
| }, defaultShutdownTimeout, "pubsub provider shutdown timed out") | ||
| } | ||
|
Comment on lines
-2299
to
-2306
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would also remove the |
||
|
|
||
| func (s *graphServer) providersActionWithTimeout(ctx context.Context, action func(ctx context.Context, provider datasource.Provider) error, timeout time.Duration, timeoutMessage string) error { | ||
| func providersActionWithTimeout(ctx context.Context, providers []datasource.Provider, action func(ctx context.Context, provider datasource.Provider) error, timeout time.Duration, timeoutMessage string) error { | ||
| cancellableCtx, cancel := context.WithCancel(ctx) | ||
| defer cancel() | ||
|
|
||
| timer := time.NewTimer(timeout) | ||
| defer timer.Stop() | ||
|
|
||
| providersGroup := new(errgroup.Group) | ||
| for _, provider := range s.pubSubProviders { | ||
| for _, provider := range providers { | ||
| providersGroup.Go(func() error { | ||
| actionDone := make(chan error, 1) | ||
| go func() { | ||
|
|
||
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.
imo we don't need this comment