Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ jobs:

sudo update-ca-certificates

- name: Run Temporal plugin tests
run: |
go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=github.com/temporalio/roadrunner-temporal/v5/... -coverprofile=./tests/coverage-ci/rrt_p.out -covermode=atomic .

- name: Run Temporal canceller module tests
run: |
go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=github.com/temporalio/roadrunner-temporal/v6/... -coverprofile=./tests/coverage-ci/rrt_c.out -covermode=atomic ./canceller/...
Expand Down
12 changes: 4 additions & 8 deletions internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ func (p *Plugin) initPool() error {
}
}

p.temporal.rrWorkflowDef = wfDef
p.temporal.rrActivityDef = actDef
p.temporal.rrWorkflowDef.Store(wfDef)
p.temporal.rrActivityDef.Store(actDef)
Comment thread
rustatian marked this conversation as resolved.
p.temporal.workers = workers
p.codec = codec

Expand All @@ -140,15 +140,11 @@ func (p *Plugin) initPool() error {
}

func (p *Plugin) getWfDef() *aggregatedpool.Workflow {
p.mu.RLock()
defer p.mu.RUnlock()
return p.temporal.rrWorkflowDef
return p.temporal.rrWorkflowDef.Load()
}

func (p *Plugin) getActDef() *aggregatedpool.Activity {
p.mu.RLock()
defer p.mu.RUnlock()
return p.temporal.rrActivityDef
return p.temporal.rrActivityDef.Load()
}

func (p *Plugin) initTemporalClient(phpSdkVersion string, flags map[string]string, dc converter.DataConverter) error {
Expand Down
8 changes: 4 additions & 4 deletions plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ type Logger interface {

// temporal structure contains temporal specific structures
type temporal struct {
rrActivityDef *aggregatedpool.Activity
rrWorkflowDef *aggregatedpool.Workflow
rrActivityDef atomic.Pointer[aggregatedpool.Activity]
rrWorkflowDef atomic.Pointer[aggregatedpool.Workflow]
workflows map[string]*internal.WorkflowInfo
activities map[string]*internal.ActivityInfo
mh tclient.MetricsHandler
Expand Down Expand Up @@ -360,8 +360,8 @@ func (p *Plugin) Reset() error {

// based on the worker info -> initialize workers
workers, err := aggregatedpool.TemporalWorkers(
p.temporal.rrWorkflowDef,
p.temporal.rrActivityDef,
p.temporal.rrWorkflowDef.Load(),
p.temporal.rrActivityDef.Load(),
wi,
p.log,
p.temporal.client,
Expand Down
103 changes: 103 additions & 0 deletions plugin_stop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package rrtemporal

import (
"context"
"log/slog"
"sync"
"testing"
"time"

"github.com/roadrunner-server/events"
"github.com/stretchr/testify/require"
"github.com/temporalio/roadrunner-temporal/v6/internal"
"go.temporal.io/sdk/worker"
)

const stopTestTimeout = 5 * time.Second

type blockingWorker struct {
worker.Worker

stopping chan struct{}
release chan struct{}
}

func (b *blockingWorker) Stop() {
close(b.stopping)
<-b.release
}

type drainingPlugin struct {
plugin *Plugin
release func()
stopped chan error
}

// startDraining leaves Stop blocked inside the shutdown, holding p.mu, which is
// where a heartbeat from a still-running activity arrives.
func startDraining(t *testing.T) *drainingPlugin {
t.Helper()

w := &blockingWorker{
stopping: make(chan struct{}),
release: make(chan struct{}),
}

p := &Plugin{
log: slog.New(slog.DiscardHandler),
stopCh: make(chan struct{}, 1),
temporal: &temporal{
activities: map[string]*internal.ActivityInfo{},
workflows: map[string]*internal.WorkflowInfo{},
workers: []worker.Worker{w},
},
}
p.eventBus, p.id = events.NewEventBus()

var once sync.Once
release := func() { once.Do(func() { close(w.release) }) }
t.Cleanup(release)

stopped := make(chan error, 1)
go func() { stopped <- p.Stop(context.Background()) }()

select {
case <-w.stopping:
case <-time.After(stopTestTimeout):
require.FailNow(t, "Stop did not reach the worker drain")
}

return &drainingPlugin{plugin: p, release: release, stopped: stopped}
}

func (d *drainingPlugin) finish(t *testing.T) {
t.Helper()

d.release()

select {
case err := <-d.stopped:
require.NoError(t, err)
case <-time.After(stopTestTimeout):
require.FailNow(t, "Stop did not return after the drain finished")
}
}

func TestDefinitionsAreReadableWhileStopping(t *testing.T) {
d := startDraining(t)

done := make(chan struct{})
go func() {
d.plugin.getActDef()
d.plugin.getWfDef()
close(done)
}()

select {
case <-done:
case <-time.After(stopTestTimeout):
require.FailNow(t, "reading the activity definition blocked while the plugin was stopping")
}

d.finish(t)
}
5 changes: 1 addition & 4 deletions rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,10 @@ func (r *rpc) RecordActivityHeartbeat(in RecordHeartbeatRequest, out *RecordHear
}

// find running activity
r.plugin.mu.RLock()
ctx, err := r.plugin.temporal.rrActivityDef.GetActivityContext(in.TaskToken)
ctx, err := r.plugin.getActDef().GetActivityContext(in.TaskToken)
if err != nil {
r.plugin.mu.RUnlock()
return err
}
r.plugin.mu.RUnlock()

activity.RecordHeartbeat(ctx, details)

Expand Down
Loading