Skip to content

Commit

Permalink
IWF-274: continue as new should complete timer spawning goroutine
Browse files Browse the repository at this point in the history
  • Loading branch information
jbowers committed Dec 21, 2024
1 parent 3a6b2d5 commit a6399df
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package interpreter
package config

import (
"github.com/indeedeng/iwf/gen/iwfidl"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package interpreter
package cont

import "github.com/indeedeng/iwf/service/interpreter/interfaces"
import (
"github.com/indeedeng/iwf/service/interpreter/config"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
)

type ContinueAsNewCounter struct {
executedStateApis int32
signalsReceived int32
syncUpdateReceived int32
triggeredByAPI bool

configer *WorkflowConfiger
configer *config.WorkflowConfiger
rootCtx interfaces.UnifiedContext
provider interfaces.WorkflowProvider
}

func NewContinueAsCounter(
configer *WorkflowConfiger, rootCtx interfaces.UnifiedContext, provider interfaces.WorkflowProvider,
configer *config.WorkflowConfiger, rootCtx interfaces.UnifiedContext, provider interfaces.WorkflowProvider,
) *ContinueAsNewCounter {
return &ContinueAsNewCounter{
configer: configer,
Expand Down
3 changes: 2 additions & 1 deletion service/interpreter/queryHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package interpreter
import (
"github.com/indeedeng/iwf/gen/iwfidl"
"github.com/indeedeng/iwf/service"
"github.com/indeedeng/iwf/service/interpreter/config"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
)

func SetQueryHandlers(
ctx interfaces.UnifiedContext, provider interfaces.WorkflowProvider, persistenceManager *PersistenceManager,
internalChannel *InternalChannel, signalReceiver *SignalReceiver,
continueAsNewer *ContinueAsNewer,
workflowConfiger *WorkflowConfiger, basicInfo service.BasicInfo,
workflowConfiger *config.WorkflowConfiger, basicInfo service.BasicInfo,
) error {
err := provider.SetQueryHandler(ctx, service.GetDataAttributesWorkflowQueryType, func(req service.GetDataAttributesQueryRequest) (service.GetDataAttributesQueryResponse, error) {
dos := persistenceManager.GetDataObjectsByKey(req)
Expand Down
8 changes: 5 additions & 3 deletions service/interpreter/signalReceiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package interpreter

import (
"github.com/indeedeng/iwf/service/common/ptr"
"github.com/indeedeng/iwf/service/interpreter/config"
"github.com/indeedeng/iwf/service/interpreter/cont"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
"strings"

Expand All @@ -16,7 +18,7 @@ type SignalReceiver struct {
reasonFailWorkflowByClient *string
provider interfaces.WorkflowProvider
timerProcessor interfaces.TimerProcessor
workflowConfiger *WorkflowConfiger
workflowConfiger *config.WorkflowConfiger
interStateChannel *InternalChannel
stateRequestQueue *StateRequestQueue
persistenceManager *PersistenceManager
Expand All @@ -25,8 +27,8 @@ type SignalReceiver struct {
func NewSignalReceiver(
ctx interfaces.UnifiedContext, provider interfaces.WorkflowProvider, interStateChannel *InternalChannel,
stateRequestQueue *StateRequestQueue,
persistenceManager *PersistenceManager, tp interfaces.TimerProcessor, continueAsNewCounter *ContinueAsNewCounter,
workflowConfiger *WorkflowConfiger,
persistenceManager *PersistenceManager, tp interfaces.TimerProcessor, continueAsNewCounter *cont.ContinueAsNewCounter,
workflowConfiger *config.WorkflowConfiger,
initReceivedSignals map[string][]*iwfidl.EncodedObject,
) *SignalReceiver {
if initReceivedSignals == nil {
Expand Down
10 changes: 6 additions & 4 deletions service/interpreter/stateExecutionCounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"github.com/indeedeng/iwf/service"
"github.com/indeedeng/iwf/service/common/compatibility"
"github.com/indeedeng/iwf/service/common/ptr"
"github.com/indeedeng/iwf/service/interpreter/config"
"github.com/indeedeng/iwf/service/interpreter/cont"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
"reflect"
"slices"
Expand All @@ -14,9 +16,9 @@ import (
type StateExecutionCounter struct {
ctx interfaces.UnifiedContext
provider interfaces.WorkflowProvider
configer *WorkflowConfiger
configer *config.WorkflowConfiger
globalVersioner *GlobalVersioner
continueAsNewCounter *ContinueAsNewCounter
continueAsNewCounter *cont.ContinueAsNewCounter

stateIdCompletedCounts map[string]int
stateIdStartedCounts map[string]int // For creating stateExecutionId: count the stateId for how many times that have been executed
Expand All @@ -26,7 +28,7 @@ type StateExecutionCounter struct {

func NewStateExecutionCounter(
ctx interfaces.UnifiedContext, provider interfaces.WorkflowProvider, globalVersioner *GlobalVersioner,
configer *WorkflowConfiger, continueAsNewCounter *ContinueAsNewCounter,
configer *config.WorkflowConfiger, continueAsNewCounter *cont.ContinueAsNewCounter,
) *StateExecutionCounter {
return &StateExecutionCounter{
ctx: ctx,
Expand All @@ -44,7 +46,7 @@ func RebuildStateExecutionCounter(
ctx interfaces.UnifiedContext, provider interfaces.WorkflowProvider, globalVersioner *GlobalVersioner,
stateIdStartedCounts map[string]int, stateIdCurrentlyExecutingCounts map[string]int,
totalCurrentlyExecutingCount int,
configer *WorkflowConfiger, continueAsNewCounter *ContinueAsNewCounter,
configer *config.WorkflowConfiger, continueAsNewCounter *cont.ContinueAsNewCounter,
) *StateExecutionCounter {
return &StateExecutionCounter{
ctx: ctx,
Expand Down
35 changes: 30 additions & 5 deletions service/interpreter/timers/greedyTimerProcessor.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package timers

import (
"github.com/indeedeng/iwf/service/interpreter/cont"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
"time"

Expand All @@ -23,7 +24,10 @@ type GreedyTimerProcessor struct {
}

func NewGreedyTimerProcessor(
ctx interfaces.UnifiedContext, provider interfaces.WorkflowProvider, staleSkipTimerSignals []service.StaleSkipTimerSignal,
ctx interfaces.UnifiedContext,
provider interfaces.WorkflowProvider,
continueAsNewCounter *cont.ContinueAsNewCounter,
staleSkipTimerSignals []service.StaleSkipTimerSignal,
) *GreedyTimerProcessor {

tp := &GreedyTimerProcessor{
Expand All @@ -35,7 +39,7 @@ func NewGreedyTimerProcessor(
}

// start some single thread that manages timers
tp.createGreedyTimerScheduler(ctx)
tp.createGreedyTimerScheduler(ctx, continueAsNewCounter)

err := provider.SetQueryHandler(ctx, service.GetCurrentTimerInfosQueryType, func() (service.GetCurrentTimerInfosQueryResponse, error) {
return service.GetCurrentTimerInfosQueryResponse{
Expand All @@ -57,6 +61,10 @@ func (t *sortedTimers) addTimer(toAdd *service.TimerInfo) {
insertIndex := 0
for i, timer := range t.timers {
if toAdd.FiringUnixTimestampSeconds >= timer.FiringUnixTimestampSeconds {
// don't want dupes. Makes remove simpler
if toAdd == timer {
return
}
insertIndex = i
break
}
Expand All @@ -67,6 +75,15 @@ func (t *sortedTimers) addTimer(toAdd *service.TimerInfo) {
append([]*service.TimerInfo{toAdd}, t.timers[insertIndex:]...)...)
}

func (t *sortedTimers) removeTimer(toRemove *service.TimerInfo) {
for i, timer := range t.timers {
if toRemove == timer {
t.timers = append(t.timers[:i], t.timers[i+1:]...)
return
}
}
}

func (t *sortedTimers) pruneToNextTimer(pruneTo int64) *service.TimerInfo {

if len(t.timers) == 0 {
Expand All @@ -86,7 +103,9 @@ func (t *sortedTimers) pruneToNextTimer(pruneTo int64) *service.TimerInfo {
return t.timers[index-1]
}

func (t *GreedyTimerProcessor) createGreedyTimerScheduler(ctx interfaces.UnifiedContext) {
func (t *GreedyTimerProcessor) createGreedyTimerScheduler(
ctx interfaces.UnifiedContext,
continueAsNewCounter *cont.ContinueAsNewCounter) {

t.provider.GoNamed(ctx, "greedy-timer-scheduler", func(ctx interfaces.UnifiedContext) {
// NOTE: next timer to fire is at the end of the slice
Expand All @@ -102,9 +121,13 @@ func (t *GreedyTimerProcessor) createGreedyTimerScheduler(ctx interfaces.Unified
}
}
next := t.pendingTimers.pruneToNextTimer(now)
return next != nil && (len(createdTimers) == 0 || next.FiringUnixTimestampSeconds < createdTimers[len(createdTimers)-1])
return (next != nil && (len(createdTimers) == 0 || next.FiringUnixTimestampSeconds < createdTimers[len(createdTimers)-1])) || continueAsNewCounter.IsThresholdMet()
})

if continueAsNewCounter.IsThresholdMet() {
break
}

now := t.provider.Now(ctx).Unix()
next := t.pendingTimers.pruneToNextTimer(now)
//next := t.pendingTimers.getEarliestTimer()
Expand Down Expand Up @@ -194,10 +217,12 @@ func (t *GreedyTimerProcessor) WaitForTimerFiredOrSkipped(
}

if timer.FiringUnixTimestampSeconds >= t.provider.Now(ctx).Unix() {
timer.Status = service.TimerFired
return service.TimerFired
}

// otherwise *cancelWaiting should return false to indicate that this timer isn't completed(fired or skipped)
t.pendingTimers.removeTimer(timer)
return service.TimerPending
}

Expand All @@ -207,7 +232,7 @@ func (t *GreedyTimerProcessor) RemovePendingTimersOfState(stateExeId string) {
timers := t.stateExecutionCurrentTimerInfos[stateExeId]

for _, timer := range timers {
timer.Status = service.TimerSkipped
t.pendingTimers.removeTimer(timer)
}

delete(t.stateExecutionCurrentTimerInfos, stateExeId)
Expand Down
20 changes: 11 additions & 9 deletions service/interpreter/workflowImpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/indeedeng/iwf/service/common/event"
"github.com/indeedeng/iwf/service/common/ptr"
"github.com/indeedeng/iwf/service/common/utils"
"github.com/indeedeng/iwf/service/interpreter/config"
"github.com/indeedeng/iwf/service/interpreter/cont"
"github.com/indeedeng/iwf/service/interpreter/env"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
"github.com/indeedeng/iwf/service/interpreter/timers"
Expand Down Expand Up @@ -72,7 +74,7 @@ func InterpreterImpl(
}
}

workflowConfiger := NewWorkflowConfiger(input.Config)
workflowConfiger := config.NewWorkflowConfiger(input.Config)
basicInfo := service.BasicInfo{
IwfWorkflowType: input.IwfWorkflowType,
IwfWorkerUrl: input.IwfWorkerUrl,
Expand All @@ -82,7 +84,7 @@ func InterpreterImpl(
var stateRequestQueue *StateRequestQueue
var persistenceManager *PersistenceManager
var timerProcessor interfaces.TimerProcessor
var continueAsNewCounter *ContinueAsNewCounter
var continueAsNewCounter *cont.ContinueAsNewCounter
var signalReceiver *SignalReceiver
var stateExecutionCounter *StateExecutionCounter
var outputCollector *OutputCollector
Expand All @@ -101,12 +103,12 @@ func InterpreterImpl(
internalChannel = RebuildInternalChannel(previous.InterStateChannelReceived)
stateRequestQueue = NewStateRequestQueueWithResumeRequests(previous.StatesToStartFromBeginning, previous.StateExecutionsToResume)
persistenceManager = RebuildPersistenceManager(provider, previous.DataObjects, previous.SearchAttributes, input.UseMemoForDataAttributes)
continueAsNewCounter = cont.NewContinueAsCounter(workflowConfiger, ctx, provider)
if input.Config.GetOptimizeTimer() {
timerProcessor = timers.NewGreedyTimerProcessor(ctx, provider, previous.StaleSkipTimerSignals)
timerProcessor = timers.NewGreedyTimerProcessor(ctx, provider, continueAsNewCounter, previous.StaleSkipTimerSignals)
} else {
timerProcessor = timers.NewSimpleTimerProcessor(ctx, provider, previous.StaleSkipTimerSignals)
}
continueAsNewCounter = NewContinueAsCounter(workflowConfiger, ctx, provider)
signalReceiver = NewSignalReceiver(ctx, provider, internalChannel, stateRequestQueue, persistenceManager, timerProcessor, continueAsNewCounter, workflowConfiger, previous.SignalsReceived)
counterInfo := previous.StateExecutionCounterInfo
stateExecutionCounter = RebuildStateExecutionCounter(ctx, provider, globalVersioner,
Expand All @@ -118,12 +120,12 @@ func InterpreterImpl(
internalChannel = NewInternalChannel()
stateRequestQueue = NewStateRequestQueue()
persistenceManager = NewPersistenceManager(provider, input.InitDataAttributes, input.InitSearchAttributes, input.UseMemoForDataAttributes)
continueAsNewCounter = cont.NewContinueAsCounter(workflowConfiger, ctx, provider)
if input.Config.GetOptimizeTimer() {
timerProcessor = timers.NewGreedyTimerProcessor(ctx, provider, nil)
timerProcessor = timers.NewGreedyTimerProcessor(ctx, provider, continueAsNewCounter, nil)
} else {
timerProcessor = timers.NewSimpleTimerProcessor(ctx, provider, nil)
}
continueAsNewCounter = NewContinueAsCounter(workflowConfiger, ctx, provider)
signalReceiver = NewSignalReceiver(ctx, provider, internalChannel, stateRequestQueue, persistenceManager, timerProcessor, continueAsNewCounter, workflowConfiger, nil)
stateExecutionCounter = NewStateExecutionCounter(ctx, provider, globalVersioner, workflowConfiger, continueAsNewCounter)
outputCollector = NewOutputCollector(nil)
Expand Down Expand Up @@ -525,8 +527,8 @@ func processStateExecution(
signalReceiver *SignalReceiver,
timerProcessor interfaces.TimerProcessor,
continueAsNewer *ContinueAsNewer,
continueAsNewCounter *ContinueAsNewCounter,
configer *WorkflowConfiger,
continueAsNewCounter *cont.ContinueAsNewCounter,
configer *config.WorkflowConfiger,
shouldSendSignalOnCompletion bool,
) (*iwfidl.StateDecision, service.StateExecutionStatus, error) {
waitUntilApi := StateStart
Expand Down Expand Up @@ -832,7 +834,7 @@ func invokeStateExecute(
executionContext iwfidl.Context,
commandRes *iwfidl.CommandResults,
continueAsNewer *ContinueAsNewer,
configer *WorkflowConfiger,
configer *config.WorkflowConfiger,
executeApi interface{},
stateExecutionLocal []iwfidl.KeyValue,
shouldSendSignalOnCompletion bool,
Expand Down
8 changes: 5 additions & 3 deletions service/interpreter/workflowUpdater.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"github.com/indeedeng/iwf/gen/iwfidl"
"github.com/indeedeng/iwf/service"
"github.com/indeedeng/iwf/service/common/event"
"github.com/indeedeng/iwf/service/interpreter/config"
"github.com/indeedeng/iwf/service/interpreter/cont"
"github.com/indeedeng/iwf/service/interpreter/interfaces"
"time"
)
Expand All @@ -12,11 +14,11 @@ type WorkflowUpdater struct {
persistenceManager *PersistenceManager
provider interfaces.WorkflowProvider
continueAsNewer *ContinueAsNewer
continueAsNewCounter *ContinueAsNewCounter
continueAsNewCounter *cont.ContinueAsNewCounter
internalChannel *InternalChannel
signalReceiver *SignalReceiver
stateRequestQueue *StateRequestQueue
configer *WorkflowConfiger
configer *config.WorkflowConfiger
logger interfaces.UnifiedLogger
basicInfo service.BasicInfo
globalVersioner *GlobalVersioner
Expand All @@ -25,7 +27,7 @@ type WorkflowUpdater struct {
func NewWorkflowUpdater(
ctx interfaces.UnifiedContext, provider interfaces.WorkflowProvider, persistenceManager *PersistenceManager,
stateRequestQueue *StateRequestQueue,
continueAsNewer *ContinueAsNewer, continueAsNewCounter *ContinueAsNewCounter, configer *WorkflowConfiger,
continueAsNewer *ContinueAsNewer, continueAsNewCounter *cont.ContinueAsNewCounter, configer *config.WorkflowConfiger,
internalChannel *InternalChannel, signalReceiver *SignalReceiver, basicInfo service.BasicInfo,
globalVersioner *GlobalVersioner,
) (*WorkflowUpdater, error) {
Expand Down

0 comments on commit a6399df

Please sign in to comment.