Skip to content

Add Java Durable Task SDK sample for Work Item Filtering#276

Open
bachuv wants to merge 1 commit intoAzure-Samples:mainfrom
bachuv:vabachu/work-item-filters-java
Open

Add Java Durable Task SDK sample for Work Item Filtering#276
bachuv wants to merge 1 commit intoAzure-Samples:mainfrom
bachuv:vabachu/work-item-filters-java

Conversation

@bachuv
Copy link
Copy Markdown

@bachuv bachuv commented Apr 24, 2026

Purpose

Adds a Java port of the Work Item Filtering split activities scenario sample (samples/scenarios/WorkItemFilteringSplitActivitiesJava), using the standalone Durable Task SDK (not Durable Functions). This demonstrates the new useWorkItemFilters() API from durabletask-java v1.9.0 to route orchestration and activity work items to dedicated workers.

Note: Work item filtering for Java is currently supported only in the standalone Durable Task SDK (durabletask-client / durabletask-azuremanaged). It is not yet available in the Durable Functions Java extension.

Note: Creating the PR as a draft PR until the feature is released in the Durable Java SDK.

What's included

  • 4 standalone Java services (Gradle multi-project build):
    • Orchestrator Worker — registers only OrderProcessingOrchestration
    • Validator Worker — registers only ValidateOrder activity
    • Shipper Worker — registers only ShipOrder activity
    • Client — schedules orchestration batches in a loop and prints results
  • Shared ConnectionHelper — builds DTS connection strings from environment variables (emulator, Managed Identity, DefaultAzure)
  • Dockerfiles for each service (Eclipse Temurin 21)
  • Bicep infrastructure and azure.yaml for one-command azd up deployment to Azure Container Apps with KEDA scaling
  • README with architecture diagram, step-by-step local run instructions, expected output, and Azure deployment guide

How it works

Each worker calls .useWorkItemFilters() on the DurableTaskGrpcWorkerBuilder to auto-generate filters from registered tasks. DTS routes work items only to workers with matching filters — no cross-processing occurs.

Tested locally

Verified end-to-end against the DTS emulator (mcr.microsoft.com/dts/dts-emulator:latest):

  • All 3 orchestrations per batch complete successfully
  • Orchestrator Worker handles only orchestration replays
  • Validator Worker handles only ValidateOrder activities
  • Shipper Worker handles only ShipOrder activities

Related

Does this introduce a breaking change?

  • Yes
  • No

Pull Request Type

What kind of change does this Pull Request introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Documentation content changes
  • Other... Please describe:

How to Test

  • Get the code
git clone https://github.com/Azure-Samples/Durable-Task-Scheduler.git
cd Durable-Task-Scheduler
git checkout vabachu/work-item-filters-java
  • Start the DTS emulator
docker pull mcr.microsoft.com/dts/dts-emulator:latest
docker run -d --name dts-emulator -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest
  • Build the sample
cd samples/scenarios/WorkItemFilteringSplitActivitiesJava
./gradlew build
  • Start the three workers (each in a separate terminal)
./gradlew :orchestrator-worker:run
./gradlew :validator-worker:run
./gradlew :shipper-worker:run
  • Run the client (in a fourth terminal)
./gradlew :client:run

What to Check

Verify that the following are valid:

  • Client schedules 3 orchestrations per batch and all complete successfully
  • Orchestrator Worker logs only orchestration processing (no activity execution)
  • Validator Worker logs only ValidateOrder activity work items
  • Shipper Worker logs only ShipOrder activity work items
  • Stopping Shipper Worker causes ShipOrder work items to remain pending (not routed to other workers)
  • Restarting Shipper Worker causes pending ShipOrder work items to complete immediately

Other Information

  • Requires Java 11+ and Docker
  • Uses durabletask-client:1.9.0 and durabletask-azuremanaged:1.9.0 from Maven local (not yet published to Maven Central)
  • This is a standalone Durable Task SDK sample — work item filtering is not yet available in the Durable Functions Java extension

@bachuv bachuv marked this pull request as ready for review April 27, 2026 23:12
@torosent
Copy link
Copy Markdown
Collaborator

Tested this PR locally end-to-end against the DTS emulator (built durabletask-{client,azuremanaged}:1.9.0 from the merged microsoft/durabletask-java#275 — note that 1.9.0 is now on Maven Central as of 2026-04-24, so the "draft until SDK release" gate is cleared).

All functional checklist items pass: orchestrations complete 3/3 across batches, each worker only processes its registered work item types (Orchestrator: 30 dispatches / 0 activities; Validator: 36 ValidateOrder / 0 ShipOrder / 0 orchestrations; Shipper: 24 ShipOrder / 0 ValidateOrder / 0 orchestrations), the strict-routing experiment works as documented (stop Shipper → ShipOrder backlogs, restart → backlog drains immediately), and az bicep build compiles all 11 templates cleanly.

Found four issues worth addressing before merge:

1. gradlew is committed without the execute bit

git ls-tree HEAD samples/scenarios/WorkItemFilteringSplitActivitiesJava/gradlew reports mode 100644, but every other Java sample in this repo has it as 100755:

100755 .../async-http-api/gradlew
100755 .../eternal-orchestrations/gradlew
100755 .../fan-out-fan-in/gradlew
...
100644 .../WorkItemFilteringSplitActivitiesJava/gradlew   ← this PR

Without it, the README's ./gradlew build step fails with Permission denied on macOS/Linux. Fix:

git update-index --chmod=+x samples/scenarios/WorkItemFilteringSplitActivitiesJava/gradlew

2. README documents activity log lines that the Java SDK can't produce

The "Expected Output" section shows:

[Validator] Activity | Name=ValidateOrder | InstanceId=abc123 | Validating order 'ORD-B001-001'...
[Shipper]   Activity | Name=ShipOrder    | InstanceId=abc123 | Shipping order 'ORD-B001-001'...

But TaskActivityContext in durabletask-client only exposes getName() and getInput(...) — there is no getInstanceId(). The actual code in ValidatorWorker.java / ShipperWorker.java correctly omits InstanceId, so what the runtime actually emits is:

[Validator] Activity | Name=ValidateOrder | Validating order 'ORD-B001-001'...
[Shipper]   Activity | Name=ShipOrder | Shipping order 'ORD-B001-001'...

(The .NET equivalent has context.InstanceId available, which is presumably where the example was copied from.) Either drop the InstanceId=... segments from the README's example output, or add getInstanceId() to the Java SDK and then log it.

3. Prerequisite Java version is inconsistent

  • README.md line 55: [Java 21](https://adoptium.net/) (or later)
  • build.gradle lines 13–14: sourceCompatibility = JavaVersion.VERSION_11 / targetCompatibility = JavaVersion.VERSION_11
  • PR description: "Requires Java 11+ and Docker"

The Dockerfiles use Temurin 21 for image builds, but the local-build target is 11. Either bump the source/target to 21 in build.gradle or change the README prereq to "Java 11+" to match the PR description.

4. CI doesn't validate this sample builds

.github/workflows/build-samples.yml lists every existing Java sample under samples/durable-task-sdks/java/ (function-chaining, fan-out-fan-in, sub-orchestrations, human-interaction, monitoring, eternal-orchestrations, async-http-api, opentelemetry-tracing) but has no step for samples/scenarios/WorkItemFilteringSplitActivitiesJava. That's why the "Java Samples" check is green even with the missing +x bit on gradlew. Suggested addition:

- name: Build Scenario - WorkItemFilteringSplitActivitiesJava
  run: cd samples/scenarios/WorkItemFilteringSplitActivitiesJava && chmod +x gradlew && ./gradlew build

(chmod +x gradlew becomes unnecessary once #1 is fixed.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants