Functions in Oracle Cloud Infrastructure are great. As a serverless execution environment with pre-built logging, metrics, etc. it allows developers to simply focus on their code and not worry about all of the supporting infrastructure, while still providing a lot of flexibility through the use of container primitives. As great as Functions are, they are reactive, they can only be invoked and can’t natively be configured to be executed in a spontaneous or scheduled manner. Often this won’t matter, as Functions will be invoked directly or indirectly by users, or in response to events, but sometimes you simply need a bit of code to run periodically.
In a previous blog post, I used Health Checks and API Gateway in order to invoke a function every 10 minutes. This worked well, was very lightweight and used only Oracle-managed services, so almost no ongoing maintenance requirements (I did rebuild the function with an updated base container and upgraded libraries once). Unfortunately, this approach did have some downsides – the Function was effectively able to be invoked publicly from the internet, API Gateway was underutilised, and Health Checks required that the invocation be at least once per 10 minutes. If you wanted to invoke the Function once per hour or once per day, Health Checks wasn’t able to support the model.
Recently I stumbled upon a capability which caused me to revisit this approach for invoking Functions, and this is a capability within the Oracle Cloud Infrastructure (OCI) Monitoring capabilities to define alarms. This is a core OCI capability which allows for thresholds to be set on various metrics to trigger alerts, such as alarming on high CPU utilisation. These alarms, when triggered can be actioned through the use of Oracle Cloud Infrastructure Notifications, which has capabilities such as sending emails or SMS messages, alerting using Slack or PagerDuty, and invoking a Function, which is exactly what I was looking to achieve. The intention of this is to be able to perform automated remediation, or alternatively integrate other alerting mechanisms (i.e. create a IT service ticket via API invocation), but we can also ignore the alert message and simply invoke a function at an interval.
Alarms provides a tuneable mechanism for periodic invocation which is a lot more flexible than Health Checks, as Alarms can be configured to repeat their Notification at an interval, which can be as often as every minute, or as infrequent as once every 30 days.
Let’s look at how to go about configuring this. I am going to assume you have developed and configured your function. In this example, I am just using a simple function that writes an object to object store with the current timestamp, to easily track the invocation frequency.
The first required component is a Notification Topic, which has a very simple configuration, requiring only a name and description.
Once the topic has been created, you can add a subscription, which invokes your function when a message is sent to the Notification topic.
Once the Notification is set up, the prerequisites are in place to enable us to configure an Alarm. This is handled from within Alarm Definitions in OCI Monitoring, though you will probably want to start on the Service Metrics page. The reason for this is that you need an appropriate metric to take advantage of when defining your alarm, as you want your alarm to be continually firing, and an appropriate alarm definition will depend upon the other services running in your environment, as no metrics will be present if a service isn’t running.
Your chosen metric needs to have regular events, so good candidates are things like CPU Utilisation from the oci_computeagent namespace, or a metric from oci_vcn, which reports a lot of packet metrics.
Once you have identified a metric, you can define an Alarm and select that metric – in the screenshot I am using a VCN metric of packets dropped by my security lists.
Then define an impossible trigger rule to lock the alarm in the firing state, i.e. as the VCN metric always reports at least 0 dropped packets, the following will always be true.
The final step, and the piece of configuration which enables this functionality is to add your Notification Topic as the Destination and configure how often you require your function to be invoked.
The UI easily enables you to set invoke frequency as frequently as once per minute, or configure invocations less frequently by changing the units from minutes to hours. According to the API specification for creating an Alarm definition, the repeatNotificationDuration setting has a minimum duration of 1 minute, and a maximum of 30 days.
Once this has been configured, you can confirm that your function has been invoked (and re-invoked) at the required frequency from the Functions logs, or in this case, simply by checking the Objects that were being written.
As you can see, this is writing every 60 minutes, as configured by the repeat notification setting in the Alarm definition.
I prefer this approach to periodic function invocation over the previous approach I used, primarily because it doesn’t required the function to be exposed publicly, and provides flexibility if you require invocations to be less frequent than Health Check allows. A number of use cases require hourly or daily invocation, and this approach allows for the use of Functions as opposed to cron or a similar alternative approach.