@@ -46,6 +46,7 @@ import (
46
46
"github.com/conduitio/conduit/pkg/foundation/metrics/measure"
47
47
"github.com/conduitio/conduit/pkg/foundation/metrics/prometheus"
48
48
"github.com/conduitio/conduit/pkg/lifecycle"
49
+ lifecycle_v2 "github.com/conduitio/conduit/pkg/lifecycle-poc"
49
50
"github.com/conduitio/conduit/pkg/orchestrator"
50
51
"github.com/conduitio/conduit/pkg/pipeline"
51
52
conn_plugin "github.com/conduitio/conduit/pkg/plugin/connector"
@@ -77,7 +78,7 @@ import (
77
78
)
78
79
79
80
const (
80
- exitTimeout = 10 * time .Second
81
+ exitTimeout = 30 * time .Second
81
82
)
82
83
83
84
// Runtime sets up all services for serving and monitoring a Conduit instance.
@@ -95,7 +96,7 @@ type Runtime struct {
95
96
pipelineService * pipeline.Service
96
97
connectorService * connector.Service
97
98
processorService * processor.Service
98
- lifecycleService * lifecycle. Service
99
+ lifecycleService lifecycleService
99
100
100
101
connectorPluginService * conn_plugin.PluginService
101
102
processorPluginService * proc_plugin.PluginService
@@ -107,6 +108,14 @@ type Runtime struct {
107
108
logger log.CtxLogger
108
109
}
109
110
111
+ // lifecycleService is an interface that we use temporarily to allow for
112
+ // both the old and new lifecycle services to be used interchangeably.
113
+ type lifecycleService interface {
114
+ Start (ctx context.Context , pipelineID string ) error
115
+ Stop (ctx context.Context , pipelineID string , force bool ) error
116
+ Init (ctx context.Context ) error
117
+ }
118
+
110
119
// NewRuntime sets up a Runtime instance and primes it for start.
111
120
func NewRuntime (cfg Config ) (* Runtime , error ) {
112
121
if err := cfg .Validate (); err != nil {
@@ -203,21 +212,28 @@ func createServices(r *Runtime) error {
203
212
tokenService ,
204
213
)
205
214
206
- // Error recovery configuration
207
- errRecoveryCfg := & lifecycle.ErrRecoveryCfg {
208
- MinDelay : r .Config .Pipelines .ErrorRecovery .MinDelay ,
209
- MaxDelay : r .Config .Pipelines .ErrorRecovery .MaxDelay ,
210
- BackoffFactor : r .Config .Pipelines .ErrorRecovery .BackoffFactor ,
211
- MaxRetries : r .Config .Pipelines .ErrorRecovery .MaxRetries ,
212
- MaxRetriesWindow : r .Config .Pipelines .ErrorRecovery .MaxRetriesWindow ,
213
- }
214
-
215
215
plService := pipeline .NewService (r .logger , r .DB )
216
216
connService := connector .NewService (r .logger , r .DB , r .connectorPersister )
217
217
procService := processor .NewService (r .logger , r .DB , procPluginService )
218
- lifecycleService := lifecycle .NewService (r .logger , errRecoveryCfg , connService , procService , connPluginService , plService )
219
- provisionService := provisioning .NewService (r .DB , r .logger , plService , connService , procService , connPluginService , lifecycleService , r .Config .Pipelines .Path )
220
218
219
+ var lifecycleService lifecycleService
220
+ if r .Config .Preview .PipelineArchV2 {
221
+ r .logger .Info (context .Background ()).Msg ("using lifecycle service v2" )
222
+ lifecycleService = lifecycle_v2 .NewService (r .logger , connService , procService , connPluginService , plService )
223
+ } else {
224
+ // Error recovery configuration
225
+ errRecoveryCfg := & lifecycle.ErrRecoveryCfg {
226
+ MinDelay : r .Config .Pipelines .ErrorRecovery .MinDelay ,
227
+ MaxDelay : r .Config .Pipelines .ErrorRecovery .MaxDelay ,
228
+ BackoffFactor : r .Config .Pipelines .ErrorRecovery .BackoffFactor ,
229
+ MaxRetries : r .Config .Pipelines .ErrorRecovery .MaxRetries ,
230
+ MaxRetriesWindow : r .Config .Pipelines .ErrorRecovery .MaxRetriesWindow ,
231
+ }
232
+
233
+ lifecycleService = lifecycle .NewService (r .logger , errRecoveryCfg , connService , procService , connPluginService , plService )
234
+ }
235
+
236
+ provisionService := provisioning .NewService (r .DB , r .logger , plService , connService , procService , connPluginService , lifecycleService , r .Config .Pipelines .Path )
221
237
orc := orchestrator .NewOrchestrator (r .DB , r .logger , plService , connService , procService , connPluginService , procPluginService , lifecycleService )
222
238
223
239
r .Orchestrator = orc
@@ -415,6 +431,15 @@ func (r *Runtime) initProfiling(ctx context.Context) (deferred func(), err error
415
431
}
416
432
417
433
func (r * Runtime ) registerCleanup (t * tomb.Tomb ) {
434
+ if r .Config .Preview .PipelineArchV2 {
435
+ r .registerCleanupV2 (t )
436
+ } else {
437
+ r .registerCleanupV1 (t )
438
+ }
439
+ }
440
+
441
+ func (r * Runtime ) registerCleanupV1 (t * tomb.Tomb ) {
442
+ ls := r .lifecycleService .(* lifecycle.Service )
418
443
t .Go (func () error {
419
444
<- t .Dying ()
420
445
// start cleanup with a fresh context
@@ -423,12 +448,12 @@ func (r *Runtime) registerCleanup(t *tomb.Tomb) {
423
448
// t.Err() can be nil, when we had a call: t.Kill(nil)
424
449
// t.Err() will be context.Canceled, if the tomb's context was canceled
425
450
if t .Err () == nil || cerrors .Is (t .Err (), context .Canceled ) {
426
- r . lifecycleService .StopAll (ctx , pipeline .ErrGracefulShutdown )
451
+ ls .StopAll (ctx , pipeline .ErrGracefulShutdown )
427
452
} else {
428
453
// tomb died due to a real error
429
- r . lifecycleService .StopAll (ctx , cerrors .Errorf ("conduit experienced an error: %w" , t .Err ()))
454
+ ls .StopAll (ctx , cerrors .Errorf ("conduit experienced an error: %w" , t .Err ()))
430
455
}
431
- err := r . lifecycleService .Wait (exitTimeout )
456
+ err := ls .Wait (exitTimeout )
432
457
t .Go (func () error {
433
458
r .connectorPersister .Wait ()
434
459
return r .DB .Close ()
@@ -437,6 +462,62 @@ func (r *Runtime) registerCleanup(t *tomb.Tomb) {
437
462
})
438
463
}
439
464
465
+ func (r * Runtime ) registerCleanupV2 (t * tomb.Tomb ) {
466
+ ls := r .lifecycleService .(* lifecycle_v2.Service )
467
+ t .Go (func () error {
468
+ <- t .Dying ()
469
+ // start cleanup with a fresh context
470
+ ctx := context .Background ()
471
+
472
+ err := ls .StopAll (ctx , false )
473
+ if err != nil {
474
+ r .logger .Err (ctx , err ).Msg ("some pipelines stopped with an error" )
475
+ }
476
+
477
+ // Wait for the pipelines to stop
478
+ const (
479
+ count = 6
480
+ interval = exitTimeout / count
481
+ )
482
+
483
+ pipelinesStopped := make (chan struct {})
484
+ go func () {
485
+ for i := count ; i > 0 ; i -- {
486
+ if i == 1 {
487
+ // on last try, stop forcefully
488
+ _ = ls .StopAll (ctx , true )
489
+ }
490
+
491
+ r .logger .Info (ctx ).Msgf ("waiting for pipelines to stop running (time left: %s)" , time .Duration (i )* interval )
492
+ select {
493
+ case <- time .After (interval ):
494
+ case <- pipelinesStopped :
495
+ return
496
+ }
497
+ }
498
+ }()
499
+
500
+ err = ls .Wait (exitTimeout )
501
+ switch {
502
+ case err != nil && err != context .DeadlineExceeded :
503
+ r .logger .Warn (ctx ).Err (err ).Msg ("some pipelines stopped with an error" )
504
+ case err == context .DeadlineExceeded :
505
+ r .logger .Warn (ctx ).Msg ("some pipelines did not stop in time" )
506
+ default :
507
+ r .logger .Info (ctx ).Msg ("all pipelines stopped gracefully" )
508
+ }
509
+
510
+ pipelinesStopped <- struct {}{}
511
+
512
+ t .Go (func () error {
513
+ r .connectorPersister .Wait ()
514
+ return r .DB .Close ()
515
+ })
516
+
517
+ return nil
518
+ })
519
+ }
520
+
440
521
func (r * Runtime ) newHTTPMetricsHandler () http.Handler {
441
522
return promhttp .Handler ()
442
523
}
@@ -770,13 +851,25 @@ func (r *Runtime) initServices(ctx context.Context, t *tomb.Tomb) error {
770
851
}
771
852
772
853
if r .Config .Pipelines .ExitOnDegraded {
773
- r .lifecycleService .OnFailure (func (e lifecycle.FailureEvent ) {
774
- r .logger .Warn (ctx ).
775
- Err (e .Error ).
776
- Str (log .PipelineIDField , e .ID ).
777
- Msg ("Conduit will shut down due to a pipeline failure and 'exit-on-degraded' enabled" )
778
- t .Kill (cerrors .Errorf ("shut down due to 'exit-on-degraded' error: %w" , e .Error ))
779
- })
854
+ if r .Config .Preview .PipelineArchV2 {
855
+ ls := r .lifecycleService .(* lifecycle_v2.Service )
856
+ ls .OnFailure (func (e lifecycle_v2.FailureEvent ) {
857
+ r .logger .Warn (ctx ).
858
+ Err (e .Error ).
859
+ Str (log .PipelineIDField , e .ID ).
860
+ Msg ("Conduit will shut down due to a pipeline failure and 'exit-on-degraded' enabled" )
861
+ t .Kill (cerrors .Errorf ("shut down due to 'exit-on-degraded' error: %w" , e .Error ))
862
+ })
863
+ } else {
864
+ ls := r .lifecycleService .(* lifecycle.Service )
865
+ ls .OnFailure (func (e lifecycle.FailureEvent ) {
866
+ r .logger .Warn (ctx ).
867
+ Err (e .Error ).
868
+ Str (log .PipelineIDField , e .ID ).
869
+ Msg ("Conduit will shut down due to a pipeline failure and 'exit-on-degraded' enabled" )
870
+ t .Kill (cerrors .Errorf ("shut down due to 'exit-on-degraded' error: %w" , e .Error ))
871
+ })
872
+ }
780
873
}
781
874
err = r .pipelineService .Init (ctx )
782
875
if err != nil {
0 commit comments