F# AWS lambda - Parts of asynchronous function are never executed

Viewed 167

I have an asynchronous F# function in an AWS Lambda. However, when testing it, parts of the function are never called. The function is always terminated after 10 seconds, even though the aws-lambda-tools-defaults.json file specifies the timeout to 30 seconds.

Here is the code:

namespace MyProject

open FSharp.Data

type SecretsJson = JsonProvider<"./Resources/Secrets.json", RootName="Secret">

module ApiClient = 
    let testGet (secrets: SecretsJson.Secret) =
        async {      
            printfn "%s" "3. This is sometimes printed out and sometimes not."
            
            let! test = Http.AsyncRequestString("https://postman-echo.com/get?foo1=bar1&foo2=bar2")
            printfn "%s" "4. This is never printed."
            return ""
        }
namespace MyProject

open Amazon.Lambda.Core
open Amazon
open Amazon.S3
open Amazon.S3.Util
open System.IO
open Amazon.S3.Model
open Amazon.SecretsManager.Extensions.Caching

[<assembly: LambdaSerializer(typeof<Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer>)>]
()

type Function() =
member __.FunctionHandler (input: S3EventNotification) (_: ILambdaContext) : System.Threading.Tasks.Task<unit> =
        printfn "%s" "1. This gets printed always."
        
        async {        
            use client = new AmazonS3Client(RegionEndpoint.EUWest1)
            use secretsCache = new SecretsManagerCache()
            
            let! secretsString = secretsCache.GetSecretString "secretsKey" |> Async.AwaitTask
            printfn "%s" "2. Also this is always printed."

            let secrets = SecretsJson.Parse(secretsString)
            let! response = ApiClient.testGet secrets

            printfn "%s" "5. This is never printed."
        } |> Async.StartAsTask

I don't understand this behaviour. I thought that the |> Async.StartAsTask expression will ensure that the whole async block in the Function.FunctionHandler gets executed as a standard C# Task<T>. Or do I have to convert each of my Async<'a> functions into a Task<T>? Or is there a different error in my code that I don't see?

1 Answers

The async code shouldn't be the source of the problem. I tried the following:

open System
open Amazon.Lambda.Core

open FSharp.Data

let loadStuff (context: ILambdaContext) (i:int) =
        async {        

            let! test = Http.AsyncRequestString("https://www.google.com")

            sprintf "i:%i Length: %i" i test.Length
            |> context.Logger.LogLine

        } 

and

type Functions() =

member __.Get (request: APIGatewayProxyRequest) (context: ILambdaContext) =

    async {        

            do! [1..10]
                |>Seq.map (Loader.loadStuff context)
                |>Async.Parallel
                |>Async.Ignore

            do! Async.Sleep(10000)

            do! [11..15]
                |>Seq.map (Loader.loadStuff context)
                |>Async.Parallel
                |>Async.Ignore


    } 
    |> Async.StartAsTask

This takes 12 seconds and outputs:

i:6 Length: 47974
i:3 Length: 47201
i:4 Length: 47200
i:8 Length: 47255
i:5 Length: 47183
i:1 Length: 47145
i:7 Length: 47203
i:9 Length: 47177
i:10 Length: 47202
i:2 Length: 47198
i:14 Length: 47201
i:12 Length: 47155
i:11 Length: 47250
i:13 Length: 47162
i:15 Length: 47130

Consider adding try catch blocks to your code and also using context.Logger.Logline instead of printfn. Also if you have very long running pieces of work e.g. >30secs. Consider breaking them down into more functions and possibly coordinating them with something like serverless workflows.

Related