Tips & tricks

This document describes best practices for designing, implementing, testing, and deploying Cloud Functions.

Correctness

This section describes general best practices for designing and implementing Cloud Functions.

Write idempotent functions

Your functions should produce the same result even if they are called multiple times. This lets you retry an invocation if the previous invocation fails partway through your code. For more information, see retrying background functions.

Always delete temporary files

Local disk storage in the temporary directory is an in-memory filesystem. Files that you write consume memory available to your function, and sometimes persist between invocations. Failing to explicitly delete these files may eventually lead to an out-of-memory error and a subsequent cold start.

You can see the memory used by an individual function by selecting it in the list of functions in the API Console and choosing the Memory usage plot.

Do not attempt to write outside of the temporary directory, and be sure to use platform/OS-independent methods to construct file paths.

You can bypass the size restrictions on temporary files by using pipelining. For example, you can process a file on Cloud Storage by creating a read stream, passing it through a stream-based process, and writing the output stream directly to Cloud Storage.

Tools

This section provides guidelines on how to use tools to implement, test, and interact with Cloud Functions.

Local development

Function deployment takes a bit of time, so it is often faster to test the code of your function locally using a shim.

We recommend using the Cloud Functions Node.js Emulator as a shim.

Use Sendgrid to send emails

Cloud Functions does not allow outbound connections on port 25, so you cannot make non-secure connections to an SMTP server. The recommended way to send emails is to use SendGrid. You can find a complete example in the SendGrid Tutorial, and other options for sending email in the Google Compute Engine document Sending Email from an Instance.

Performance

This section describes best practices for optimizing performance.

Use dependencies wisely

Because functions are stateless, the execution environment is often initialized from scratch (during what is known as a cold start). When a cold start occurs, the global context of the function is evaluated.

If your functions import modules, the load time for those modules can add to the invocation latency during a cold start. You can reduce this latency, as well as the time needed to deploy your function, by loading dependencies correctly and not loading dependencies your function doesn't use.

Use global variables to reuse objects in future invocations

There is no guarantee that the state of a Cloud Function will be preserved for future invocations. However, Cloud Functions often recycles the execution environment of a previous invocation. If you declare a variable in global scope, its value can be reused in subsequent invocations without having to be recomputed.

This way you can cache objects that may be expensive to recreate on each function invocation. Moving such objects from the function body to global scope may result in significant performance improvements. The following example creates a heavy object only once per function instance, and shares it across all function invocations reaching the given instance:

console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');

exports.function = functions.https.onRequest((req, res) => {
    console.log('Function invocation');
    const perFunction = lightweightComputation();

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

It is particularly important to cache network connections, library references, and API clients in global scope. See Optimizing Networking for examples.

Do lazy initialization of global variables

If you initialize variables in global scope, the initialization code will always be executed via a cold start invocation, increasing your function's latency. If some objects are not used in all code paths, consider initializing them lazily on demand:

const functions = require('firebase-functions');
let myCostlyVariable;

exports.function = functions.https.onRequest((req, res) => {
    doUsualWork();
    if(unlikelyCondition()){
        myCostlyVariable = myCostlyVariable || buildCostlyVariable();
    }
    res.status(200).send('OK');
});

This is particularly important if you define several functions in a single file, and different functions use different variables. Unless you use lazy initialization, you may waste resources on variables that are initialized but never used.

Additional resources

Find out more about optimizing performance in the "Google Cloud Performance Atlas" video Cloud Functions Cold Boot Time.

Оставить отзыв о...

Текущей странице
Нужна помощь? Обратитесь в службу поддержки.