I’m confused on getting Lambda provisioned concurr...
# help
s
I’m confused on getting Lambda provisioned concurrency working w/ API Gateway (HTTP API). I’ve figured out how to set up prov. concurrency on a function, but API Gateway is still pointing to the
$LATEST
version of the function. how do I make it point to the specific version? and more importantly, how do I ensure that when I update the code and the Lambda version gets bumped, that anything else that depends on that Lambda func updates its reference? I would think if I create an SST function using provisioned concurrency and i pass that function as an API route, it the API Gateway integration would point to the version, but it doesn’t
hang on. maybe I have to pass the
func.currentVersion
into the API route def
f
Hey Sam, yeah I think you do. (I haven’t tried it myself.)
s
hm, guess not
Copy code
Error: Invalid function definition for the "Lambda_GET_/embeds/{embedId}" Function
    at Function.fromDefinition (/Volumes/SuperData/Sites/reelcrafter/v2-microservices/node_modules/@serverless-stack/resources/src/Function.ts:512:11)
    at Api.createFunctionIntegration (/Volumes/SuperData/Sites/reelcrafter/v2-microservices/node_modules/@serverless-stack/resources/src/Api.ts:575:23)
    at Api.addRoute (/Volumes/SuperData/Sites/reelcrafter/v2-microservices/node_modules/@serverless-stack/resources/src/Api.ts:470:26)
well shoot, I’m not really sure how to pass a versioned function to sst.Api
or I have to make a new
lambda.Alias()
using that version, then pass that into the route def
f
hmm.. i think
sst.Api
doesn’t accept a versioned Lambda. Can you try adding the versioned function as a route manually, something like this?
Copy code
import * as apig from "@aws-cdk/aws-apigatewayv2";
import * as apigIntegrations from "@aws-cdk/aws-apigatewayv2-integrations";

// Create the Api without the route with versioned function
const api = new sst.Api(...);

// Create an new route integration with the versioned function
const integration = new apigIntegrations.LambdaProxyIntegration({
  handler: myFunction.currentVersion,
  payloadFormatVersion: apig.PayloadFormatVersion.VERSION_2_0,
});

// Add the new route to the Api
const route = new apig.HttpRoute(scope, `VerionedRoute`, {
  httpApi: api.httpApi,
  routeKey: apig.HttpRouteKey.with("/path/to/route", "GET"),
  integration,
});
Let’s see if this works. We can definitely add support for versioned function.
Let me know if the code snippet above makes sense.
^^ I didn’t tested the code, might have a typo here and there
s
yeah, I think that makes sense. and so once I generate a route like that.. how do I add it to the HTTP API? like this?
Copy code
myApi.addRoutes(this, {
  'GET /things/{id}': route
})
f
The above code should be enough. No need to call
addRoutes
s
I have a complicated setup that requires
addRoutes
. basically I create the “base API” and then pass that into each stack, and each of those stacks adds on the specific routes
f
You can use
addRoutes
for adding normal routes. But for routes that uses a specific Function version,
addRoutes
currently doesn’t support that.
s
ahhh, so that’s why it didn’t work
f
The snippet I shared above bypasses
addRoutes
and manually adds a new route with the Function version.
s
cool 👍 I’m juggling a bit too much to get to it now, but I’ll circle back. for now I just have Pingdom pinging the API endpoints to keep the functions warm 😄 which is probably sufficient, and cheaper than provisioned concurrency which is $20/mo/function
f
If you give it a try and it works, we can add support for funciton version for the Api, so this workaround won’t be required, and you can just call
addRoutes()
and pass in a version.
Sorry for the confusion 🙏
s
no worries! 🙂
it’s a minor and very specific thing. I appreciate all the other awesome changes your team’s been making lately 💪
f
Ah yeah, or even a
Cron
that calls the function would work.
s
that’s probably a better idea. Pingdom only does 1-min & 5-min increments.. neither of those are really ideal. I think a 3-min ping is perfect
f
Thought I owe u a fast reply, here’s a snippet 😁
Copy code
new Cron(this, "Cron", {
  schedule: "rate(3 minutes)",
  job: {
    function: {
      handler: "src/warn.main",
      environment: {
        FUNCTION_NAME: api.getFunction("GET /route/to/warm"),
      },
    },
  },
});
and the function code for
warm.js
Copy code
const lambda = new AWS.Lambda();
await lambda.invoke({
  FunctionName: process.env.FUNCTION_NAME,
  InvocationType: "Event",
}).promise();
s
wow, white glove service 😄 thank you!
part of my latency issue is also the fact that the Nuxt app has to make an API call to fetch data. if I can somehow refactor that app to use Vite SSR, then bring that into the SST stack where the rest of my back end lives, then the SSR app can just use DB-accessing functions directly and save the extra API call
f
Ah gotcha.
h
Hello @Frank I needed the same functionality and I tried the code above to point the api gateway to the new version. It works just fine. One problem though that when you update the stack cdk throws an error on the lambda version with the error “Internal Failure”:
Copy code
UPDATE_FAILED | AWS::Lambda::Version | RestApiLambdaCurrentVersion852D2DA360a377647a092f1d514208146cd7b343 | Internal Failure
I can’t find any information on this. Would you know anything about this or what could I do to debug this ?
f
Hey @Haseeb Naseem, does this happen consistently on retry?
h
Yes it does. As long as provisioned concurrency is defined, any other update to the lambda fails.
f
I see. Can I see how you are defining the Api and the Lambda version?
h
Copy code
this.api = new sst.API(this, "API, {
    customDomain: {
        ...,
    }
});

const LambdaFn = new sst.Function(this, "ApiLambda", {
   ...,
   currentVersionOptions:{
       provisionedConcurrentExecutions: 5,
   }
});
const version = LambdaFn.currentVersion;
const integration = new apigIntegrations.HttpLambdaIntegration("ApiVersionIntegration", LambdaFn.currentVersion, {
    payloadFormatVersion: apig.PayloadFormatVersion.VERSION_2_0
});
const route = new apig.HttpRoute(this, "someroute", {
    httpApi: this.api.httpApi,
    routeKey: apig.HttpRouteKey.with("/api/{proxy+}", apig.HttpMethod.ANY),
    integration
});
f
@Haseeb Naseem I created a repo with the above setup. Can you give it a try and see if it work for you?
h
Yeah sure, where can I find it ?
Ah found it https://github.com/fwang/sst-api-with-lambda-version So i tried it. It worked on the first deploy and as soon as i updated the code or provisioned concurrency it threw an internal failure.
f
Omg.. how did i forget to share the repo link. Glad u found it!
I will give that a try again.
h
Hello @Frank, were you able to reproduce this ?
f
Hey @Haseeb Naseem, sorry for the delayed follow up..
I made a couple updates to the function, and was not able to reproduce this error.
Btw, has the Lambda version worked ever worked in this AWS account previously?
h
Are you also able to update the reserved concurrency as well ?
Yeah the lambda version did work. But as the default way of setting up a function in sst/cdk doesn’t point the api gateway to the version it wasn’t used.
j
I’ve just found this thread by chance. We’re seeing the same error, which is not super helpful
UPDATE_FAILED | AWS::Lambda::Version … | Internal Failure
The CF events don’t give us much more detail
h
I solved it by creating an alias instead of a new version.
j
Yeah I wanted to avoid that but it might do the trick
h
Any particular reason why you wanted to avoid it? Might be that I'm missing some drawbacks.
j
Just additional complexity
I
sst remove
my app and re deployed it again. Now it does not complain when adding the lambda with provisioned capacity
But this is not something we’ll do in production. I’m wondering if it’s not publishing a new version because the actual lambda code has not changed. Since no new version is published, the provisioned concurrency does not get applied (and AWS replies with ‘Internal Failure’)
h
I explored that possibility and did both changes in one deploy ( changing the lambda code and updating the provisioned concurrency) but got the same error.
j
Hey. Just an update here. That was exactly the issue. If you have an existing version and you make a change to the lambda config only (to add provisioned concurrency in this case), it fails (maybe it tries to update the existing version?). The error is just ‘Internal Failure’ so not sure what it’s really happening. However, if you do a code change at the same time (or the lambda source code you’re deploying is different from the last version), then a new one is created successfully with the provisioned concurrency set in it
I can reproduce this successfully
BTW you have to actually deploy your lambda (the sst shim does not change so no new version is published if you’re doing live dev.)
Changing the env vars also works