diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e6604b26..5ed2e199a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Coordinated release summary + +Core 3.0.0, OTel 2.0.0 and Testing SDK 1.3.2 release together. The entries +below describe each change in detail. + +- Plugin factories replace plugin instances in `DurableExecutionConfig.plugins`. + The SDK creates one plugin per invocation. (6597e075) +- The SDK no longer awaits operation plugin hooks. (4e4e9407) +- OTel reports `faas.max_memory` in bytes, not MiB. (e049d07c) +- `InvokeError.cause.name` is the invoked function's error type. (a0d310f5) +- The idle cooldown before an invocation suspends is 3 ms, not 20 ms. (c0a7e349) +- Telemetry Exporter and Telemetry MCP are `0.1.0-alpha.1`, and the ESLint + plugin is `1.2.0`. So the fixes merged after their published versions ship. + + ### Changed - **Breaking for plugin authors (experimental interface).** The SDK no longer waits for the diff --git a/README.md b/README.md index 75c609cae..3efae3d98 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ Build **resilient, long-running AWS Lambda functions** with automatic state pers ## 📦 Packages +Upgrading an existing application? Read the [2.x to 3.0 migration guide](docs/migration-2.x-to-3.0.md) +for the coordinated Core 3 / OTel 2 upgrade and plugin factory examples. + This monorepo contains the following packages. Most developers need only the first two. ### Build and test durable functions diff --git a/RELEASING.md b/RELEASING.md index 6257400e8..eca402607 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -34,26 +34,48 @@ Each package maintains its own version in the `version` field of its `package.js Bump the version in the appropriate `package.json` file(s) and merge to `main` before creating a release. -### Released minor versions and the next major release - -The published `sdk-2.7.0` release contains core **2.7.0**, OTel **1.2.0**, and -testing **1.3.1**. The `sdk-2.7.1` patch releases core **2.7.1** and OTel -**1.2.1** with the operation telemetry corrections from #960. The major factory -migration retains those fixes and the current conformance handlers. - -Those released core/OTel pairs include registration exclusivity from #956, -but do not include invocation-local LMI carrier forwarding or extraction from -#955. That carrier capability is planned for core **3.0.0** / OTel **2.0.0** in -#924, together with per-invocation plugin factories. Until that release, it is -available only in matched development builds containing the change; existing -package version labels do not identify those builds. - -The released OTel 1.2 packages preserve their optional `>=2.4.0 <3.0.0` core -peer, provider API version 1, and separate OTel-only layers. Core 3 uses the new -factory contract and requires matching plugin packages/layers. Installed-package -tests retain released-version controls and verify both the declared major-pair -compatibility and explicit rejection of legacy providers. Version edits do not -publish packages. +### Prepared Core 3 / OTel 2 release + +The next coordinated release publishes these versions: + +| Package | Version | Publisher decision | +| --- | --- | --- | +| SDK | `3.0.0` | New stable version, `latest` | +| OTel | `2.0.0` | New stable version, `latest` | +| Testing SDK | `1.3.2` | Compatible patch with the widened Core 3 peer range, `latest` | +| ESLint plugin | `1.2.0` | New minor version with the child-context rule, `latest` | +| Telemetry Exporter | `0.1.0-alpha.1` | Prerelease above the published `0.1.0-alpha.0`, `beta` | +| Telemetry MCP | `0.1.0-alpha.1` | Prerelease above the published `0.1.0-alpha.0`, `beta` | + +Use the tag `sdk-3.0.0` and the title `sdk-3.0.0 / otel-2.0.0 / test-1.3.2`. +Paste [the release notes](release-notes/sdk-3.0.0.md) into the GitHub Release. +Refresh them against the selected release commit before publishing. + +The published minor pairs remain Core 2.7.0 / OTel 1.2.0 and Core 2.7.1 / +OTel 1.2.1, with Testing SDK 1.3.1. They include registration exclusivity and +the patch telemetry corrections. They do not include the invocation-local LMI +carrier or the factory migration. Released OTel 1.x keeps its optional +`>=2.4.0 <3.0.0` Core peer and provider API v1. Core 3 requires matching factory +packages and layers. Installed-package tests keep the released-pair controls and +verify that legacy providers are rejected. + +The two Telemetry packages were published on 2026-10-22 outside this workflow, +from the #924 merge commit 6597e075. The publisher skips a version that npm +already has. So this release bumps both packages to `0.1.0-alpha.1`. That bump +publishes the 3 exporter fixes and the 3 MCP fixes merged after 6597e075. It +does not take the `1.0.0` bump from #1030. The ESLint plugin is bumped to +`1.2.0` for the same reason. + +The manual publication left `latest` on `0.1.0-alpha.0` for both Telemetry +packages. The publisher puts `0.1.0-alpha.1` on `beta` and does not move +`latest`. So after the release, a maintainer must move `latest` by hand: + +```bash +npm dist-tag add @aws/durable-execution-sdk-js-telemetry-exporter@0.1.0-alpha.1 latest +npm dist-tag add @aws/durable-execution-sdk-js-telemetry-mcp@0.1.0-alpha.1 latest +``` + +The automatic package list and the Extras and MicroVM deferrals are unchanged. ## Cutting a Release @@ -208,7 +230,7 @@ Before publishing a release: The published minor releases use testing SDK 1.3.1 with core 2.x. This major migration uses testing SDK 1.3.2 with its broadened `>=1.0.1 <4.0.0` peer range, so publication cannot skip the new range because 1.3.1 already exists. Core -3.0.0 and OTel 2.0.0 carry the explicit factory migration; testing remains a -compatible patch. The renamed Telemetry Exporter retains the upstream -0.1.0-alpha.0 version and requires core `>=3.0.0 <4.0.0` for its factory API. +3.0.0 and OTel 2.0.0 carry the explicit factory migration. Testing remains a +compatible patch. The Telemetry Exporter is `0.1.0-alpha.1` and requires core +`>=3.0.0 <4.0.0` for its factory API. Version metadata does not authorize a merge or publication. diff --git a/docs/migration-2.x-to-3.0.md b/docs/migration-2.x-to-3.0.md new file mode 100644 index 000000000..e311a32a4 --- /dev/null +++ b/docs/migration-2.x-to-3.0.md @@ -0,0 +1,367 @@ +# Migrating from 2.x to 3.0 + +`3.0` is a major release. Its breaking changes for plugin authors include the +[per-invocation plugin factory contract](#register-plugin-factories) and +[non-blocking operation hooks](#adapt-operation-hook-scheduling). +Upgrade the OTel package to `2.x` together with Core `3.x`, including packages +delivered through Lambda layers. + +Applications that register no plugins, select none through +`DURABLE_EXECUTION_PLUGINS`, and ship no plugin providers can update their core +dependency without changing their handler or durable operation calls. They should +still review the [behavior changes outside plugins](#review-behavior-changes-outside-plugins). +This guide covers migration from Core `2.x`. The +[release notes](../release-notes/sdk-3.0.0.md) describe the full package scope. + +## Why upgrade to 3.0 + +- **Plugin state is isolated between invocations.** Each Lambda invocation gets + fresh instrumentation, including concurrent invocations in Lambda Managed + Instances. Spans and operation buffers belong to the invocation they describe. +- **Resource ownership is explicit.** Factories can reuse a + provider or exporter while creating separate invocation state. +- **Trace context follows the invocation.** Factories and hooks receive the + invocation-local `xRayTraceId` carrier, avoiding another invocation's process + header when the runtime supplies a carrier. + +## Porting to 3.0 + +The changes most likely to touch your code are the `plugins` argument, +environment-loaded providers, OTel registrations, and custom operation hooks. Use +[Finding affected code](#finding-affected-code) to locate them before upgrading. + +| Core 2.x / OTel 1.x | Core 3.x / OTel 2.x | +| --- | --- | +| `plugins: [pluginInstance]` | `plugins: [factoryObject]` | +| `DurableInstrumentationPluginProvider` with `pluginApiVersion`, `pluginType`, and `createPlugin()` | `DurableInstrumentationPluginFactory` with `createPlugin(info)` | +| `new InvocationOtelPlugin(config)` registration | `createInvocationOtelPluginFactory(config)` | +| `new ExecutionOtelPlugin(config)` registration | `createExecutionOtelPluginFactory(config)` | +| Registration metadata on the plugin instance or constructor | Metadata on the registered factory | +| SDK awaits operation-hook promises before continuing | Synchronous state setup. Explicitly queued asynchronous work when order matters | + +### Upgrade dependencies together + +The coordinated release uses the following versions and requires Node.js 22 or +later: + +| Package | Version | Core compatibility | +| --- | --- | --- | +| `@aws/durable-execution-sdk-js` | `3.0.0` | Core SDK | +| `@aws/durable-execution-sdk-js-otel` | `2.0.0` | `>=3.0.0 <4.0.0` | +| `@aws/durable-execution-sdk-js-testing` | `1.3.2` | `>=1.0.1 <4.0.0` | + +After publication, update the packages your application uses and regenerate its +lock file. If you use the Testing SDK, upgrade it first: `1.3.1` excludes Core 3 +from its peer range, while `1.3.2` supports both Core 2 and Core 3. Keeping a +pinned `1.3.1` while installing Core 3 can cause npm to reject the install with +`ERESOLVE`. + +```bash +npm install --save-dev @aws/durable-execution-sdk-js-testing@1.3.2 +npm install @aws/durable-execution-sdk-js@3.0.0 \ + @aws/durable-execution-sdk-js-otel@2.0.0 +npm ls @aws/durable-execution-sdk-js @aws/durable-execution-sdk-js-otel +``` + +OTel is optional. If you use it, retain the provider/exporter setup described in +the [OTel installation guide](../packages/aws-durable-execution-sdk-js-otel/README.md#installation). +Upgrading the package alone does not configure trace export. + +Rebuild application bundles and custom provider packages, and update attached +layers that contain Core or OTel. Updating the function's `package.json` does not +update layer versions. Core 3 rejects the OTel 1.x provider contract, and OTel 2 +requires Core 3. Verify the versions resolved by the deployed function, not just +the versions in your source tree. + +### Register plugin factories + +`DurableExecutionConfig.plugins` now accepts factory objects with a synchronous +`createPlugin(info)` method. Return a fresh plugin on every call. The factory +receives the same `InvocationInfo` object passed to `onInvocationStart`. + +```typescript +// Core 2.x: one instance registered with the handler +const handler = withDurableExecution(myHandler, { + plugins: [new AuditPlugin()], +}); +``` + +```typescript +// Core 3.0: each invocation gets a new instance +import { + withDurableExecution, + type DurableInstrumentationPlugin, + type DurableInstrumentationPluginFactory, + type OperationInfo, +} from "@aws/durable-execution-sdk-js"; + +class AuditPlugin implements DurableInstrumentationPlugin { + private readonly operationIds = new Set(); + + constructor(private readonly executionArn: string) {} + + async onOperationEnd(info: OperationInfo): Promise { + this.operationIds.add(info.id); + } + + async onInvocationEnd(): Promise { + console.info({ + executionArn: this.executionArn, + operationIds: [...this.operationIds], + }); + } +} + +const auditFactory: DurableInstrumentationPluginFactory = { + createPlugin(info) { + return new AuditPlugin(info.executionArn); + }, +}; + +export const handler = withDurableExecution( + async (_event, context) => context.step("work", async () => "ok"), + { plugins: [auditFactory] }, +); +``` + +A bare function or plugin class without a `createPlugin` member is not a factory. +For example, `plugins: [(info) => new AuditPlugin(info.executionArn)]` is invalid. +Do not return a cached plugin from a factory. A cached plugin shares its state +across invocations again. + +Invalid registrations fail with `PluginLoadError` when the handler's plugin +configuration is resolved. A valid factory that throws or returns `null` or +`undefined` instead skips instrumentation for that invocation. Async factories +are unsupported: Promise-like results are skipped. Keep shared resource setup +outside `createPlugin`, and verify telemetry independently of handler success. + +### Adapt operation hook scheduling + +Core 3 no longer awaits the promises returned by `onOperationStart`, +`onOperationEnd`, `onOperationAttemptStart`, `onOperationAttemptEnd`, or +`onOperationChange` before continuing an operation. Their asynchronous work +cannot delay the next operation or prevent invocation suspension. Migrating to +a factory alone does not adapt a plugin that relied on those waits. + +Hook calls still follow lifecycle order, including +`parent start → child start → child end → parent end` for nested operations. +Each hook's synchronous part runs before the SDK continues, but asynchronous +work from consecutive hooks can overlap and finish out of order. For example, +a child's asynchronous `onOperationEnd` work can finish after its parent's. + +Update custom plugins as follows: + +- Initialize state needed by later hooks or wrappers synchronously, before the + first `await` or queued callback. For example, create and store an operation's + span in `onOperationStart` before awaiting export or enrichment. If the span + is stored after an `await`, a wrapper can run before that span exists. +- If asynchronous work must preserve hook call order, serialize it explicitly + in an invocation-local queue or promise chain. Return the queued work's + promise from each hook so the SDK can track it for draining. Handle rejected + work so one failure does not prevent later queued work from running. +- Flush exporters and other buffered work in `onInvocationEnd`, and await that + work inside the hook. Do not assume all earlier hook work completed: the + drain can reach its deadline with work still pending. + +Before calling `onInvocationEnd`, the SDK drains promises returned by earlier +operation hooks across all plugins until they settle or until 500 ms before +the invocation's deadline. It then calls `onInvocationEnd` and waits until it +settles or until 50 ms before the deadline. These bounds reserve time for +flushing and returning the Lambda response. Reaching a bound stops the wait. +It does not cancel the underlying work or guarantee telemetry delivery. Pending work may never +complete after the runtime freezes. If the context reports no usable deadline, +both waits continue until the work settles. + +`onInvocationStart` remains awaited before any operation, so invocation-wide +asynchronous setup can finish there. `wrapInvocation`, `wrapChildContextFn`, +and `wrapOperationAttemptFn` remain inline around the customer function. They +do not wait for asynchronous work started by an earlier operation hook. See +[the hook scheduling contract](../packages/aws-durable-execution-sdk-js/src/types/plugin.ts) +for the full lifecycle guarantees. + +### Replace the v1 provider contract + +Environment-loaded modules still export `durableExecutionPluginProvider`, but +the exported object now uses the same factory contract as explicit registration. + +```typescript +// Core 2.x +export const durableExecutionPluginProvider = { + pluginApiVersion: 1, + pluginType: AuditPlugin, + createPlugin: () => new AuditPlugin(), +}; +``` + +```typescript +// Core 3.0, alongside AuditPlugin and its imports above +export const durableExecutionPluginProvider: DurableInstrumentationPluginFactory = { + createPlugin(info) { + return new AuditPlugin(info.executionArn); + }, +}; +``` + +Remove `pluginApiVersion` and `pluginType` after migrating construction and state +ownership. Declaring `pluginApiVersion` causes rejection on both explicit and +environment registration. The SDK does not reinterpret a v1 provider with a +different lifetime. Replace imports of `DurableInstrumentationPluginProvider`, +`DurableInstrumentationPluginType`, and +`DURABLE_INSTRUMENTATION_PLUGIN_API_VERSION` with the appropriate factory type. + +The `DURABLE_EXECUTION_PLUGINS` variable still selects package/module specifiers. +Preserve the provider export name and rebuild the package or layer containing +it. See [dynamic instrumentation plugins](../packages/aws-durable-execution-sdk-js/README.md#dynamic-instrumentation-plugins). + +### Migrate OpenTelemetry registrations + +Replace plugin constructors with factory builders. Existing `OtelPluginConfig` +options belong on the factory builder: + +```typescript +// Core 2.x / OTel 1.x +import { InvocationOtelPlugin } from "@aws/durable-execution-sdk-js-otel"; + +const handler = withDurableExecution(myHandler, { + plugins: [new InvocationOtelPlugin()], +}); +``` + +```typescript +// Core 3.0 / OTel 2.0 +import { withDurableExecution } from "@aws/durable-execution-sdk-js"; +import { createInvocationOtelPluginFactory } from "@aws/durable-execution-sdk-js-otel"; + +export const handler = withDurableExecution( + async (_event, context) => context.step("work", async () => "ok"), + { plugins: [createInvocationOtelPluginFactory()] }, +); +``` + +Use `createExecutionOtelPluginFactory` for the execution-rooted view instead. +Register only one view across code and environment selection. The subpaths +`@aws/durable-execution-sdk-js-otel/otel-invocation` and +`@aws/durable-execution-sdk-js-otel/otel-execution` remain the environment-loading +entry points, but must resolve to OTel 2. + +Shared providers remain outside invocation instances. `flushTimeoutMillis` +bounds the export wait. It does not guarantee delivery after timeout, export +failure, or runtime freezing. An invocation must not shut down a shared provider. +See the [OTel migration reference](../packages/aws-durable-execution-sdk-js-otel/README.md#migration-from-the-v1-provider-contract). + +The execution view now reports `faas.max_memory` on the Invocation span in +bytes, as the OpenTelemetry semantic conventions define it. OTel 1.x reported +the Lambda memory setting in MiB. So a 1024 MiB function now reports +`1073741824` instead of `1024`. Update any query, dashboard or alarm that reads +this attribute. + +### Move registration metadata to the factory + +Use `RegisteredDurableInstrumentationPluginFactory` for custom exclusivity +metadata. `RegisteredDurableInstrumentationPluginType` is removed. +`RegisteredDurableInstrumentationPlugin` remains a type describing instance +metadata, but registration validates the factory's metadata in Core 3. +The symbol remains +`Symbol.for("aws.lambda.durable.instrumentation.plugin-registration")`. Place its +`name` and optional `exclusiveGroup` metadata on the registered factory, not on +the returned plugin or its constructor. Configuration validates it before constructing plugins. +Inherited groups accumulate, so a wrapper must preserve its underlying view's +constraint rather than trying to hide it. + +```typescript +// Core 3.0, alongside auditFactory above +import { + DURABLE_INSTRUMENTATION_PLUGIN_REGISTRATION, + type RegisteredDurableInstrumentationPluginFactory, +} from "@aws/durable-execution-sdk-js"; + +const registeredAuditFactory: RegisteredDurableInstrumentationPluginFactory = { + ...auditFactory, + [DURABLE_INSTRUMENTATION_PLUGIN_REGISTRATION]: { + name: "example-audit", + exclusiveGroup: "example-audit-view", + }, +}; +// Pass registeredAuditFactory in plugins when this exclusivity rule is needed. +``` + +### Review behavior changes outside plugins + +These changes apply with or without plugins: + +- **Invoke failure causes.** A failed `context.invoke` still throws + `InvokeError`. Its `cause.name` is now the invoked function's error type, for + example `"PaymentError"`. In 2.x it was always `"Error"`. Update code that + branches on `cause.name`. +- **Slow deserializers.** An operation now stays `EXECUTING` until its result is + deserialized. So a deserializer that fails slowly now fails the invocation. In + 2.x the invocation could return `PENDING` and the failure was not reported. A + deserializer that never settles now keeps the invocation open until the Lambda + timeout. +- **Untracked awaits between operations.** The SDK suspends an idle invocation + 3 ms after the last operation goes idle. In 2.x it waited 20 ms. The SDK does + not track an `await` in handler code between two durable operations, for + example a dynamic `import()` or a file read. So when that `await` takes longer + than 3 ms while another operation such as a callback is pending, the + invocation can suspend before the next operation starts. The code after the + `await` then runs again on the next invocation. Move that work into a `step`. + +### Finding affected code + +Search application code, providers, layer packaging, and tests: + +```bash +rg -n 'plugins:|DURABLE_EXECUTION_PLUGINS|durableExecutionPluginProvider' . +rg -n 'pluginApiVersion|pluginType|DURABLE_INSTRUMENTATION_PLUGIN_API_VERSION' . +rg -n 'DurableInstrumentationPluginProvider|RegisteredDurableInstrumentationPlugin|DurableInstrumentationPluginType' . +rg -n 'new (InvocationOtelPlugin|ExecutionOtelPlugin)|createPlugin' . +rg -n 'onOperation(Start|End|AttemptStart|AttemptEnd|Change)|onInvocation(Start|End)|wrap(Invocation|ChildContextFn|OperationAttemptFn)' . +rg -n '@aws/durable-execution-sdk-js|Layers:|layerVersionArn' . +rg -n 'cause\.name|faas\.max_memory' . +``` + +## Plugin state and invocation lifecycle + +Keep invocation identity, spans, and operation buffers on the new plugin. Keep +intentionally shared providers/exporters outside it. One execution can suspend +and resume many times, creating a new plugin each time. Instance fields are not +checkpointed. Reconstruct execution-wide state from invocation metadata and +checkpointed operations rather than assuming the last instance survives. + +Do not use the presence of `OperationInfo.error` alone to detect failure. Error +details can be absent for failed operations. Use `OperationInfo.status` and +`AttemptEndInfo.outcome` for their respective outcomes. This metadata correction +does not change persisted error payloads or operation identity. + +If you copied the former Powertools logging example, update queries for the +adapter's `executionArn`, `requestId`, `operationId`, and `operationName` fields. +See the [logging guide](../packages/aws-durable-execution-sdk-js/README.md). + +## Recommended validation after upgrading + +1. Resolve the coordinated package versions, regenerate the lock file, and run + your TypeScript build and tests. Verify the effective versions in any bundles + and Lambda layers. +2. Exercise explicit and environment-selected providers. Confirm plugin output. + A successful workflow does not prove a factory returned usable instrumentation. +3. Run a workflow that suspends and resumes. Verify a fresh plugin for each + invocation, stable operation identity, and no repeated completed-step side + effects. Keep operation names, order, nesting, and serialization compatible + while executions are in flight. +4. Exercise concurrent invocations and `map` or `parallel`. Confirm spans, + buffers, and execution identities stay with their own invocation. +5. Test success, failure, retry, and callbacks without error details. Check + cleanup, status-based error handling, and telemetry for each outcome. +6. With OTel, verify the chosen view and log correlation after resume and under + concurrency. With the Testing SDK, review assertions about invocation counts + and history ordering against its corrected clock and notification behavior + in the [release notes](../release-notes/sdk-3.0.0.md). +7. Delay custom operation-hook work and verify that wrappers see synchronously + initialized state while operations continue. Check any explicit queue's + ordering and recovery after a rejection. Test near-deadline invocation end + with a hook that never settles: `onInvocationEnd` should still run, tolerate + incomplete hook work, and allow the response to return within its time budget. +8. Run a workflow with a pending callback and a slow custom deserializer. + Confirm that the next step runs in the same invocation. Check handler code for + `await`s between durable operations that can take longer than 3 ms, and move + that work into a step. diff --git a/package-lock.json b/package-lock.json index 34695d7e8..05cce09c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15322,7 +15322,7 @@ }, "packages/aws-durable-execution-sdk-js-eslint-plugin": { "name": "@aws/durable-execution-sdk-js-eslint-plugin", - "version": "1.1.1", + "version": "1.2.0", "license": "Apache-2.0", "devDependencies": { "@biomejs/biome": "2.5.15", @@ -16261,7 +16261,7 @@ }, "packages/aws-durable-execution-sdk-js-telemetry-exporter": { "name": "@aws/durable-execution-sdk-js-telemetry-exporter", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "license": "Apache-2.0", "devDependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -16489,7 +16489,7 @@ }, "packages/aws-durable-execution-sdk-js-telemetry-mcp": { "name": "@aws/durable-execution-sdk-js-telemetry-mcp", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.32.1", diff --git a/packages/aws-durable-execution-sdk-js-eslint-plugin/package.json b/packages/aws-durable-execution-sdk-js-eslint-plugin/package.json index 8501f4c8d..93ad51f40 100644 --- a/packages/aws-durable-execution-sdk-js-eslint-plugin/package.json +++ b/packages/aws-durable-execution-sdk-js-eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@aws/durable-execution-sdk-js-eslint-plugin", - "version": "1.1.1", + "version": "1.2.0", "description": "ESLint plugin for AWS Lambda durable functions best practices", "license": "Apache-2.0", "repository": { diff --git a/packages/aws-durable-execution-sdk-js-otel/README.md b/packages/aws-durable-execution-sdk-js-otel/README.md index 73bb9a31c..ad9ef7e34 100644 --- a/packages/aws-durable-execution-sdk-js-otel/README.md +++ b/packages/aws-durable-execution-sdk-js-otel/README.md @@ -1047,6 +1047,9 @@ invent the future Source/Processor/Destination structures. ## Migration from the v1 provider contract +Start with the [Core 3.0 / OTel 2.0 migration guide](../../docs/migration-2.x-to-3.0.md#migrate-opentelemetry-registrations) +for the coordinated dependency upgrade and before/after registration examples. + With core 3.x, legacy providers declaring `pluginApiVersion` fail with `PluginLoadError` for explicit and environment registration. Upgrade the OTel package/layer to the compatible 2.x factory implementation and use diff --git a/packages/aws-durable-execution-sdk-js-telemetry-exporter/package.json b/packages/aws-durable-execution-sdk-js-telemetry-exporter/package.json index 6a55bd45e..de93d9e48 100644 --- a/packages/aws-durable-execution-sdk-js-telemetry-exporter/package.json +++ b/packages/aws-durable-execution-sdk-js-telemetry-exporter/package.json @@ -2,7 +2,7 @@ "name": "@aws/durable-execution-sdk-js-telemetry-exporter", "description": "Durable Execution Telemetry plugin for AWS Durable Execution SDK — curated execution observability", "license": "Apache-2.0", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "private": false, "repository": { "type": "git", diff --git a/packages/aws-durable-execution-sdk-js-telemetry-mcp/package.json b/packages/aws-durable-execution-sdk-js-telemetry-mcp/package.json index aa9847afe..2673d0804 100644 --- a/packages/aws-durable-execution-sdk-js-telemetry-mcp/package.json +++ b/packages/aws-durable-execution-sdk-js-telemetry-mcp/package.json @@ -1,6 +1,6 @@ { "name": "@aws/durable-execution-sdk-js-telemetry-mcp", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "license": "Apache-2.0", "private": false, "repository": { diff --git a/packages/aws-durable-execution-sdk-js/README.md b/packages/aws-durable-execution-sdk-js/README.md index efed1b53e..d68122015 100644 --- a/packages/aws-durable-execution-sdk-js/README.md +++ b/packages/aws-durable-execution-sdk-js/README.md @@ -714,6 +714,9 @@ This project is licensed under the Apache-2.0 License. ## SDK 3.x plugin migration +See the [2.x to 3.0 migration guide](../../docs/migration-2.x-to-3.0.md) for +package versions, before/after examples, provider packaging, and validation. + SDK 3.x requires an explicit migration from the legacy v1 provider contract. Providers declaring `pluginApiVersion` are rejected with `PluginLoadError` in both `plugins` and `DURABLE_EXECUTION_PLUGINS` registration. They are not silently diff --git a/release-notes/sdk-3.0.0.md b/release-notes/sdk-3.0.0.md new file mode 100644 index 000000000..7b3348680 --- /dev/null +++ b/release-notes/sdk-3.0.0.md @@ -0,0 +1,115 @@ +# SDK 3.0.0 / OTel 2.0.0 / Testing SDK 1.3.2 + +This release contains Core 3.0.0, OTel 2.0.0, Testing SDK 1.3.2, Telemetry +Exporter 0.1.0-alpha.1, Telemetry MCP 0.1.0-alpha.1 and ESLint plugin 1.2.0. +Core 3 and OTel 2 are major releases for plugin authors. Upgrade them together. +The [2.x to 3.0 migration guide](../docs/migration-2.x-to-3.0.md) lists the +porting steps. + +# @aws/durable-execution-sdk-js 3.0.0 + +## Breaking and behavior changes + +- `DurableExecutionConfig.plugins` accepts factory objects with a synchronous + `createPlugin(info)` method. The SDK calls it once per invocation. So plugin + state no longer crosses concurrent Lambda Managed Instances requests. A v1 + provider or a plugin instance fails with `PluginLoadError`. 6597e075 +- The SDK no longer awaits `onOperationStart`, `onOperationEnd`, + `onOperationAttemptStart`, `onOperationAttemptEnd` or `onOperationChange`. + The synchronous part of each hook still runs in call order. The asynchronous + parts can overlap. Before `onInvocationEnd`, the SDK waits for that work until + 500 ms before the deadline. It waits for `onInvocationEnd` until 50 ms before + the deadline. 4e4e9407 +- A failed `context.invoke` throws `InvokeError` as before. Its `cause.name` is + now the invoked function's error type, not `"Error"`. a0d310f5 +- The SDK suspends an idle invocation 3 ms after the last operation goes idle, + not 20 ms. The SDK does not track an `await` in handler code between two + operations. So such an `await` longer than 3 ms can let the invocation suspend + before the next operation starts. c0a7e349 + +## Fixes and additions + +- Factories and hooks receive the invocation-local `context.xRayTraceId` + carrier. 6597e075 +- `createPowertoolsLogger` adds durable context to Powertools log entries. + 3542d025 +- A reused logger takes its identity from the active execution context. + eacf9df2 +- An operation stays `EXECUTING` while its result is deserialized. Before, a + slow deserializer next to a pending callback could let the invocation suspend + early. A deserializer that fails slowly now fails the invocation. a6428832 +- A `wrap` hook that rejects for its own reasons no longer fails the + invocation. A context without a usable `getRemainingTimeInMillis` counts as + having no deadline. 4e4e9407 +- The instrumentation plugin interface is no longer `@experimental` or `@beta`. + 8e5e3a44 +- After a `map` or `parallel` completes early, the invocation can suspend + again. Before, a still-running step in an abandoned item blocked suspension. + 0cb2d80f +- A resumed `map` or `parallel` keeps every recorded item outcome. No item + starts after the batch resolves. 2e98408d, f753d364 + +# @aws/durable-execution-sdk-js-otel 2.0.0 + +## Breaking changes + +- `createExecutionOtelPluginFactory` and `createInvocationOtelPluginFactory` + replace constructing the plugin classes. OTel 2 requires Core + `>=3.0.0 <4.0.0`, also in Lambda layers. 6597e075 +- The execution view reports `faas.max_memory` in bytes, not MiB. So a 1024 MiB + function reports `1073741824`. Update queries and alarms that read it. + e049d07c + +## Fixes + +- Spans, operation maps, replay state and clocks belong to each plugin + instance. The provider stays shared. 6597e075 +- Trace and sampling data come from the invocation-local carrier. 6597e075 + +See the [OTel migration reference](../packages/aws-durable-execution-sdk-js-otel/README.md#migration-from-the-v1-provider-contract). + +# @aws/durable-execution-sdk-js-testing 1.3.2 + +- The Core peer range is `>=1.0.1 <4.0.0`. So it accepts Core 3. 6597e075 +- A settled invocation no longer starts an extra invocation from a queued + wakeup. 9462cd92 +- The cloud runner stores an operation before it notifies history waiters. + 417497f0 +- Skip-time keeps checkpoint timestamps, delays and nested invocations on the + runner's clock. bcb41eba +- An abandoned durable invoke no longer holds its caller's fake clock. 8b8ebe88 +- Skip-time keeps advancing after a runner's clock hold expires. e9689d7e + +Upgrade the Testing SDK before Core 3. Testing SDK 1.3.1 excludes Core 3 from +its peer range. A timer inside a step runs in real time. Use a durable wait to +test a durable delay. The corrected scheduling can change recorded invocation +counts and history order. + +# @aws/durable-execution-sdk-js-telemetry-exporter 0.1.0-alpha.1 + +- Every error carries its cause chain as `{ name, message, cause? }`. A record + whose causes were removed to fit a size limit has `droppedCauses: true`. + 2e9031d4, e25244ad +- `SQSExporter` hashes a FIFO group or deduplication id longer than 128 + characters. Before, SQS rejected these sends. 2e9031d4 +- `emittedAt` strictly increases within an invocation. Before, two records in + one millisecond collided in SQS and DynamoDB. 2676e48c + +The package remains a preview on the `beta` dist-tag. + +# @aws/durable-execution-sdk-js-telemetry-mcp 0.1.0-alpha.1 + +- `list_executions` validates `since` and `until`, and reads a bound without an + offset as UTC. 9840138c +- The bundled Athena DDL uses `timestamp` time columns, keeps key case and adds + `droppedcauses`. e25244ad, 9840138c, 16b6f28b +- `test_destination` no longer requires `DURABLE_TELEMETRY_ATHENA_S3_LOCATION` + for the `s3` destination. 16b6f28b + +The package remains a preview on the `beta` dist-tag. + +# @aws/durable-execution-sdk-js-eslint-plugin 1.2.0 + +- The new rule `require-durable-operation-in-child-context` reports a child + context function that calls no durable operation. It is not in + `flat/recommended`. 3cbb456c, e2f013fa