Asynchronous processing with Cloud Run and Pub/Sub

Erik
Dreamwod tech
Published in
3 min readFeb 8, 2021

Secure asynchronous processing with Cloud Run through Pub/Sub.

Background

Cloud Run will throttle the CPU when there isn’t an active request processing. That means that it isn’t possible to spawn a new thread or goroutine to do background processing within a Cloud Run API. Background processing can for example be to send an email during registration or to send push notifications when a user likes a post, something that the user doesn’t have to wait for.

This article will describe a solution for asynchronous processing where an API running on Cloud Run will send events to Pub/Sub. These events will then be sent back to the API through a Pub/Sub push subscription.

Overview
  1. An API request is sent to a Cloud Run service.
  2. The API request is processed and events are sent to an event topic in Pub/Sub.
  3. The API response is returned back the client and the CPU will be throttled.
  4. Pub/Sub pushes an event back to the API Service for asynchronous processing.

In our example, we will have a golang REST API with two methods.

POST /change-name    // Used to change the name of a user
POST /events // Used by Pub/Sub for processing events

Source code and example

The library used for signing and consuming events and an end-to-end example is available at https://github.com/eripe970/pubsub-signing.

Create events

Pub/Sub required that the Cloud Run allows all public traffic to be able to receive events. It supports authentication of requests and as an extra security measurement, we will sign each message to be sure that they are sent from the API. The GCP requirement that the API must be public means that we cannot have an internal API that handles the events.

Example event for changing the name

The event-type is stored on the message as an attribute. Each message is signed with a secret and the signature is verified when the event is processed in the event handler in the API.

Creating, signing, and publishing an event to Pub/Sub

The contents of the event will look like below.

{
"message": {
"attributes": {
"event-type": "user.name.changed",
"signature": "cc6d0457d8b4ec0e994d793302cd6962a0d12101abbc79e561f532a826eca4ee"
},
"data": "eyJOYW1lIjoibmV3LW5hbWUifQ=="
},
"subscription": "api-events"
}

Event-type will be used for the deserialization of the event when it is processed in the api.

The signature is a hex-encoded signature that will be verified when the message is deserialized.

Data is base64 encoded payload (automatically encoded by GCP).

Subscription can be used to know which subscription the message originates from (automatically added by GCP).

Consume Events

The code below constructs the message and validates the signature. The event will also be deserialized depending on its type.

Example code for consuming an event

Developing locally

One option when developing locally is to use the Pub/Sub emulator from https://cloud.google.com/pubsub/docs/emulator. Another option is to have two implementations for publishing messages. One to use at GCP that publishes messages to Pub/Sub and one to use locally that makes a HTTP request directly back to the API.

Example of implementation for GCP and local development

Cloud Run vs Cloud Functions

Events can also be processed by a Cloud Function instead of an API on Cloud Run. Some pros with processing with Cloud Run instead of Cloud Function.

  • Support languages/versions that Cloud Functions does not yet support (any docker image).
  • Cloud Run can handle multiple types of events/messages in one deployment.
  • May not be impacted by cold starts in the same way as Cloud Functions.

Summary

Using Cloud Run in combination with a push subscription on Pub/Sub is a good alternative to Cloud Functions for handling asynchronous processing of events. Try it out!

--

--

Erik
Dreamwod tech

Developer, backend, frontend, ML. Likes crossfit and training. Building on the app dreamwod.app.