Repository navigation
fix(telemetry): keep emittedAt unique per invocation - #1071
Merged
Merged
Conversation
Two on-change records built in the same millisecond got the same emittedAt. SQS FIFO deduplication and the default DynamoDB sort key both use it, so a record was dropped or overwritten. emittedAt now strictly increases within one plugin instance.
ParidelPooya
temporarily deployed
to
ai-pr-review-runtime
October 23, 2026 19:59 — with
GitHub Actions
Inactive
ParidelPooya
temporarily deployed
to
ai-pr-review-runtime
October 23, 2026 20:00 — with
GitHub Actions
Inactive
This comment has been minimized.
This comment has been minimized.
ParidelPooya
added a commit
to aws/aws-durable-execution-sdk-java
that referenced
this pull request
Oct 23, 2026
emittedAt has millisecond precision. With ON_CHANGE, one invocation can build two records in the same millisecond. One example is the last RUNNING record and the terminal record. So both records got the same emittedAt. SQSExporter builds the FIFO deduplication id from executionArn and emittedAt. DynamoDBExporter uses emittedAt as its default sort key. So SQS dropped the second message, and DynamoDB overwrote the first item. TelemetryPlugin now keeps the last emittedAt it assigned, in epoch milliseconds. If the clock is past that value, the clock is used. Otherwise the last value plus 1 ms is used. So emittedAt strictly increases within one plugin instance. One instance serves one invocation. This is the rule of aws/aws-durable-execution-sdk-js#1071. Hook threads can build records at the same time. No lock serializes the builds. So the last value is an AtomicLong, and the read and the update are one updateAndGet(prev -> Math.max(prev + 1, now)).
hln33
approved these changes
Oct 23, 2026
ParidelPooya
temporarily deployed
to
ai-pr-review-runtime
October 23, 2026 22:20 — with
GitHub Actions
Inactive
Codex AI reviewNo actionable findings. SDK invariants and contract tests remain unchanged. Added unit tests cover timestamp collisions and clock advancement; runtime validation is limited because repository code execution was prohibited. Reviewed commit |
ParidelPooya
added a commit
to aws/aws-durable-execution-sdk-java
that referenced
this pull request
Oct 23, 2026
* fix(telemetry): write fixed-width UTC timestamps Record timestamps were written with Instant.toString(). That method writes 0, 3, 6 or 9 fraction digits, depending on the value. So one record could hold startTime with 3 digits and endTime with 9. Destinations such as DynamoDB and CloudWatch Logs Insights compare these values as text. Text order equals time order only when every value has the same width. So mixed widths sort and filter incorrectly. emittedAt, startTime and endTime, on the record and on each operation, now use YYYY-MM-DDTHH:MM:SS.sssZ. This is the format of JavaScript's Date.prototype.toISOString(). Digits below the millisecond are dropped. The new internal Timestamps.format writes it. LambdaLogExporter uses the same method for the envelope timestamp, so its output is unchanged. * fix(telemetry): keep emittedAt unique per invocation emittedAt has millisecond precision. With ON_CHANGE, one invocation can build two records in the same millisecond. One example is the last RUNNING record and the terminal record. So both records got the same emittedAt. SQSExporter builds the FIFO deduplication id from executionArn and emittedAt. DynamoDBExporter uses emittedAt as its default sort key. So SQS dropped the second message, and DynamoDB overwrote the first item. TelemetryPlugin now keeps the last emittedAt it assigned, in epoch milliseconds. If the clock is past that value, the clock is used. Otherwise the last value plus 1 ms is used. So emittedAt strictly increases within one plugin instance. One instance serves one invocation. This is the rule of aws/aws-durable-execution-sdk-js#1071. Hook threads can build records at the same time. No lock serializes the builds. So the last value is an AtomicLong, and the read and the update are one updateAndGet(prev -> Math.max(prev + 1, now)).
ParidelPooya
added a commit
that referenced
this pull request
Oct 23, 2026
Resolves the CHANGELOG conflict with #1071 by keeping both Fixed entries. The MCP entry now describes the final bound rules.
2 tasks done
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
emittedAthas millisecond precision. WithemitMode: "on-change", two records of one execution can be built within the same millisecond, for example a RUNNING record and the final record. They then get the sameemittedAt.Two exporters key on
executionArn+emittedAt:SQSExporterderives the FIFO deduplication id fromexecutionArn:emittedAt. So SQS dropped the second message as a duplicate, and that can be the final record.DynamoDBExporterusesemittedAtas its default sort key. So the second item overwrote the first.This change makes
emittedAtstrictly increase within one plugin instance, which serves one invocation. A record built in the same millisecond as the previous one, or earlier, gets the previous value plus 1 ms. Otherwise it gets the current time. The format and the record shape do not change.Codex raised this on the Python and Java PRs that pin record timestamps to the same millisecond format (aws/aws-durable-execution-sdk-python#821, aws/aws-durable-execution-sdk-java#821). JS already wrote milliseconds, so it already had the collision. Those two PRs get the same rule.
Testing
index.test.ts:Date.nowfrozen, an on-change invocation's three records (RUNNING, RUNNING, SUCCEEDED) get.123Z,.124Z,.125Z.biome ciis clean on the exporter source and the CHANGELOG.