Skip to content

Commit 864d8e0

Browse files
committed
Add single worker mode
1 parent ae35998 commit 864d8e0

7 files changed

Lines changed: 131 additions & 17 deletions

File tree

internal/contextvalue/registry.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package contextvalue
2+
3+
import (
4+
"github.com/cschleiden/go-workflows/internal/sync"
5+
"github.com/cschleiden/go-workflows/registry"
6+
)
7+
8+
type registryKey struct{}
9+
10+
func WithRegistry(ctx sync.Context, r *registry.Registry) sync.Context {
11+
return sync.WithValue(ctx, registryKey{}, r)
12+
}
13+
14+
func GetRegistry(ctx sync.Context) *registry.Registry {
15+
if v := ctx.Value(registryKey{}); v != nil {
16+
return v.(*registry.Registry)
17+
}
18+
19+
return nil
20+
}

registry/registry.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,28 @@ import (
88

99
"github.com/cschleiden/go-workflows/internal/args"
1010
"github.com/cschleiden/go-workflows/internal/fn"
11-
wf "github.com/cschleiden/go-workflows/workflow"
1211
)
1312

1413
type Registry struct {
1514
sync.Mutex
1615

17-
workflowMap map[string]wf.Workflow
18-
activityMap map[string]interface{}
16+
workflowMap map[string]any
17+
activityMap map[string]any
1918
}
2019

2120
// New creates a new registry instance.
2221
func New() *Registry {
2322
return &Registry{
24-
workflowMap: make(map[string]wf.Workflow),
25-
activityMap: make(map[string]interface{}),
23+
workflowMap: make(map[string]any),
24+
activityMap: make(map[string]any),
2625
}
2726
}
2827

2928
type registerConfig struct {
3029
Name string
3130
}
3231

33-
func (r *Registry) RegisterWorkflow(workflow wf.Workflow, opts ...RegisterOption) error {
32+
func (r *Registry) RegisterWorkflow(workflow any, opts ...RegisterOption) error {
3433
cfg := registerOptions(opts).applyRegisterOptions(registerConfig{})
3534
name := cfg.Name
3635
if name == "" {
@@ -75,7 +74,7 @@ func (r *Registry) RegisterWorkflow(workflow wf.Workflow, opts ...RegisterOption
7574
return nil
7675
}
7776

78-
func (r *Registry) RegisterActivity(activity wf.Activity, opts ...RegisterOption) error {
77+
func (r *Registry) RegisterActivity(activity any, opts ...RegisterOption) error {
7978
cfg := registerOptions(opts).applyRegisterOptions(registerConfig{})
8079

8180
t := reflect.TypeOf(activity)
@@ -151,7 +150,7 @@ func checkActivity(actType reflect.Type) error {
151150
return nil
152151
}
153152

154-
func (r *Registry) GetWorkflow(name string) (wf.Workflow, error) {
153+
func (r *Registry) GetWorkflow(name string) (any, error) {
155154
r.Lock()
156155
defer r.Unlock()
157156

registry/registry_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"github.com/cschleiden/go-workflows/internal/fn"
88
"github.com/cschleiden/go-workflows/internal/sync"
9-
wf "github.com/cschleiden/go-workflows/workflow"
109
"github.com/stretchr/testify/require"
1110
)
1211

@@ -17,7 +16,7 @@ func reg_workflow1(ctx sync.Context) error {
1716
func TestRegistry_RegisterWorkflow(t *testing.T) {
1817
type args struct {
1918
name string
20-
workflow wf.Workflow
19+
workflow any
2120
}
2221
tests := []struct {
2322
name string

worker/workflow_orchestrator.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package worker
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/cschleiden/go-workflows/backend"
9+
"github.com/cschleiden/go-workflows/client"
10+
"github.com/cschleiden/go-workflows/internal/fn"
11+
"github.com/cschleiden/go-workflows/registry"
12+
"github.com/cschleiden/go-workflows/workflow"
13+
)
14+
15+
// WorkflowOrchestrator combines a worker and client into a single entity.
16+
// It orchestrates the entire workflow lifecycle, from creation to execution.
17+
type WorkflowOrchestrator struct {
18+
worker *Worker
19+
Client *client.Client // Exposed for direct access to GetWorkflowResult
20+
registry *registry.Registry
21+
}
22+
23+
// NewWorkflowOrchestrator creates a new orchestrator with client capabilities and optional registration.
24+
func NewWorkflowOrchestrator(backend backend.Backend, options *Options) *WorkflowOrchestrator {
25+
if options == nil {
26+
options = &DefaultOptions
27+
}
28+
29+
// Create registry that will be shared between worker and orchestrator
30+
reg := registry.New()
31+
32+
// Create a regular worker with the registry
33+
workflowWorker := newWorkflowWorker(backend, reg, &options.WorkflowWorkerOptions)
34+
activityWorker := newActivityWorker(backend, reg, &options.ActivityWorkerOptions)
35+
w := newWorker(backend, reg, []worker{workflowWorker, activityWorker})
36+
c := client.New(backend)
37+
38+
// Create orchestrator that combines both
39+
orchestrator := &WorkflowOrchestrator{
40+
worker: w,
41+
Client: c,
42+
registry: reg,
43+
}
44+
45+
// No automatic registration - will be done on-demand
46+
47+
return orchestrator
48+
}
49+
50+
// Start starts the worker.
51+
func (o *WorkflowOrchestrator) Start(ctx context.Context) error {
52+
return o.worker.Start(ctx)
53+
}
54+
55+
// WaitForCompletion waits for the worker to complete processing.
56+
func (o *WorkflowOrchestrator) WaitForCompletion() error {
57+
return o.worker.WaitForCompletion()
58+
}
59+
60+
// CreateWorkflowInstance creates a new workflow instance using the client.
61+
// Automatically registers the workflow if it's not already registered.
62+
func (o *WorkflowOrchestrator) CreateWorkflowInstance(ctx context.Context, options client.WorkflowInstanceOptions, wf workflow.Workflow, args ...any) (*workflow.Instance, error) {
63+
// Check if the workflow is a function (not a string name) and register it if needed
64+
if _, ok := wf.(string); !ok {
65+
// It's a function reference, try to register it if not already registered
66+
name := fn.Name(wf)
67+
_, err := o.registry.GetWorkflow(name)
68+
if err != nil {
69+
// Workflow not found in registry, register it directly
70+
if err := o.worker.RegisterWorkflow(wf); err != nil {
71+
return nil, fmt.Errorf("auto-registering workflow %s: %w", name, err)
72+
}
73+
}
74+
}
75+
76+
return o.Client.CreateWorkflowInstance(ctx, options, wf, args...)
77+
}
78+
79+
// WaitForWorkflowInstance waits for a workflow instance to complete.
80+
func (o *WorkflowOrchestrator) WaitForWorkflowInstance(ctx context.Context, instance *workflow.Instance, timeout time.Duration) error {
81+
return o.Client.WaitForWorkflowInstance(ctx, instance, timeout)
82+
}
83+
84+
// Note: Use client.GetWorkflowResult directly with the embedded client:
85+
// result, err := client.GetWorkflowResult[ResultType](ctx, orchestrator.client, instance, timeout)
86+
87+
// SignalWorkflow signals a workflow instance.
88+
func (o *WorkflowOrchestrator) SignalWorkflow(ctx context.Context, instanceID string, name string, arg any) error {
89+
return o.Client.SignalWorkflow(ctx, instanceID, name, arg)
90+
}
91+
92+
// RemoveWorkflowInstance removes a workflow instance.
93+
func (o *WorkflowOrchestrator) RemoveWorkflowInstance(ctx context.Context, instance *workflow.Instance) error {
94+
return o.Client.RemoveWorkflowInstance(ctx, instance)
95+
}

workflow/executor/executor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ type executor struct {
8181
func NewExecutor(
8282
logger *slog.Logger,
8383
tracer trace.Tracer,
84-
registry *registry.Registry,
84+
r *registry.Registry,
8585
cv converter.Converter,
8686
propagators []wf.ContextPropagator,
8787
historyProvider WorkflowHistoryProvider,
@@ -96,6 +96,7 @@ func NewExecutor(
9696
wfCtx = contextvalue.WithConverter(wfCtx, cv)
9797
wfCtx = workflowstate.WithWorkflowState(wfCtx, s)
9898
wfCtx = sync.WithValue(wfCtx, contextvalue.PropagatorsCtxKey, propagators)
99+
wfCtx = contextvalue.WithRegistry(wfCtx, r)
99100
wfCtx, cancel := sync.WithCancel(wfCtx)
100101

101102
// As part of this, the default tracing propagator will run, and set the parent span
@@ -114,7 +115,7 @@ func NewExecutor(
114115
)
115116

116117
return &executor{
117-
registry: registry,
118+
registry: r,
118119
historyProvider: historyProvider,
119120
workflowState: s,
120121
workflowCtx: wfCtx,

workflow/executor/executor_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,10 @@ func Test_Executor(t *testing.T) {
278278

279279
sync.Select(
280280
ctx,
281-
sync.Await[int](f1, func(ctx sync.Context, f sync.Future[int]) {
281+
sync.Await(f1, func(ctx sync.Context, f sync.Future[int]) {
282282
workflowWithSelectorHits++
283283
}),
284-
sync.Await[any](t, func(ctx sync.Context, _ sync.Future[any]) {
284+
sync.Await(t, func(ctx sync.Context, _ sync.Future[any]) {
285285
workflowWithSelectorHits++
286286
}),
287287
)

workflow/select.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@ func Select(ctx Context, cases ...SelectCase) {
1111

1212
// Await calls the provided handler when the given future is ready.
1313
func Await[T any](f Future[T], handler func(Context, Future[T])) SelectCase {
14-
return sync.Await[T](f, func(ctx sync.Context, f sync.Future[T]) {
14+
return sync.Await(f, func(ctx sync.Context, f sync.Future[T]) {
1515
handler(ctx, f)
1616
})
1717
}
1818

1919
// Receive calls the provided handler if the given channel can receive a value. The handler receives
2020
// the received value, and the ok flag indicating whether the value was received or the channel was closed.
2121
func Receive[T any](c Channel[T], handler func(ctx Context, v T, ok bool)) SelectCase {
22-
return sync.Receive[T](c, handler)
22+
return sync.Receive(c, handler)
2323
}
2424

2525
// Send calls the provided handler if the given value can be sent to the channel.
2626
func Send[T any](c Channel[T], value *T, handler func(ctx Context)) SelectCase {
27-
return sync.Send[T](c, value, handler)
27+
return sync.Send(c, value, handler)
2828
}
2929

3030
// Default calls the provided handler if none of the other cases match.

0 commit comments

Comments
 (0)