Azure / Azure/azure-functions-durable-js

context.df.Task.any with createTimer and waitForExternalEvent leaves orchestrator in invalid state

Open
#577 0 comments 0 reactions 0 assignees View on GitHub
P1
Dominant language
TypeScript
Stars
142
Forks
66
Avg merge
3d 19h
Merged PRs (30d)
4

Description

**Describe the bug**

I have this code
```ts
df.app.orchestration('queue_orchestrator', {
handler: function* (context) {
const response: 'queue_empty' | 'continue' | null = yield context.df.callActivity('pop_queue');

if (!response) {
// Activity failed retry in 1 minute
yield context.df.createTimer(new Date(context.df.currentUtcDateTime.getTime() + 1000 * 60)); // 1 minute
} else if (response === 'queue_empty') {
// No more items in queue, wait for new items
const timeoutTask = context.df.createTimer(
new Date(context.df.currentUtcDateTime.getTime() + 1000 * 60 * 60 * 5),
); // 5 hours
const hasQueueTask = context.df.waitForExternalEvent('has_queue');
yield context.df.Task.any([timeoutTask, hasQueueTask]);
}

context.df.continueAsNew(undefined);
},
});
```

1. After this code is run under function emulator 4.0.5455 on WIndows 11
```ts
const timeoutTask = context.df.createTimer(
new Date(context.df.currentUtcDateTime.getTime() + 1000 * 60 * 60 * 5),
); // 5 hours
const hasQueueTask = context.df.waitForExternalEvent('has_queue');
yield context.df.Task.any([timeoutTask, hasQueueTask]);
```

2. this code starts returning old data without calling the activity
```ts
const response: 'queue_empty' | 'continue' | null = yield context.df.callActivity('pop_queue');
```

If I change code 1. to this everything works
```ts
yield context.df.waitForExternalEvent('has_queue');
```

I think there is a bug in context.df.Task.any that it leaves orchestrator in invalid state if waitForExternalEvent is first task to finish.

**Investigative information**

- durable-functions npm module version: 3.0.1
- Language (JavaScript/TypeScript) and version: 5.2.2
- Node.js version: v18.18

**Actual behavior**
When using context.df.Task.any with context.df.createTimer & context.df.waitForExternalEvent and if context.df.waitForExternalEvent task finishes first orchestrator is left in invalid state and starts to receive old values from context.df.callActivity('pop_queue') without even calling the activity meaning code in the activity is never called.

Contributor guide

Open the contributing guide

Research direction

Start with the context.df.Task.any reproduction combining createTimer and waitForExternalEvent, using the function emulator 4.0.5455 and the versions listed. Verify whether the orchestrator receives stale callActivity('pop_queue') results after the external event wins; done means the activity is invoked normally and the orchestrator remains valid after either task completes.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, node.js, typescript
Domain
backend, distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.