aws-amplify / amplify-backend

Home to all tools related to Amplify's code-first DX (Gen 2) for building fullstack apps on AWS
Apache License 2.0
164 stars 55 forks source link

Lambda function timeout overrode to 30 secs by Amplify even when set to longer in the function configuration #2026

Open mblanche opened 2 days ago

mblanche commented 2 days ago

I'm running into a little annoying glitch... Setting up a lambda function timeout seems to get override by the Amplify deployment. I polling the content of an FTP website and recursive search takes about a minute, I really need to get his through...

Here's a playground example using a lambda function that goes into sleep for a number of seconds define by the front-end, everything successfully return as long as the sleep time is less than 30 seconds, even though the timeout setting on the function is set to 2 minutes...

Any chance this can look at soon?

Thanks team.

BTW: Great work on an excellent package overall!

Environment information

System:
  OS: macOS 14.6.1
  CPU: (8) arm64 Apple M3
  Memory: 114.27 MB / 24.00 GB
  Shell: /bin/bash
Binaries:
  Node: 22.2.0 - ~/.nvm/versions/node/v22.2.0/bin/node
  Yarn: 1.22.22 - ~/.nvm/versions/node/v22.2.0/bin/yarn
  npm: 10.8.3 - ~/.nvm/versions/node/v22.2.0/bin/npm
  pnpm: 9.8.0 - ~/.nvm/versions/node/v22.2.0/bin/pnpm
NPM Packages:
  @aws-amplify/auth-construct: 1.3.0
  @aws-amplify/backend: 1.2.1
  @aws-amplify/backend-auth: 1.1.4
  @aws-amplify/backend-cli: 1.2.6
  @aws-amplify/backend-data: 1.1.3
  @aws-amplify/backend-deployer: 1.1.2
  @aws-amplify/backend-function: 1.4.0
  @aws-amplify/backend-output-schemas: 1.2.0
  @aws-amplify/backend-output-storage: 1.1.1
  @aws-amplify/backend-secret: 1.1.1
  @aws-amplify/backend-storage: 1.1.2
  @aws-amplify/cli-core: 1.1.2
  @aws-amplify/client-config: 1.3.0
  @aws-amplify/deployed-backend-client: 1.4.0
  @aws-amplify/form-generator: 1.0.1
  @aws-amplify/model-generator: 1.0.6
  @aws-amplify/platform-core: 1.1.0
  @aws-amplify/plugin-types: 1.2.1
  @aws-amplify/sandbox: 1.2.1
  @aws-amplify/schema-generator: 1.2.2
  aws-amplify: 6.6.0
  aws-cdk: 2.158.0
  aws-cdk-lib: 2.158.0
  typescript: 5.6.2
AWS environment variables:
  AWS_STS_REGIONAL_ENDPOINTS = regional
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  AWS_SDK_LOAD_CONFIG = 1
No CDK environment variables

Describe the bug

Lambda function exiting after 30 secs no matter what the timeoutSeconds value in resource.ts is.

Expected Behavior

Function return with success after 60 seconds, well below the timeout set in the function resource.ts

Reproduction steps

amplify/data/test-lambda-timeout/resource.ts

import { defineFunction } from '@aws-amplify/backend'

export const testLambdaTimeout = defineFunction({
  name: 'test-lambda-timeout',
  timeoutSeconds: 2 * 60, // 2 minute timeout
})

amplify/data/test-lambda-timeout/handler.ts

import type { Schema } from '../resource'
type Handler = Schema['testLambdaTimeout']['functionHandler']

export const handler: Handler = async (event) => {
  const seconds = event.arguments.seconds || 5

  await myTimeout(seconds)
  return `Waited ${seconds} seconds`
}

const myTimeout = (seconds: number) =>
  new Promise((resolve) => {
    console.log(`Waiting for ${seconds}`)
    setTimeout(resolve, seconds * 1000)
  })

amplify/data/resource.ts

import { type ClientSchema, a, defineData } from '@aws-amplify/backend'
import { testLambdaTimeout } from './test-lambda-timeout/resource'

const schema = a.schema({
  testLambdaTimeout: a
    .query()
    .arguments({
      seconds: a.integer(),
    })
    .authorization((allow) => [allow.authenticated()])
    .handler(a.handler.function(testLambdaTimeout))
    .returns(a.json()),
})

src/main.ts

import { Schema } from '@amplify/data/resource'
import { generateClient } from 'aws-amplify/api'

const client = generateClient<Schema>()
let counter = 0
let isRunning = false
let interval: ReturnType<typeof setInterval>
const seconds = 60

const onClick = async () => {
  toggleTimer()
  const { data, errors } = await client.queries.testLambdaTimeout({
    seconds: seconds,
  })

  if (errors) {
    console.log(
      `Wait time set to ${seconds} seconds, but exited with error after ${counter} seconds with errors:`,
      errors
    )
    toggleTimer()
  }
  if (data) {
    console.log(
      `Exited with success after ${counter} seconds with return:`,
      data
    )
    toggleTimer()
  }
}

const toggleTimer = () => {
  isRunning = !isRunning
  if (isRunning) {
    counter = 0
    interval = setInterval(() => ++counter, 1000)
  } else {
    clearInterval(interval)
    console.log('timer stops')
  }
}

onClick()

Additional info

The function does have a 5*60 second timeout configured:

$ aws lambda get-function-configuration \
    --function-name amplify-updateducbpdui-ma-testlambdatimeoutlambdaB-WUtOKcPZHgMu \
    --query 'Timeout'
300
ykethan commented 1 day ago

Hey @mblanche, thank you for reaching out. AWS AppSync has a limit of 30 seconds on query execution time. But with the latest release you should be able to use to use Async function handlers allow you to execute long-running operations asynchronously. refer to documentation providing this information.

mblanche commented 1 day ago

What version of @aws-amplify/backend is this functionality is implemented?

Also, could you suggest a strategy/pattern for functions needing to return data to the client?

Thanks

ykethan commented 1 day ago

@mblanche, this should be in @aws-amplify/data-schema@1.6.0. you could try running npm update @aws-amplify/data-schema to update this. Could you provide us additional information on your use case to better assist you?

mblanche commented 1 day ago

Thanks, got @aws-amplify/data-schema to 1.6.0 and @aws-amplify/backend to 1.2.2.

The Fire and Forget model seems to have limited applications from a first glance... Jobs taking longer than 30 secs would benefit from a mechanism to poll/monitor progress and completion, I think...

My need right now is to recursively poll an FTP server for file paths carrying specific prefixes, build a list of file paths that need to be download then transfer the download requests to Fargate tasks on ECS to copy the file on S3. Surveying the FTP server takes some time (usually less than 30 seconds but depending on latency sometime up to a minute or so...). The files can be pretty massive, ranging from a few Gb close to a fraction of Tb, hence passing the copy request to Fargate.

Thanks for any suggestions. Adding more patterns to the docs could prove helpful!