Skip to content

Commit 531a36e

Browse files
authored
Merge pull request #319 from thand-io/fix/temporal-register-before-start
Fix Temporal worker startup order
2 parents 4bff86e + ab1d80b commit 531a36e

7 files changed

Lines changed: 82 additions & 23 deletions

File tree

internal/config/providers.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ type initResult struct {
187187

188188
// InitializeProviders initializes all providers in parallel using channels
189189
func (c *Config) InitializeProviders() error {
190+
if c.IsServer() {
191+
_ = c.GetServices()
192+
if err := c.SetupTemporal(); err != nil {
193+
return fmt.Errorf("setting up temporal services: %w", err)
194+
}
195+
}
190196

191197
defs := c.GetProviders().Definitions
192198

@@ -364,6 +370,10 @@ func (c *Config) InitializeProviders() error {
364370
c.providerInstances = results
365371
c.mu.Unlock()
366372

373+
if err := c.StartTemporalWorkers(); err != nil {
374+
return err
375+
}
376+
367377
logrus.Debugln("All providers initialized successfully")
368378

369379
return nil

internal/config/services.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,14 @@ func (c *Config) GetServices() models.ServicesClientImpl {
1818
return
1919
}
2020
c.servicesClient = newClient
21-
go func() {
22-
// Post services setup initialization, we need to do some additional setup for certain services that are dependent on the configuration being fully loaded.
23-
err = c.SetupTemporal()
24-
if err != nil {
25-
logrus.WithError(err).Error("Failed to set up temporal services")
26-
}
27-
}()
2821
})
2922

3023
return c.servicesClient
3124

3225
}
3326

3427
func (c *Config) SetupTemporal() error {
35-
36-
if c.GetServices() != nil && c.GetServices().GetTemporal() != nil {
28+
if c.servicesClient != nil && c.servicesClient.GetTemporal() != nil {
3729

3830
logrus.Infoln("Setting up temporal services...")
3931

@@ -60,3 +52,19 @@ func (c *Config) SetupTemporal() error {
6052
return nil
6153

6254
}
55+
56+
func (c *Config) StartTemporalWorkers() error {
57+
if c.servicesClient == nil || c.servicesClient.GetTemporal() == nil {
58+
return nil
59+
}
60+
61+
if !c.IsServer() {
62+
return nil
63+
}
64+
65+
if err := c.servicesClient.GetTemporal().StartWorkers(); err != nil {
66+
return fmt.Errorf("starting temporal workers: %w", err)
67+
}
68+
69+
return nil
70+
}

internal/config/services/temporal/main.go

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type TemporalClient struct {
3333
mu sync.Mutex
3434
readyCh chan struct{}
3535
closeReadyOnce sync.Once
36+
workersStarted bool
3637
}
3738

3839
func NewTemporalClient(
@@ -131,7 +132,8 @@ func (a *TemporalClient) Initialize() error {
131132
}
132133
}
133134

134-
// Create and start a worker for each identity (task queue)
135+
// Create a worker for each identity (task queue).
136+
// Registration must happen before workers are started.
135137
a.mu.Lock()
136138
defer a.mu.Unlock()
137139

@@ -147,32 +149,70 @@ func (a *TemporalClient) Initialize() error {
147149
workerOptions,
148150
)
149151

152+
a.workers[identity] = newWorker
153+
}
154+
155+
if len(a.workers) == 0 {
156+
a.markReady() // Unblock any waiters even on failure
157+
return fmt.Errorf("failed to create any Temporal workers")
158+
}
159+
160+
return nil
161+
}
162+
163+
// StartWorkers starts all registered Temporal workers.
164+
// This must be called only after workflow/activity registration is complete.
165+
func (c *TemporalClient) StartWorkers() error {
166+
c.mu.Lock()
167+
defer c.mu.Unlock()
168+
169+
if c.client == nil {
170+
return fmt.Errorf("temporal client is not initialized")
171+
}
172+
173+
if len(c.workers) == 0 {
174+
c.markReady()
175+
return fmt.Errorf("no Temporal workers configured")
176+
}
177+
178+
if c.workersStarted {
179+
logrus.Warn("Temporal workers already started, skipping worker startup")
180+
return nil
181+
}
182+
183+
buildID := common.GetBuildIdentifier()
184+
startedCount := 0
185+
186+
for identity, w := range c.workers {
150187
logrus.WithFields(logrus.Fields{
151188
"BuildID": buildID,
152189
"taskQueue": identity,
153-
}).Infof("Starting Temporal worker")
190+
}).Info("Starting Temporal worker")
154191

155-
if err := newWorker.Start(); err != nil {
192+
if err := w.Start(); err != nil {
156193
logrus.WithError(err).
157194
WithField("taskQueue", identity).
158195
Error("Failed to start temporal worker")
196+
delete(c.workers, identity)
159197
continue
160198
}
161199

162-
a.workers[identity] = newWorker
200+
startedCount++
163201
}
164202

165-
if len(a.workers) == 0 {
166-
a.markReady() // Unblock any waiters even on failure
203+
if startedCount == 0 {
204+
c.markReady()
167205
return fmt.Errorf("failed to start any Temporal workers")
168206
}
169207

208+
c.workersStarted = true
209+
170210
// If versioning is enabled, confirm our deployment version is registered
171211
// on the Temporal server before allowing workflow submissions via GetClient().
172-
if a.config.DisableVersioning {
173-
a.markReady()
212+
if c.config.DisableVersioning {
213+
c.markReady()
174214
} else {
175-
go a.awaitVersionRegistration(buildID)
215+
go c.awaitVersionRegistration(buildID)
176216
}
177217

178218
return nil
@@ -335,6 +375,7 @@ func (c *TemporalClient) Shutdown() error {
335375

336376
c.workers = nil
337377
c.client = nil
378+
c.workersStarted = false
338379

339380
return nil
340381
}

internal/config/services/temporal/multi_worker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
// identity-specific task queues.
1414
//
1515
// Lifecycle methods (Start/Run/Stop) are no-ops because TemporalClient
16-
// manages worker lifecycle directly in Initialize() and Shutdown().
16+
// manages worker lifecycle directly via StartWorkers() and Shutdown().
1717
type multiWorker struct {
1818
workers []worker.Worker
1919
}

internal/config/temporal.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import (
1010

1111
// Register temporal workflows and activities
1212
func (c *Config) registerTemporalWorkflows() error {
13-
14-
if c.GetServices() == nil || c.GetServices().GetTemporal() == nil {
13+
if c.servicesClient == nil || c.servicesClient.GetTemporal() == nil {
1514
return fmt.Errorf("temporal service is not initialized")
1615
}
1716

@@ -26,8 +25,7 @@ func (c *Config) registerTemporalWorkflows() error {
2625
}
2726

2827
func (c *Config) registerTemporalActivities() error {
29-
30-
if c.GetServices() == nil || c.GetServices().GetTemporal() == nil {
28+
if c.servicesClient == nil || c.servicesClient.GetTemporal() == nil {
3129
return fmt.Errorf("temporal service is not initialized")
3230
}
3331

internal/models/temporal.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func (t *TemporalConfig) HasMtlsConfig() bool {
6161

6262
type TemporalImpl interface {
6363
Initialize() error
64+
StartWorkers() error
6465
Shutdown() error
6566

6667
GetClient() client.Client

test/integration/services/temporal_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func initAndRegister(t *testing.T, infra *testinfra.TestInfrastructure, cfg *mod
6161
require.NotNil(t, w, "GetWorker must return a worker after Initialize")
6262
w.RegisterWorkflow(echoWorkflow)
6363
w.RegisterActivity(echoActivity)
64+
require.NoError(t, tc.StartWorkers(), "StartWorkers should succeed")
6465
return tc
6566
}
6667

0 commit comments

Comments
 (0)