@@ -76,20 +76,56 @@ supplied, all three are exported.
7676- ** Permissions** — the storage credential needs blob write on the container; the DTS credential needs
7777 orchestration read.
7878
79+ ## Export format
80+
81+ Each blob holds the instance's full history, and the ** blob body is byte-for-byte identical to the .NET
82+ ` Microsoft.DurableTask.ExportHistory ` output** (pinned by a test against golden output captured from
83+ ` Microsoft.Azure.DurableTask.Core ` ):
84+
85+ - ** JSONL** (default, gzipped) is one JSON object per line; ** JSON** is a single array.
86+ - Each event is ` {"eventType": "...", <type-specific fields>, "eventId": N, "isPlayed": false, "timestamp": "..."} ` .
87+ - camelCase field names, null fields omitted, empty maps as ` {} ` , enum values in PascalCase (e.g. ` "Completed" ` ),
88+ timestamps as trimmed ISO-8601 ending in ` Z ` , and the same HTML-safe string escaping (` " ` → ` \u0022 ` ,
89+ ` & < > ' + ` and all non-ASCII → ` \uXXXX ` ).
90+
91+ Blob ** names** : a lowercase-hex SHA-256 of ` "<completedTimestamp>|<instanceId>" ` plus the format extension.
92+
7993## Differences from .NET
8094
81951 . ** Worker registration takes an explicit client** — ` useExportHistory(workerBuilder, storage, client) ` . Java has
8296 no dependency injection, so the export activities require a ` DurableTaskClient ` for the same backend.
83- 2 . ** Export format** — history is serialized from the structured ` com.microsoft.durabletask.history ` domain model
84- (camelCase, null fields omitted, one event per line for JSONL). Byte-level parity with .NET's
85- protobuf-` HistoryEvent ` -JSON output is an open item.
97+ 2 . ** Entity events** — the .NET export folds durable-entity operations into core events via a stateful converter, so
98+ it has no distinct JSON for them; Java emits entity events in a Java-native shape instead (a reflective projection
99+ with an ` eventType ` discriminator, e.g. ` "EntityLockGranted" ` — matching the Python SDK, which also keeps entity
100+ events as first-class types). Every ** non-entity** event is byte-for-byte identical to .NET, so only orchestrations
101+ that call entities differ, and only on those entity-specific lines.
86102
87103## Backend requirement
88104
89105The export feature relies on the ` ListInstanceIds ` and ` StreamInstanceHistory ` gRPC operations. Managed DTS serves
90106both; the emulator / self-hosted sidecar needs ** ≥ v0.4.22** . Against an older backend, a raw gRPC ` UNIMPLEMENTED `
91107surfaces (matching .NET).
92108
109+ ## Validating the export
110+
111+ Locally, with the DTS emulator and Azurite:
112+
113+ 1 . Start the backends:
114+ ```
115+ docker run --name durabletask-emulator -p 4001:8080 -d mcr.microsoft.com/dts/dts-emulator:latest
116+ docker run --name azurite -p 10000:10000 -d mcr.microsoft.com/azure-storage/azurite azurite-blob --blobHost 0.0.0.0
117+ ```
118+ 2 . Point the app at them: DTS connection ` Endpoint=http://localhost:4001;Authentication=None ` , storage = the Azurite
119+ dev connection string, container ` orchestration-history ` .
120+ 3 . Run an orchestration to a terminal state, then create a ` BATCH ` export job whose window covers its completion time.
121+ 4 . Confirm the job reaches ` COMPLETED ` and inspect progress:
122+ ``` java
123+ ExportJobDescription d = job. describe();
124+ // d.getStatus() == ExportJobStatus.COMPLETED, d.getExportedInstances() >= 1
125+ ```
126+ 5 . Download the blob from the container (gunzip for JSONL) and inspect it. Every line carries an ` eventType `
127+ discriminator, ` isPlayed:false ` , and a trailing ` timestamp ` .
128+
93129## Sample
94130
95131See [ ` HistoryExportSample ` ] ( ../samples/src/main/java/io/durabletask/samples/HistoryExportSample.java ) :
0 commit comments