This topic describes the runtime extension feature of Function Compute. This feature is developed based on traditional long-running applications to help you reduce the unnecessary costs generated by idle instances. This topic describes the principles and billing of the runtime extension feature in Function Compute. This topic also describes how to configure the PreFreeze hook and the PreStop hook by using the Function Compute console, Serverless Devs, and SDKs, and provides sample code.
Long-running applications and FaaS environment
The billing interval for traditional long-running virtual machines or managed container services often starts when an instance is started and ends when the instance is stopped. You are charged even when no requests are executed within the billing interval. Function Compute charges you at a billing granularity of 1 milliseconds. Instances are billed only during the execution of actual requests. The instances are frozen in time periods where no requests are executed. This way, the unnecessary costs generated by idle instances that use the fully event-driven billing model can be reduced. However, the freeze mechanism shatters the hypotheses about the long-running processes in traditional architectures and makes it difficult to migrate applications. Function Compute runs in a special environment. The system cannot track and report data as expected in scenarios where there is no cold start, such as when the open source distributed Tracing Analysis libraries or third-party application performance management (APM) solutions are used.
The following pain points hinder the smooth migration of traditional applications to a serverless architecture:
Data of asynchronous background metrics is delayed or lost. If the data fails to be sent during the execution of a request, the data may be delayed until the time point when the next request is sent, or the data points are discarded.
The latency is increased if metrics are sent synchronously. If a method similar to Flush is called after each request is completed, the latency of each request is increased and excessive workloads on backend servers are caused.
To support graceful shutdown of function instances, applications need to close connections, stop processes, and report the status when instances are stopped. Developers do not know when instances are released in Function Compute. In addition, no webhook is provided to send notifications about release events of function instances.

Programming model extensions
Function Compute provides the runtime extension feature to resolve the preceding pain points. The feature extends the existing programming model for HTTP servers by adding the PreFreeze and PreStop webhooks to the existing HTTP server model. This way, extension developers can use HTTP handlers to listen to function instance lifecycle events.

PreFreeze
Each time Function Compute decides to freeze the current function instance, Function Compute sends an HTTP GET request to the /pre-freeze path. Extension developers implement the logic to ensure that necessary operations are completed before the instance is frozen. For example, the instance waits until metrics are sent. The duration to execute the PreFreeze hook is not included in the duration to call the InvokeFunction operation.
PreStop
Each time Function Compute decides to stop the current function instance, Function Compute sends an HTTP GET request to the /pre-stop path. Extension developers implement the logic to ensure that the required operations are completed before the instance is released. For example, database connections are closed and the status is reported or updated.
Usage notes
The input parameters of the PreFreeze hook and the PreStop hook do not include the event parameter.
The PreFreeze hook and the PreStop hook do not return values. The logic that is appended to the PreFreeze hook or the PreStop hook to return values does not take effect.
You can configure PreStop hooks in all runtime environments. However, you cannot configure the PreFreeze hook in the Python, PHP, or C# runtime environment.
If you use the Java runtime environment, you must update fc-java-core to version 1.4.0 or later. Otherwise, you cannot use the PreFreeze hook or the PreStop hook. For more information, see HTTP handler.
If your custom container runtime is in non-web server mode, the PreFreeze and PreStop hooks that you configured do not take effect.
Billing rules
The billing method for PreFreeze or PreStop calls is the same as that for InvokeFunction calls. For more information, see Billing methods. You are not charged for the number of requests sent to the HTTP hooks. The extensions are also applicable to scenarios in which multiple concurrent requests are executed on a single instance. Assume that multiple invocation requests are concurrently executed on the same instance. After all the requests are executed, the PreFreeze hook is called before the instance is frozen.
In the example shown in the following figure, the specification of the function is 1 GB. Assume that the period from t1 when PreFreeze starts to t6 when Request 2 is completed is 1s. The execution time of the instance is calculated based on the following formula: t6 - t1. The consumed resource is calculated based on the following formula: 1s × 1 GB = 1 CU.

Prerequisite
Configure the PreFreeze hook and the PreStop hook in the Function Compute console
When you use the Function Compute console to create a function, you cannot configure the PreFreeze hook and the PreStop hook. You can configure the PreFreeze and PreStop hooks when you update the function.
- Log on to the Function Compute console. In the left-side navigation pane, click Services & Functions.
- In the top navigation bar, select a region. On the Services page, click the desired service.
- On the Functions page, find the function that you modify and click Configure in the Actions column.
In the Instance Lifecycle Hook section of the Modify Function Settings page, configure the hooks and timeout periods, and then click Save.
NoteYou must configure a hook and timeout period for each extension function. The format of the hook is [File name].[Extension function name]. For example, if you set the PreStop hook to index.preStopHandler when you create a function in the Python runtime environment, the file name is index.py and the name of the PreStop function is preStopHandler.
After you configure the extension function, implement the function in the code that you want to execute.
Click the Code tab. In the code editor, enter the code of the extension function.
For example, if you set the PreStop hook to
index.preStopHandler
, you must implement the preStopHandler function. In this example, an event function that runs Node.js 12 is used. For more information about the sample code of extension functions in different runtime environments, see Sample code.NoteThe online IDE supports PHP, Python, Node.js, and custom runtime environments. However, the online IDE does not support compiled languages such as Java, Go, .NET, or custom containers.
Click Deploy in the upper part of the code editor, and then click Test Function.
Use Serverless Devs to configure the PreFreeze hook and the PreStop hook
The following sample code provides an example on how to use Serverless Devs to configure the PreFreeze hook and the PreStop hook:
codeUri: './code.zip'
......
instanceLifecycleConfig:
preFreeze:
handler: index.PreFreeze
timeout: 60
preStop:
handler: index.PreStop
timeout: 60
To disable an extension function, leave the Handler
parameter of the extension function empty. Otherwise, the function is not updated by default. For example, to disable the PreFreeze hook, you can deploy and update the function based on the following configuration. In this case, the Timeout
parameter of the PreFreeze hook is invalid.
codeUri: './code.zip'
......
instanceLifecycleConfig:
preFreeze:
handler: ""
timeout: 60
preStop:
handler: index.PreStop
timeout: 60
Use SDKs to configure the PreFreeze hook and the PreStop hook
You can use SDKs to deploy and update extension functions. The following code provides an example on how to use the SDK for Go to configure the PreStop hook and the PreFreeze hook when you create a function:
package main
import (
"github.com/aliyun/fc-go-sdk"
"fmt"
"os"
)
func main() {
client, _ := fc.NewClient(
os.Getenv("ENDPOINT"),
"2016-08-15",
os.Getenv("ACCESS_KEY_ID"),
os.Getenv("ACCESS_KEY_SECRET"),
)
serviceName := "ExtensionService"
functionName := "ExtensionFunction"
createFunctionInput := fc.NewCreateFunctionInput(serviceName).WithFunctionName(functionName)
// Configure the PreStop hook and the PreFreeze hook.
preStopHook := fc.NewLifecycleHook().WithHandler("index.preStop").WithTimeout(int32(30))
preFreezeHook := fc.NewLifecycleHook().WithHandler("index.preFreeze").WithTimeout(int32(10))
instanceLifecycle := fc.NewInstanceLifecycleConfig().WithPreStopHook(preStopHook).WithPreStopHook(preFreezeHook)
createFunctionOutput, err := client.CreateFunction(createFunctionInput.WithInstanceLifecycleConfig(instanceLifecycle))
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
fmt.Printf("CreateFunction response: %s \n", createFunctionOutput)
}
return
}
Sample code
The following section describes the sample code of extension functions in different runtime environments. The PreFreeze hook is defined in the same manner in which the PreStop hook is defined in a runtime environment. This topic provides the sample code for only the PreStop hook.
Python PreStop
# -*- coding: utf-8 -*-
import logging
def handler(event, context):
logger = logging.getLogger()
logger.info('handler start')
return "ok"
def pre_stop(context):
logger = logging.getLogger()
logger.info('preStop start')
Node.js PreFreeze
'use strict';
var counter = 0;
module.exports.handler = function(event, context, callback) {
counter += 2;
callback(null, String(counter));
};
module.exports.preFreeze = function(ctx, callback) {
counter += 1;
callback(null, "");
};
PHP PreStop
<?php
$counter = 0;
function fc_func_pre_stop($context) {
sleep(2);
}
function fc_func_handler($event, $context) {
global $counter;
$counter += 2;
return $counter;
}
?>
C# PreStop
using System;
using System.IO;
using Aliyun.Serverless.Core;
using Microsoft.Extensions.Logging;
namespace Aliyun.Serverless.TestHandlers
{
public class PreStopAndPojoCounter
{
public int counter;
public PreStopAndPojoCounter()
{
this.counter = 0;
}
public void PreStop(IFcContext context)
{
// todo
}
// todo
}
}
Java PreFreeze
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.PreFreezeHandler;
import java.io.IOException;
public class PreFreezeNormal implements PreFreezeHandler {
@Override
public void preFreeze(Context context) throws IOException {
// todo
}
}
Query logs related to hooks
After you configure an instance lifecycle hook and execute the code of the hook, you can query the logs of the hook.
- Log on to the Function Compute console. In the left-side navigation pane, click Services & Functions.
- In the top navigation bar, select a region. On the Services page, click the desired service.
On the Functions page, click the function that you want to manage and click the Logs tab.
On the Request List tab, find the request that you want to query, copy the Instance ID, and then click SLS Logs.
You are redirected to the query and analysis page in the Log Service console. You can use the copied instance ID to query the start and end logs of all lifecycle hooks. You can also use the
instance ID + lifecycle hook keyword
to query the start and end logs of a specified hook, for example,c-62833f38-20f1629801fa4bd***** and PreStop
.You can also query the request logs based on the request ID in the start logs and the end logs. If the log does not contain the request ID, click
to query the logs.