Cloudtasker - Monitor your Cloud Tasks jobs on GCP
Tags: ruby, rails, googlecloud
Published: 2021-06-25 08:13:31

**TL;DR;** Make sure to properly monitor your Cloud Task queues and job durations so as to detect abnormal loads or jobs doing doing much at once. Using a combination of GCP monitoring and custom GCP log-based metrics can give you a lot of needed insights.

Monitoring is an essential part of maintaining production systems. Ensuring you have the right metrics available in a dashboard will allow you to be more proactive should an issue or unexpected load arise.

This is particularly applicable for background jobs where issues are usually more subtle than with direct server errors. Background job issues tend to slowly creep until you discover that your queues are saturated with retries or timed out jobs.

You can get quick insights using the out-of-the-box metrics provided by GCP and dig deeper using Logs-based metrics. 

In this article I'll cover how to put monitoring metrics in place for Cloud Tasks and [Cloudtasker](https://github.com/keypup-io/cloudtasker).

Below is a list of standard metrics I like to track when it comes to background jobs.

# Cloud Tasks queue size
The first metric to monitor is the queue size. If your queues are going off control, it means you either need to crank up your backend capacity or something is wrong with your jobs and they keep getting re-enqueued.

Use the [metrics explorer](https://console.cloud.google.com/monitoring/metrics-explorer) with the following configuration:
- **Metric:** cloudtasks.googleapis.com/queue/depth
- **Group by:** Queue Id
- **Aggregator:** Sum
- **Period:** 1 minute

This will generate the following chart:
![Cloud Tasks Queue Monitoring](https://uploads-ssl.webflow.com/5ebd54899f7ac255cd8e0950/607d9b25d9bc6cd297e96050_image1.png)

You can also [click here](https://console.cloud.google.com/monitoring/metrics-explorer?pageState=%7B%22xyChart%22:%7B%22dataSets%22:%5B%7B%22timeSeriesFilter%22:%7B%22filter%22:%22metric.type%3D%5C%22cloudtasks.googleapis.com%2Fqueue%2Fdepth%5C%22%20resource.type%3D%5C%22cloud_tasks_queue%5C%22%22,%22minAlignmentPeriod%22:%2260s%22,%22aggregations%22:%5B%7B%22perSeriesAligner%22:%22ALIGN_MEAN%22,%22crossSeriesReducer%22:%22REDUCE_SUM%22,%22groupByFields%22:%5B%22resource.label.%5C%22queue_id%5C%22%22%5D%7D,%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22%7D%5D%7D,%22targetAxis%22:%22Y1%22,%22plotType%22:%22LINE%22%7D%5D,%22options%22:%7B%22mode%22:%22COLOR%22%7D,%22constantLines%22:%5B%5D,%22timeshiftDuration%22:%220s%22,%22y1Axis%22:%7B%22label%22:%22y1Axis%22,%22scale%22:%22LINEAR%22%7D%7D,%22isAutoRefresh%22:true,%22timeSelection%22:%7B%22timeRange%22:%221w%22%7D,%22xZoomDomain%22:%7B%22start%22:%222021-04-16T10:08:31.370Z%22,%22end%22:%222021-04-18T02:18:07.387Z%22%7D%7D) to build this chart automatically.


# Cloud Run billable runtime
For backend services essentially running jobs, monitoring the amount of containers being billed can give you good insights on how (1) how your services are performing under heavy job load and (2) how jobs impact your overall bill at the end of the month.

It's a good metric to have independently of background job monitoring.

Use the [metrics explorer](https://console.cloud.google.com/monitoring/metrics-explorer) with the following configuration:
- **Metric:** run.googleapis.com/container/billable_instance_time
- **Group by:** configuration_name
- **Aggregator:** Sum
- **Period:** 1 minute

This will generate the following chart:
![Cloud Run Billable Time](https://uploads-ssl.webflow.com/5ebd54899f7ac255cd8e0950/607d9b8c5a46a7c6a9f0141f_image2.png)

You can also [click here](https://console.cloud.google.com/monitoring/metrics-explorer?pageState=%7B%22xyChart%22:%7B%22dataSets%22:%5B%7B%22timeSeriesFilter%22:%7B%22filter%22:%22metric.type%3D%5C%22run.googleapis.com%2Fcontainer%2Fbillable_instance_time%5C%22%20resource.type%3D%5C%22cloud_run_revision%5C%22%22,%22minAlignmentPeriod%22:%223600s%22,%22aggregations%22:%5B%7B%22perSeriesAligner%22:%22ALIGN_RATE%22,%22crossSeriesReducer%22:%22REDUCE_SUM%22,%22groupByFields%22:%5B%22resource.label.%5C%22configuration_name%5C%22%22%5D%7D,%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22%7D%5D%7D,%22targetAxis%22:%22Y1%22,%22plotType%22:%22LINE%22%7D%5D,%22options%22:%7B%22mode%22:%22COLOR%22%7D,%22constantLines%22:%5B%5D,%22timeshiftDuration%22:%220s%22,%22y1Axis%22:%7B%22label%22:%22y1Axis%22,%22scale%22:%22LINEAR%22%7D%7D,%22isAutoRefresh%22:true,%22timeSelection%22:%7B%22timeRange%22:%221w%22%7D,%22xZoomDomain%22:%7B%22start%22:%222021-04-15T22:16:42.689Z%22,%22end%22:%222021-04-17T23:06:02.247Z%22%7D%7D) to build this chart automatically.

This chart shows the number of instance ms billed per second. If you see 500ms/s it means your service is actively running 50% of the time. If you see 8s/s it means you have 8 containers actively running.

Cloud Run may have additional containers provisioned on top of the actively running containers (idle containers) but these are only kept to avoid cold starts and are not billed to you.

# Job duration (Cloudtasker-specific)
Measuring job duration is essential to know if you need to break down your jobs and to detect issues with slow downstream services (database, cache store, third-party API).

Cloud Task job duration is not a native GCP metric. You need to create this metric using log-based data.

The [Cloudtasker](https://github.com/keypup-io/cloudtasker) gem automatically adds a duration field on each "Job done" log entry. This value can be used to create a metric on job duration.

First create a [new GCP Log-based metric](https://console.cloud.google.com/logs/metrics/edit) from GCP Logging > Logs-based metric.

Select **Distribution** as the metric type, name it **cloudtasker/job_duration** and enter **"s"** (seconds) for the unit.

For the filter use the following query:
```sql
resource.type="cloud_run_revision"
jsonPayload.name="Cloudtasker"
jsonPayload.payload.duration >= 0
```

Finally enter **jsonPayload.payload.duration** for the field name and save your metric.

![Custom Job Duration Metric](https://uploads-ssl.webflow.com/5ebd54899f7ac255cd8e0950/607da246ff230218fa197b15_image3_metrics.png)

The metric is not retroactive and you will need to wait a few minutes before data become available (depending on your job volume).

Now that you have this custom metric in place, let's create a chart displaying the log duration per service.

Use the [metrics explorer](https://console.cloud.google.com/monitoring/metrics-explorer) with the following configuration:
- **Metric:** logging.googleapis.com/user/cloudtasker/job_duration
- **Group by:** service_name
- **Aggregator:** 99th percentile
- **Period:** 1 minute

This will generate the following chart:
![Cloudtasker Job duration](https://uploads-ssl.webflow.com/5ebd54899f7ac255cd8e0950/607d9c8d206d04fc7a68f577_image3.png)

You can also [click here](https://console.cloud.google.com/monitoring/metrics-explorer?pageState=%7B%22xyChart%22:%7B%22dataSets%22:%5B%7B%22timeSeriesFilter%22:%7B%22filter%22:%22metric.type%3D%5C%22logging.googleapis.com%2Fuser%2Fcloudtasker%2Fjob_duration%5C%22%20resource.type%3D%5C%22cloud_run_revision%5C%22%22,%22minAlignmentPeriod%22:%2260s%22,%22aggregations%22:%5B%7B%22perSeriesAligner%22:%22ALIGN_DELTA%22,%22crossSeriesReducer%22:%22REDUCE_PERCENTILE_99%22,%22groupByFields%22:%5B%22resource.label.%5C%22service_name%5C%22%22%5D%7D,%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22%7D%5D%7D,%22targetAxis%22:%22Y1%22,%22plotType%22:%22LINE%22%7D%5D,%22options%22:%7B%22mode%22:%22COLOR%22%7D,%22constantLines%22:%5B%5D,%22timeshiftDuration%22:%220s%22,%22y1Axis%22:%7B%22label%22:%22y1Axis%22,%22scale%22:%22LINEAR%22%7D%7D,%22isAutoRefresh%22:true,%22timeSelection%22:%7B%22timeRange%22:%221h%22%7D%7D) to build this chart automatically.

# Redis memory (Cloudtasker-specific)
If you use [Cloudtasker Batch](https://github.com/keypup-io/cloudtasker/blob/master/docs/BATCH_JOBS.md) or [payload storage in Redis](https://github.com/keypup-io/cloudtasker#option-1-use-cloudtasker-optional-support-for-payload-storage-in-redis) then you better monitor your Redis memory usage. 

Reaching your maximum memory on Redis means you'll have jobs failing due to lack of memory OR because keys will have been evicted automatically (depending on your maxmemory-policy on Redis).

Use the [metrics explorer](https://console.cloud.google.com/monitoring/metrics-explorer) with the following configuration:
- **Metric:** redis.googleapis.com/stats/memory/usage
- **Aggregator:** None
- **Period:** 1 minute

This will generate the following chart:
![Redis Memory Usage](https://uploads-ssl.webflow.com/5ebd54899f7ac255cd8e0950/607d9c7ee4451393c903a69f_image4.png)

You can also [click here](https://console.cloud.google.com/monitoring/metrics-explorer?pageState=%7B%22xyChart%22:%7B%22dataSets%22:%5B%7B%22timeSeriesFilter%22:%7B%22filter%22:%22metric.type%3D%5C%22redis.googleapis.com%2Fstats%2Fmemory%2Fusage%5C%22%20resource.type%3D%5C%22redis_instance%5C%22%22,%22minAlignmentPeriod%22:%2260s%22,%22aggregations%22:%5B%7B%22perSeriesAligner%22:%22ALIGN_MEAN%22,%22crossSeriesReducer%22:%22REDUCE_NONE%22,%22groupByFields%22:%5B%5D%7D,%7B%22crossSeriesReducer%22:%22REDUCE_NONE%22%7D%5D%7D,%22targetAxis%22:%22Y1%22,%22plotType%22:%22LINE%22%7D%5D,%22options%22:%7B%22mode%22:%22COLOR%22%7D,%22constantLines%22:%5B%5D,%22timeshiftDuration%22:%220s%22,%22y1Axis%22:%7B%22label%22:%22y1Axis%22,%22scale%22:%22LINEAR%22%7D%7D,%22isAutoRefresh%22:true,%22timeSelection%22:%7B%22timeRange%22:%221h%22%7D%7D) to build this chart automatically.

Note that it is usually good practice to separate your Rails cache Redis instance from your Cloudtasker Redis instance. 

General caching with Rails is usually non-critical and can generally be repopulated at runtime. 

On the other side Cloudtasker uses Redis as a persistent store for Batch jobs and payload storage - storage is therefore critical and letting Redis evict Cloudtasker keys will lead to failed jobs.

# Wrapping up
The four metrics above should give you a nice overview of how your job processing load is being handled by your platform.

If you need more fine-tuned metrics you should create new Logs-based metrics to extract and aggregate specific log values, the same way we did for the "Job duration" metric.

You could even go as far as logging specific values using the Cloudtasker logger (e.g. API call response time) and then capturing these values in custom log-based metrics on GCP. Possibilities are endless from there!