func init() vs func main() для инициализации глобального состояния в обработчиках AWS Lambda

Посмотрите на раздел Использование глобального состояния в официальном обработчике функций AWS Lambda в документе Go https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html

предлагает инициализировать все глобальное состояние, т.е. любые переменные уровня пакета, которые мы хотим разделить между несколькими лямбда-вызовами, идут сюда.
Насколько я понимаю, эта инициализация выполняется один раз при запуске лямбда-контейнера (то есть при холодном запуске).

У меня вопрос, можно ли сделать то же самое, используя вместо.
Использование в основном делает мой обработчик функцией ( func LambdaHandler) не подлежит модульному тестированию из-за побочных эффектов от запуска.
Кажется, что перемещение кода в папку решает эту проблему легко.
Есть ли побочные эффекты при использовании func main() против func init()

Пример кода

Использование func init()

      package main
 
import (
        "log"
        "github.com/aws/aws-lambda-go/lambda"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
        "github.com/aws/aws-sdk-go/aws"
)
 
var invokeCount = 0
var myObjects []*s3.Object
func init() {
        svc := s3.New(session.New())
        input := &s3.ListObjectsV2Input{
                Bucket: aws.String("examplebucket"),
        }
        result, _ := svc.ListObjectsV2(input)
        myObjects = result.Contents
}
 
func LambdaHandler() (int, error) {
        invokeCount = invokeCount + 1
        log.Print(myObjects)
        return invokeCount, nil
}
 
func main() {
        lambda.Start(LambdaHandler)
}

против

Использование func main()

      package main
 
import (
        "log"
        "github.com/aws/aws-lambda-go/lambda"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
        "github.com/aws/aws-sdk-go/aws"
)
 
var invokeCount = 0
var myObjects []*s3.Object
 
func LambdaHandler() (int, error) {
        invokeCount = invokeCount + 1
        log.Print(myObjects)
        return invokeCount, nil
}
 
func main() {
        svc := s3.New(session.New())
        input := &s3.ListObjectsV2Input{
                Bucket: aws.String("examplebucket"),
        }
        result, _ := svc.ListObjectsV2(input)
        myObjects = result.Contents

        lambda.Start(LambdaHandler)
}

1 ответ

Я бы предложил следующее (которое мы успешно используем во многих лямбдах Go).

main.go

      [...]

func (h *handler) handleRequest(ctx context.Context) error {
    input := h.s3Client.ListObjectsV2Input{
        Bucket: aws.String("examplebucket"),
    }

    [...]
}

type handler struct {
    s3Client s3iface.S3API
}

// main is called only once, when the Lambda is initialised (started for the first time). Code in this function should
// primarily be used to create service clients, read environments variables, read configuration from disk etc.
func main() {
    h := handler{
        s3client: s3.New(session.New()),
    }

    lambda.Start(h.handleRequest)
}

main_test.go

      type ListObjectsV2Mock struct {
    s3iface.S3API

    output *s3.ListObjectsV2Output
}

func TestHandleRequest(t *testing.T) {
    h := handler{
        s3Client: &ListObjectsV2Mock{
            output: &s3.ListObjectsV2Output{...},
        },
    }

    err := h.HandleRequest(context.TODO())
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
}

Очевидно, что не хватает большого количества кода (импорт, обработка ошибок и т. Д.), Но в этом суть.

Другие вопросы по тегам