Skip to content

Commit 3040884

Browse files
authored
refactor: streamline orchestration event handling and improve state management (#115)
1 parent c632ed8 commit 3040884

14 files changed

Lines changed: 1204 additions & 1165 deletions

File tree

.github/workflows/dts-e2e-tests.yaml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: 🧪 DTS Emulator E2E Tests
22

33
# This workflow runs E2E tests against the Durable Task Scheduler (DTS) emulator.
4-
# It mirrors the Python testing setup at durabletask-python for Azure-managed tests.
4+
# Tests are split across parallel jobs with separate task hubs for isolation.
55

66
on:
77
push:
@@ -20,6 +20,16 @@ jobs:
2020
fail-fast: false
2121
matrix:
2222
node-version: ["22.x", "24.x"]
23+
test-group:
24+
- name: "entity"
25+
pattern: "test/e2e-azuremanaged/entity.spec.ts"
26+
- name: "orchestration"
27+
pattern: "test/e2e-azuremanaged/orchestration.spec.ts"
28+
- name: "query-restart"
29+
pattern: "test/e2e-azuremanaged/query-apis.spec.ts test/e2e-azuremanaged/restart.spec.ts"
30+
- name: "retry-history-rewind"
31+
pattern: "test/e2e-azuremanaged/retry-handler.spec.ts test/e2e-azuremanaged/retry-advanced.spec.ts test/e2e-azuremanaged/history.spec.ts test/e2e-azuremanaged/rewind.spec.ts"
32+
name: "e2e (${{ matrix.test-group.name }}, node ${{ matrix.node-version }})"
2333
env:
2434
EMULATOR_VERSION: "latest"
2535
runs-on: ubuntu-latest
@@ -36,7 +46,7 @@ jobs:
3646
docker run --name dtsemulator -d -p 8080:8080 mcr.microsoft.com/dts/dts-emulator:$EMULATOR_VERSION
3747
3848
- name: ⏳ Wait for container to be ready
39-
run: sleep 10 # Adjust if your service needs more time to start
49+
run: sleep 10
4050

4151
- name: 🔧 Set environment variables
4252
run: |
@@ -50,7 +60,8 @@ jobs:
5060
registry-url: "https://registry.npmjs.org"
5161

5262
- name: ⚙️ Install dependencies
53-
run: npm install
63+
run: npm ci
5464

55-
- name: ✅ Run E2E tests against DTS emulator
56-
run: npm run test:e2e:azuremanaged:internal
65+
- name: ✅ Run E2E tests — ${{ matrix.test-group.name }}
66+
run: npx jest ${{ matrix.test-group.pattern }} --runInBand --detectOpenHandles
67+
timeout-minutes: 15

.github/workflows/pr-validation.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717

1818
env:
19-
NODE_VER: 18.x
19+
NODE_VER: 22.x
2020

2121
steps:
2222
- name: 📥 Checkout code
@@ -29,7 +29,7 @@ jobs:
2929
registry-url: "https://registry.npmjs.org"
3030

3131
- name: ⚙️ Install dependencies
32-
run: npm install
32+
run: npm ci
3333

3434
- name: 🔍 Run linting
3535
run: npm run lint
@@ -56,7 +56,7 @@ jobs:
5656
registry-url: "https://registry.npmjs.org"
5757

5858
- name: ⚙️ Install dependencies
59-
run: npm install
59+
run: npm ci
6060

6161
# Install Go SDK for durabletask-go sidecar
6262
- name: 🔧 Install Go SDK

package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"test:e2e": "./scripts/test-e2e.sh",
1717
"test:e2e:internal": "jest tests/e2e --runInBand --detectOpenHandles",
1818
"test:e2e:one": "jest tests/e2e --runInBand --detectOpenHandles --testNamePattern",
19-
"test:e2e:azuremanaged:internal": "jest test/e2e-azuremanaged --runInBand --detectOpenHandles",
19+
"test:e2e:azuremanaged:internal": "jest test/e2e-azuremanaged --detectOpenHandles",
2020
"test:e2e:azuremanaged": "./scripts/test-e2e-azuremanaged.sh",
2121
"lint": "eslint .",
2222
"pretty": "prettier --list-different \"**/*.{ts,tsx,js,jsx,json,md}\"",

packages/durabletask-js-azuremanaged/src/credential-factory.ts

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,17 @@ export function getCredentialFromAuthenticationType(
3939
}
4040

4141
case "workloadidentity": {
42-
const options: {
43-
clientId?: string;
44-
tenantId?: string;
45-
tokenFilePath?: string;
46-
additionallyAllowedTenants?: string[];
47-
} = {};
48-
4942
const clientId = connectionString.getClientId();
50-
if (clientId) {
51-
options.clientId = clientId;
52-
}
53-
5443
const tenantId = connectionString.getTenantId();
55-
if (tenantId) {
56-
options.tenantId = tenantId;
57-
}
58-
5944
const tokenFilePath = connectionString.getTokenFilePath();
60-
if (tokenFilePath) {
61-
options.tokenFilePath = tokenFilePath;
62-
}
63-
6445
const additionallyAllowedTenants = connectionString.getAdditionallyAllowedTenants();
65-
if (additionallyAllowedTenants) {
66-
options.additionallyAllowedTenants = additionallyAllowedTenants;
67-
}
6846

69-
return new WorkloadIdentityCredential(options);
47+
return new WorkloadIdentityCredential({
48+
...(clientId && { clientId }),
49+
...(tenantId && { tenantId }),
50+
...(tokenFilePath && { tokenFilePath }),
51+
...(additionallyAllowedTenants && { additionallyAllowedTenants }),
52+
});
7053
}
7154

7255
case "environment":

packages/durabletask-js-azuremanaged/src/options.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ abstract class DurableTaskAzureManagedOptionsBase {
9191

9292
// Add https:// prefix if no protocol is specified
9393
if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) {
94-
endpoint = "https://" + endpoint;
94+
endpoint = `https://${endpoint}`;
9595
}
9696

9797
try {
9898
const url = new URL(endpoint);
9999
let authority = url.hostname;
100100
if (url.port) {
101-
authority += ":" + url.port;
101+
authority = `${authority}:${url.port}`;
102102
}
103103
return authority;
104104
} catch {

packages/durabletask-js/src/orchestration/page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class Page<T> {
3030
* Returns true if there are more pages available.
3131
*/
3232
get hasMoreResults(): boolean {
33-
return this.continuationToken !== undefined && this.continuationToken !== "";
33+
return !!this.continuationToken;
3434
}
3535
}
3636

packages/durabletask-js/src/tracing/trace-helper.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -517,33 +517,27 @@ export function processActionsForTracing(
517517
}
518518
}
519519

520-
/**
521-
* Emits a span for calling an entity from an orchestration (request/response).
522-
*
523-
* @param orchestrationSpan - The parent orchestration span.
524-
* @param operationName - The entity operation name.
525-
* @param targetInstanceId - The target entity instance ID.
526-
* @param taskId - The sequential task ID.
527-
*/
528-
export function emitSpanForEntityCall(
520+
function emitSpanForEntityOperation(
529521
orchestrationSpan: Span,
530522
operationName: string,
523+
taskType: string,
524+
getSpanKind: (otel: any) => any,
531525
targetInstanceId?: string,
532526
taskId?: number,
533527
): void {
534528
const otel = getOtelApi();
535529
const tracer = getTracer();
536530
if (!otel || !tracer) return;
537531

538-
const spanName = createSpanName(TaskType.CALL_ENTITY, operationName);
532+
const spanName = createSpanName(taskType, operationName);
539533
const parentContext = otel.trace.setSpan(otel.context.active(), orchestrationSpan);
540534

541535
const span = tracer.startSpan(
542536
spanName,
543537
{
544-
kind: otel.SpanKind.CLIENT,
538+
kind: getSpanKind(otel),
545539
attributes: {
546-
[DurableTaskAttributes.TYPE]: TaskType.CALL_ENTITY,
540+
[DurableTaskAttributes.TYPE]: taskType,
547541
[DurableTaskAttributes.ENTITY_OPERATION]: operationName,
548542
...(targetInstanceId ? { [DurableTaskAttributes.ENTITY_INSTANCE_ID]: targetInstanceId } : {}),
549543
...(taskId !== undefined ? { [DurableTaskAttributes.TASK_TASK_ID]: taskId } : {}),
@@ -555,6 +549,30 @@ export function emitSpanForEntityCall(
555549
span.end();
556550
}
557551

552+
/**
553+
* Emits a span for calling an entity from an orchestration (request/response).
554+
*
555+
* @param orchestrationSpan - The parent orchestration span.
556+
* @param operationName - The entity operation name.
557+
* @param targetInstanceId - The target entity instance ID.
558+
* @param taskId - The sequential task ID.
559+
*/
560+
export function emitSpanForEntityCall(
561+
orchestrationSpan: Span,
562+
operationName: string,
563+
targetInstanceId?: string,
564+
taskId?: number,
565+
): void {
566+
emitSpanForEntityOperation(
567+
orchestrationSpan,
568+
operationName,
569+
TaskType.CALL_ENTITY,
570+
(otel) => otel.SpanKind.CLIENT,
571+
targetInstanceId,
572+
taskId,
573+
);
574+
}
575+
558576
/**
559577
* Emits a span for signaling an entity from an orchestration (fire-and-forget).
560578
*
@@ -569,28 +587,14 @@ export function emitSpanForEntitySignal(
569587
targetInstanceId?: string,
570588
taskId?: number,
571589
): void {
572-
const otel = getOtelApi();
573-
const tracer = getTracer();
574-
if (!otel || !tracer) return;
575-
576-
const spanName = createSpanName(TaskType.SIGNAL_ENTITY, operationName);
577-
const parentContext = otel.trace.setSpan(otel.context.active(), orchestrationSpan);
578-
579-
const span = tracer.startSpan(
580-
spanName,
581-
{
582-
kind: otel.SpanKind.PRODUCER,
583-
attributes: {
584-
[DurableTaskAttributes.TYPE]: TaskType.SIGNAL_ENTITY,
585-
[DurableTaskAttributes.ENTITY_OPERATION]: operationName,
586-
...(targetInstanceId ? { [DurableTaskAttributes.ENTITY_INSTANCE_ID]: targetInstanceId } : {}),
587-
...(taskId !== undefined ? { [DurableTaskAttributes.TASK_TASK_ID]: taskId } : {}),
588-
},
589-
},
590-
parentContext,
590+
emitSpanForEntityOperation(
591+
orchestrationSpan,
592+
operationName,
593+
TaskType.SIGNAL_ENTITY,
594+
(otel) => otel.SpanKind.PRODUCER,
595+
targetInstanceId,
596+
taskId,
591597
);
592-
593-
span.end();
594598
}
595599

596600
/**

0 commit comments

Comments
 (0)