Go includes several primitives and principles that enable concurrency, including:

  • goroutines - lightweight threads managed by the Go runtime and multiplexed across multiple operating system threads
  • channels - first-class values that enable communication between goroutines
  • waitgroups - a type that waits for a collection of goroutines to finish

Typical uses

In a serverless app, you would typically choose to use goroutines for use cases when the event source that invokes your AWS Lambda function passes a collection of objects in its event body.

For example, AWS Cognito generates a SyncTrigger event similar to the following:

  "version": 2,
  "eventType": "SyncTrigger",
  "region": "us-east-1",
  "identityPoolId": "identityPoolId",
  "identityId": "identityId",
  "datasetName": "datasetName",
  "datasetRecords": {
    "SampleKey1": {
      "oldValue": "oldValue1",
      "newValue": "newValue1",
      "op": "replace"
    "SampleKey2": {
      "oldValue": "oldValue2",
      "newValue": "newValue2",
      "op": "replace"

The DatasetRecords property contains an arbitrary number of records, each of which needs to be processed by your function. You could then process the event similar to the following:

var wg sync.WaitGroup

for record, _ := range(event.DatasetRecords) {

    go func(DatasetRecord) {
        defer wg.Done()

        // Some asynchronous/concurrent business logic...



// Some (optional) final operation on the aggregate result...