Skip to content

fix(telemetry): keep emittedAt unique per invocation - #1071

Merged
ParidelPooya merged 2 commits into
mainfrom
fix/telemetry-emitted-at-increasing
Oct 23, 2026
Merged

ParidelPooya merged 2 commits into
mainfrom
fix/telemetry-emitted-at-increasing

Conversation

@ParidelPooya

Copy link
Copy Markdown
Contributor

Summary

emittedAt has millisecond precision. With emitMode: "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 same emittedAt.

Two exporters key on executionArn + emittedAt:

  • SQSExporter derives the FIFO deduplication id from executionArn:emittedAt. So SQS dropped the second message as a duplicate, and that can be the final record.
  • DynamoDBExporter uses emittedAt as its default sort key. So the second item overwrote the first.

This change makes emittedAt strictly 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

  • Two new tests in index.test.ts:
    • With Date.now frozen, an on-change invocation's three records (RUNNING, RUNNING, SUCCEEDED) get .123Z, .124Z, .125Z.
    • When the clock moves past the previous record, the record uses the current time.
    • Both tests fail without the change.
  • Exporter: build passes, 161 tests pass.
  • biome ci is clean on the exporter source and the CHANGELOG.

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
ParidelPooya temporarily deployed to ai-pr-review-runtime October 23, 2026 19:59 — with GitHub Actions Inactive
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 23, 2026 20:00 — with GitHub Actions Inactive
@github-actions

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)).
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 23, 2026 22:20 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown

Codex AI review

No 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 c4c5932893e10312be54cfef32950626c7cd49cf. Workflow run

@ParidelPooya
ParidelPooya merged commit 2676e48 into main Oct 23, 2026
25 checks passed
@ParidelPooya
ParidelPooya deleted the fix/telemetry-emitted-at-increasing branch October 23, 2026 22:32
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.

This branch was previously deployed

1 inactive deployment
ai-pr-review-runtime — c4c59328 Deployed Oct 23, 2026 by ParidelPooya via ai-pr-review / Codex review / Generate Codex review #1773
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