fix: use alias for versioned lambda functions#6767
fix: use alias for versioned lambda functions#6767anatolzak wants to merge 6 commits intoanomalyco:devfrom
Conversation
vimtor
left a comment
There was a problem hiding this comment.
thanks for your contribution @anatolzak
i left some comments
| tags: args.tags, | ||
| publish: output(args.versioning).apply( | ||
| (v) => v ?? Boolean(args.durable), | ||
| (v) => v || Boolean(args.durable), |
| }; | ||
| }); | ||
| } | ||
| type NormalizedUrl = ReturnType<typeof normalizeUrl>; |
There was a problem hiding this comment.
i don't think we need this
|
|
||
| function normalizeUrl() { | ||
| return output(args.url).apply((url) => { | ||
| function normalizeUrl(url: FunctionArgs["url"]) { |
There was a problem hiding this comment.
the normalize function usually don't take arguments
| }); | ||
| } | ||
|
|
||
| type CreateUrlOpts = { |
There was a problem hiding this comment.
inline types like this are very weird
| * ID generation inside durable operations like `ctx.step()`. | ||
| * | ||
| * :::caution | ||
| * Workflow handlers have versioning enabled. Deploying an update won't update existing running workflows. |
There was a problem hiding this comment.
we should keep the versioned behaviour. i don't understand why we changed this
There was a problem hiding this comment.
@vimtor The versioned behavior is unchanged. The main reason durable workflows version the handler is so that an in-flight execution resumes on the same code it started on. That's preserved: the lanbda API captures the resolved version at execution start and pins all resumes/retries to that captured version, regardless of whether the alias is later moved.
What the alias buys us, beyond what a pinned numeric version gives, is decoupling the handler identity from the deployed version. If you ship a bad release and need to roll back, you just point the alias at the previous version, and no consumers have to be updated. Any newly-started executions immediately run against the rolled-back version. With a pinned qualifiedArn, every consumer (URL, event sources, API GW, SDK qualifier, etc.) would have to be re-wired to roll back.
It also opens the door to weighted aliases: you can split traffic across two versions of a workflow handler for A/B tests or canary rollouts without touching any client. None of that is possible if consumers point at a specific numeric version.
The doc rewrite just makes the mechanism explicit and adds the $LATEST escape hatch. No behavior regression.
| return this.useQualifiedTarget.apply((useQualifiedTarget) => | ||
| useQualifiedTarget ? this.function.qualifiedArn : this.arn, | ||
| ); | ||
| return this.#targetArn; |
There was a problem hiding this comment.
why don't define targetArn here instead of adding this #targetArn?
| const zipAsset = createZipAsset(); | ||
| const fn = createFunction(); | ||
| const urlEndpoint = createUrl(); | ||
| const isVersioningEnabled = fn.publish.apply((publish) => Boolean(publish)); |
There was a problem hiding this comment.
you can also define versioning with versioning: true
There was a problem hiding this comment.
@vimtor fn.publish is the resolved truth on the underlying aws.lambda.Function. Upstream at function.ts:2659, it's set to args.versioning || Boolean(args.durable), so checking fn.publish covers both paths in one expression
| }); | ||
| } | ||
|
|
||
| function createTargetArn(alias: Input<lambda.Alias | undefined>) { |
There was a problem hiding this comment.
this is weird because it's not creating anything. that's why i said to use the getter directly in a comment below
|
|
||
| function createLatestUrl() { | ||
| return qualifier.apply((qualifier) => | ||
| createUrl({ |
There was a problem hiding this comment.
we should inline the createUrl function
or keep the createUrl, remove this createLatestUrl and move the logic to the existing method
|
@vimtor thanks for the review! I think I addressed all your comments. A couple of notes on the changes you flagged: The On using a private The chain is: Let me know if there's anything else you'd like changed. |
04350f1 to
1e105c1
Compare
closes #6722 #6720
Summary
Reworks function versioning to always create a persistent
LatestAliaswhenversioning(ordurable) is enabled.targetArn,qualifier, Function URLs, URL permissions, and invoke ARNs now all route through this alias instead of a pinned numeric version.Previously:
targetArnpointed atfn.qualifiedArn(a specific published version number that churns each deploy).${name}Durablealias inline, but only for the Function URL — other consumers still got the pinned version ARN.Now:
${name}LatestAliasis created whenever the function is published, and every downstream integration (URL, event sources, API GW, cron, workflow, SDK link, etc.) targets it.fn.qualifierresolves to the alias name when versioning is on, andundefinedwhen it isn't.$LATEST) as soon as versioning is enabled.This is groundwork for upcoming rollout support — we need a stable indirection in front of versioned functions before we can implement traffic shifting.
What I tested
Resource.<Fn>.qualifiervia the SDK — resolves to the alias name when versioning is enabled, and has no value when it isn't.lambda:InvokeFunctionpermission is scoped to the alias.targetResponseStreamingInvokeArnnow rewrites against the alias ARN, and streamed invocations work.Upgrade notes
For any stack that previously enabled
versioning: trueordurable: truewith a URL, the Function URL will be replaced:versioning: true: the existing URL currently points at$LATEST. After upgrade it will point at the alias — a new URL will be issued.durable: true: the alias changes from the old inline${name}Durableto${name}LatestAlias, which also causes a new URL to be issued.Event sources that consume
fn.targetArn(buckets, topics, crons, API Gateway integrations, etc.) will see a one-time update on upgrade as the ARN moves from a pinned version to the alias. This isn't new churn — previously, every deploy that changed function code published a new version, which forced those same event sources and permissions to update their target. With the alias, this happens once and then stops: subsequent deploys keep pointing at the same alias ARN, so downstream resources no longer need to be touched on every deploy.