Introducing Eventarc in Pic-a-Daily Serverless Workshop

Pic-a-Daily Serverless Workshop

As you might know, Guillaume Larforge and I have a Pic-a-Daily Serverless Workshop. In this workshop, we build a picture sharing application using Google Cloud serverless technologies such as Cloud Functions, App Engine, Cloud Run and more.

Pic-a-Daily Serverless Workshop

We recently added a new service to the workshop. In this blog post, I want to talk about the new service. I also want to talk about Eventarc and how it helped us to get events to the new service.

Previous architecture

This was the previous architecture of Pic-a-Daily:

Pic-a-Daily Initial Architecture

To recap:

  1. A user uploads a picture to the pictures bucket via a frontend.
  2. Image analysis Cloud Function receives the upload event, extracts the metadata of the image and saves to Firestore.
  3. Thumbnail service receives the upload event via Pub/Sub, resizes the image and saves to the thumbnails bucket.
  4. Collage service creates a collage of thumbnail pictures.

Image garbage collector service

One problem of the previous architecture was around image deletions. When an image was deleted from the pictures bucket, the thumbnail of the image in thumbnails bucket and the metadata of the image in Firestore lingered around.

To fix this problem, we created a new Cloud Run service, image garbage collector, to clean up after an image is deleted.

To get Cloud Storage events to image garbage collector, we could have used Pub/Sub pushed messages, as we did in thumbnails service. However, we decided to use Eventarc instead.

What is Eventarc

We announced Eventarc back in October as a new eventing functionality that allows you to send events to Cloud Run from more than 60 Google Cloud sources. This is done by reading AuditLogs from various sources and sending them to Cloud Run services as CloudEvents. There is also the option of reading events from Pub/Sub topics for custom applications.

Eventarc Overview

We decided to try Eventarc as a new way to get events. We also wanted to avoid all the setup we had to do in thumbnail service to get Cloud Storage events to to Cloud Run via Pub/Sub. See details.

Current architecture

The current architecture now looks like this with Eventarc and image garbage collector:

Pic-a-Daily Current Architecture

Image garbage collector code

You can check out the full code for image garbage collector here. I’ll highlight some parts of the code.

Image garbage collector receives AuditLogs wrapped into CloudEvents from Eventarc. It first reads the CloudEvent using the CloudEvents SDK:

const { HTTP } = require("cloudevents");
const cloudEvent = HTTP.toEvent({ headers: req.headers, body: req.body });

The data field of the CloudEvent has the actual AuditLog. There’s Google Events library to parse the data into an AuditLog:

const {toLogEntryData} = require('@google/events/cloud/audit/v1/LogEntryData');
const logEntryData = toLogEntryData(;

The rest of the code is to extract bucket and object name from the event and then delete it from thumbnails bucket and Firestore collection:

const tokens = logEntryData.protoPayload.resourceName.split('/');
const bucket = tokens[3];
const objectName = tokens[5];

// Delete from thumbnails
try {
    await storage.bucket(bucketThumbnails).file(objectName).delete();
    console.log(`Deleted '${objectName}' from bucket '${bucketThumbnails}'.`);
catch(err) {
    console.log(`Failed to delete '${objectName}' from bucket '${bucketThumbnails}': ${err}.`);

// Delete from Firestore
try {
    const pictureStore = new Firestore().collection('pictures');
    const docRef = pictureStore.doc(objectName);
    await docRef.delete();

    console.log(`Deleted '${objectName}' from Firestore collection 'pictures'`);
catch(err) {
    console.log(`Failed to delete '${objectName}' from Firestore: ${err}.`);

Create a Trigger

Lastly, we need to connect events from Cloud Storage to the new service via Eventarc. This is done with a trigger:

gcloud beta eventarc triggers create trigger-${SERVICE_NAME} \
  --destination-run-service=${SERVICE_NAME} \
  --destination-run-region=europe-west1 \
  --matching-criteria="" \
  --matching-criteria="" \
  --matching-criteria="methodName=storage.objects.delete" \

Notice that we’re matching for AuditLogs for Cloud Storage and specifically looking for storage.objects.delete events.

If you want to try out our workshop and the new lab, here are some links:

Feel free to reach out to me on Twitter @meteatamel for any questions/feedback.

See also