Cloud Functions Execution Environment

Cloud Functions run in a fully-managed, serverless environment where Google handles infrastructure, operating systems, and runtime environments completely on your behalf. Each Cloud Function runs in its own isolated secure execution context, scales automatically, and has a lifecycle independent from other functions.

Runtimes

Cloud Functions supports multiple language runtimes:

Runtime Language version Base image
Node.js 6 (deprecated) Node.js 6.16.0 Debian 8
Node.js 8 Node.js 8.15.0 Ubuntu 18.04
Node.js 10 (Beta) Node.js 10.15.3 Ubuntu 18.04
Python Python 3.7.1 Ubuntu 18.04
Go Go 1.11.6 Ubuntu 18.04

Updates to the runtimes are generally done automatically, unless otherwise notified. All runtimes receive automatic updates to the language version as they are made available to the language community. Similarly, Cloud Functions might apply updates to other aspects of the execution environment, such as the operating system or included packages. These updates help keep your function secure.

Stateless functions

Cloud Functions implements the serverless paradigm, in which you just run your code without worrying about the underlying infrastructure, such as servers or virtual machines. To allow Google to automatically manage and scale the functions, they must be stateless—one function invocation should not rely on in-memory state set by a previous invocation. However, the existing state can often be reused as a performance optimization; see the recommendation in Tips and Tricks for details.

For example, the counter value returned by the following function does not correspond to the total function invocation count because invocations might be handled by different function instances, which don’t share global variables, memory, file systems, or other state:

Node.js

// Global variable, but only shared within function instance.
let count = 0;

/**
 * HTTP Cloud Function that counts how many times
 * it is executed within a specific instance.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.executionCount = (req, res) => {
  count++;

  // Note: the total function invocation count across
  // all instances may not be equal to this value!
  res.send(`Instance execution count: ${count}`);
};

Python

# Global variable, modified within the function by using the global keyword.
count = 0


def statelessness(request):
    """
    HTTP Cloud Function that counts how many times it is executed
    within a specific instance.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    global count
    count += 1

    # Note: the total function invocation count across
    # all instances may not be equal to this value!
    return 'Instance execution count: {}'.format(count)

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
)

// count is a global variable, but only shared within a function instance.
var count = 0

// ExecutionCount is an HTTP Cloud Function that counts how many times it
// is executed within a specific instance.
func ExecutionCount(w http.ResponseWriter, r *http.Request) {
	count++

	// Note: the total function invocation count across
	// all instances may not be equal to this value!
	fmt.Fprintf(w, "Instance execution count: %d", count)
}

If you need to share state across function invocations, your function should use a service such as Datastore, Cloud Firestore or Cloud Storage to persist data. For a complete list of available storage options, see Choosing a storage option.

Auto-scaling and Concurrency

Cloud Functions handles incoming requests by assigning them to instances of your function. Depending on the volume of requests, as well as the number of existing function instances, Cloud Functions may assign a request to an existing instance or create a new one.

Each instance of a function handles only one concurrent request at a time. This means that while your code is processing one request, there is no possibility of a second request being routed to the same instance. Thus the original request can use the full amount of resources (CPU and memory) that you requested.

In cases where inbound request volume exceeds the number of existing instances, Cloud Functions may start multiple new instances to handle requests. This automatic scaling behavior allows Cloud Functions to handle many requests in parallel, each using a different instance of your function.

Because concurrent requests are processed by different function instances, they do not share variables or local memory. This is discussed in detail later in this document.

Controlling auto-scaling behavior

Cloud Functions allows you to set a limit on the total number of function instances that can co-exist at any given time. In some cases, unbounded scaling is undesirable. For example, your function might depend on a resource (such as a database) that cannot scale up to the same degree as Cloud Functions. A huge spike in request volume might result in Cloud Functions creating more function instances than your database can tolerate.

Cold starts

A new function instance is started in two cases:

  • When you deploy your function.

  • When a new function instance is automatically created to scale up to the load, or occasionally to replace an existing instance.

Starting a new function instance involves loading the runtime and your code. Requests that include function instance startup (cold starts) can be slower than requests hitting existing function instances. If your function receives steady load, however, then the number of cold starts is typically negligible unless your function frequently crashes and requires restarting of the function environment. See Errors to learn how to handle errors properly and avoid cold starts.

Function instance lifespan

The environment running a function instance is typically resilient and reused by subsequent function invocations, unless the number of instances is being scaled down (due to lack of ongoing traffic), or your function crashes. This means that when one function execution ends, another function invocation can be handled by the same function instance. Therefore, it is recommended to cache state across invocations in global scope where possible. Your function should be still prepared to work without this cache available as there is no guarantee that the next invocation will reach the same function instance (see Stateless functions).

Function scope versus global scope

A single function invocation results in executing only the body of the function declared as the entry point. The global scope in the function file, which is expected to contain the function definition, is executed on every cold start, but not if the instance has already been initialized.

Node.js

// Global (instance-wide) scope
// This computation runs at instance cold-start
const instanceVar = heavyComputation();

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
exports.scopeDemo = (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
};

Python

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()


def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return 'Instance: {}; function: {}'.format(instance_var, function_var)

Go


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
}

// ScopeDemo is an example of using globally and locally
// scoped variables in a function.
func ScopeDemo(w http.ResponseWriter, r *http.Request) {
	l := lightComputation()
	fmt.Fprintf(w, "Global: %q, Local: %q", h, l)
}

You can assume that the global scope has been executed exactly once before your function code is invoked in a new function instance (and on every subsequent creation of a new function instance). However, you should not depend on the total number of or timing of global scope executions as they depend on the autoscaling managed by Google.

Function execution timeline

A function has access to the resources requested (CPU and memory) only for the duration of function execution. Code run outside of the execution period is not guaranteed to execute, and it can be stopped at any time. Therefore, you should always signal the end of your function execution correctly and avoid running any code beyond it. See HTTP Functions and Background Functions for guidance.

For example, the code executed after sending the HTTP response could be interrupted at any time:

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to early HTTP response
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterResponse = (req, res) => {
  res.end();

  // This statement may not execute
  console.log('Function complete!');
};

Execution guarantees

Your functions are typically invoked once for each incoming event. However, Cloud Functions does not guarantee a single invocation in all cases because of differences in error scenarios.

The maximum or minimum number of times your function is going to be invoked on a single event depends on the type of your function:

  • HTTP functions are invoked at most once. This is because of the synchronous nature of HTTP calls, and it means that any error on handling function invocation will be returned without retrying. The caller of an HTTP function is expected to handle the errors and retry if needed.

  • Background functions are invoked at least once. This is because of the asynchronous nature of handling events, in which there is no caller that waits for the response. The system might, in rare circumstances, invoke a background function more than once in order to ensure delivery of the event. If a background function invocation fails with an error, it will not be invoked again unless retries on failure are enabled for that function.

To make sure that your function behaves correctly on retried execution attempts, you should make it idempotent by implementing it so that an event results in the desired results (and side effects) even if it is delivered multiple times. In the case of HTTP functions, this also means returning the desired value even if the caller retries calls to the HTTP function endpoint. See Retrying Background Functions for more information on how to make your function idempotent.

Errors

The recommended way for a function to signal an error depends on the function type:

  • HTTP functions should return appropriate HTTP status codes which denote an error. See HTTP Functions for examples.

  • Background functions should log and return an error message. See Background Functions for examples.

If an error is returned the recommended way, then the function instance that returned the error is labelled as behaving normally and can serve future requests if need be.

If your code or any other code you call throws an uncaught exception or crashes the current process, then the function instance might be restarted before handling the next invocation. This can lead to more cold starts, resulting in higher latency, and thus is discouraged.

See Reporting Errors for more discussion of how to report errors in Cloud Functions.

Timeout

Function execution time is limited by the timeout duration, which you can specify at function deployment time. By default, a function times out after 1 minute, but you can extend this period up to 9 minutes.

When function execution exceeds the timeout, an error status is immediately returned to the caller. CPU resources used by the timed-out function instance are throttled and request processing may be immediately paused. Paused work may or may not proceed on subsequent requests, which can cause unexpected side effects.

The snippet below includes code that is scheduled to execute 2 minutes after function execution starts. If the timeout happens to be set to 1 minute, this code might never execute:

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to function execution timeout
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterTimeout = (req, res) => {
  setTimeout(() => {
    // May not execute if function's timeout is <2 minutes
    console.log('Function running...');
    res.end();
  }, 120000); // 2 minute delay
};

Python

def timeout(request):
    print('Function running...')
    time.sleep(120)

    # May not execute if function's timeout is <2 minutes
    print('Function completed!')
    return 'Function completed!'

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

// Timeout sleeps for 2 minutes and may time out before finishing.
func Timeout(w http.ResponseWriter, r *http.Request) {
	log.Println("Function execution started...")
	time.Sleep(2 * time.Minute)
	log.Println("Function completed!")
	fmt.Fprintln(w, "Function completed!")
}

In some circumstances, the above code may execute successfully but in an unexpected manner. Consider the scenario when the function times out. The instance that is serving the request is paused (by throttling the CPU). Pending work is paused. If a subsequent request is routed to the same instance, work resumes and Function running... is emitted to Logs.

A common symptom of this behavior is the appearance that work and logs from one request are "leaking" into a subsequent request. Because you cannot depend on paused work being resumed, you should not rely on this behavior. Instead, your function should avoid timeouts using a combination of the following techniques:

  1. Set a timeout that is higher than your expected function execution time.
  2. Track the amount of time left during execution and perform cleanup/exit early.

To set a function's maximum execution time using the gcloud command-line tool, use the --timeout flag at deploy time:

gcloud functions deploy FUNCTION_NAME --timeout=TIMEOUT FLAGS...

In the above command, FLAGS... refers to other options that you pass during deployment of your function. For a complete reference for the deploy command, see gcloud functions deploy.

You can also set the timeout during function creation in the Cloud Console, as follows:

  1. Go to the Cloud Functions Overview page in the Cloud Console.

  2. Click Create function.

  3. Fill in the required fields for your function.

  4. View the advanced settings by clicking More.

  5. Enter a value in the Timeout field.

File system

The function execution environment contains a runnable function file, plus files and directories included in the deployed function package such as local dependencies. These files are available in a read-only directory, which can be determined based on the function file location. Note that the function’s directory can be different than the current working directory.

The following example lists files located in the function directory:

Node.js

const fs = require('fs');

/**
 * HTTP Cloud Function that lists files in the function directory
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.listFiles = (req, res) => {
  fs.readdir(__dirname, (err, files) => {
    if (err) {
      console.error(err);
      res.sendStatus(500);
    } else {
      console.log('Files', files);
      res.sendStatus(200);
    }
  });
};

Python

def list_files(request):
    import os
    from os import path

    root = path.dirname(path.abspath(__file__))
    children = os.listdir(root)
    files = [c for c in children if path.isfile(path.join(root, c))]
    return 'Files: {}'.format(files)

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// ListFiles lists the files in the current directory.
func ListFiles(w http.ResponseWriter, r *http.Request) {
	files, err := ioutil.ReadDir("./")
	if err != nil {
		http.Error(w, "Unable to read files", http.StatusInternalServerError)
		log.Printf("ioutil.ListFiles: %v", err)
		return
	}
	fmt.Fprintln(w, "Files:")
	for _, f := range files {
		fmt.Fprintf(w, "\t%v\n", f.Name())
	}
}

You can also load code from other files deployed with the function.

The only writeable part of the filesystem is the /tmp directory, which you can use to store temporary files in a function instance. This is a local disk mount point known as a "tmpfs" volume in which data written to the volume is stored in memory. Note that it will consume memory resources provisioned for the function.

The rest of the file system is read-only and accessible to the function.

Network

Your function can access the public internet using standard libraries offered by the runtime or third-party providers. For example, you can call an HTTP endpoint as shown below:

Node.js

const fetch = require('node-fetch');

/**
 * HTTP Cloud Function that makes an HTTP request
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.makeRequest = async (req, res) => {
  const url = 'https://example.com'; // URL to send the request to
  const externalRes = await fetch(url);
  res.sendStatus(externalRes.ok ? 200 : 500);
};

Python

def make_request(request):
    """
    HTTP Cloud Function that makes another HTTP request.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    import requests

    # The URL to send the request to
    url = 'http://example.com'

    # Process the request
    response = requests.get(url)
    response.raise_for_status()
    return 'Success!'

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
	"time"
)

var urlString = "https://example.com"

// client is used to make HTTP requests with a 10 second timeout.
// http.Clients should be reused instead of created as needed.
var client = &http.Client{
	Timeout: 10 * time.Second,
}

// MakeRequest is an example of making an HTTP request. MakeRequest uses a
// single http.Client for all requests to take advantage of connection
// pooling and caching. See https://godoc.org/net/http#Client.
func MakeRequest(w http.ResponseWriter, r *http.Request) {
	resp, err := client.Get(urlString)
	if err != nil {
		http.Error(w, "Error making request", http.StatusInternalServerError)
		return
	}
	if resp.StatusCode != http.StatusOK {
		msg := fmt.Sprintf("Bad StatusCode: %d", resp.StatusCode)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "ok")
}

Try to reuse network connections across function invocations, as described in Optimizing Networking. However, note that a connection that remains unused for 2 minutes might be closed by the system, and further attempts to use a closed connection will result in a "connection reset" error. Your code should either use a library that handles closed connections well, or handle them explicitly if using low-level networking constructs.

Multiple functions

Every deployed function is isolated from all other functions—even those deployed from the same source file. In particular, they don’t share memory, global variables, file systems, or other state.

To share data across deployed functions, you can use storage services like Datastore, Cloud Firestore, or Cloud Storage. Alternatively, you can invoke one function from another, using their appropriate triggers. For example, make an HTTP request to the endpoint of an HTTP function or publish a message to a Pub/Sub topic to trigger a Pub/Sub function.

Was this page helpful? Let us know how we did:

Send feedback about...

Cloud Functions Documentation