STEPHANIE R. WONG: Cloud Functions 2nd gen
helps developers achieve more when deploying their code
to a serverless environment.
Longer processing times, faster response times
with greater cost efficiency, and native integrations
with dozens of event sources.
Features like these enable developers
to take advantage of serverless with more diverse workloads.
I introduced some of this new functionality embedded
into the new generation of Cloud Functions.
But today, let's take a deeper dive
into how these functions in 2nd gen
work and walk through how developers can take advantage
of this functionality.
These new features in Cloud Function 2nd gen
can be categorized into two key themes, enhanced underlying
infrastructure and greater event integrations based
on the open source spec CloudEvents,
all while maintaining the same developer
contract you're familiar with in Cloud Functions.
Write your code, deploy it, and let the serverless platform
handle the rest.
It is worth sharing what powers the improved infrastructure
of this new generation.
2nd gen leverages Cloud Run under the hood,
which is Google's container as a service platform.
This is where new 2nd gen functionality
such as concurrency and traffic splitting comes from.
The beauty of it is that you don't have to build and bring
a container image to Cloud Function 2nd gen
as your ticket for admission, just to use a new feature set.
Cloud Functions 2nd gen maintains the same developer
experience as its preceding generation.
Just bring your code and the serverless platform
will handle the rest.
All right, let's now dig into how these features
work, starting with one of the ones
we heard developers ask for the most, running
functions with a lot longer execution times.
Event-driven functions can now run an additional minute
compared to the previous generation,
and HTTP functions can run more than six times
longer in second generation.
This means that you can now bring more complex functions
that perform more intensive processing to Cloud Functions.
To demonstrate, we'll run this function using Cloud Functions
Python 3.8 runtime.
It may not be complex, but it'll run for the maximum timeout.
It's important to note that the max
timeout is supported across all language runtimes, not just
Python.
We'll deploy this code as a function called
function-timeout and invoke it.
With the function invoked, we can see the logs from our code
directly in the Cloud Functions console.
The function counts down minute by minute
until it returns a 504 once the timeout has been hit.
We can also view execution times for our long-running functions
in dashboards provided by Cloud Functions.
And if we look at how we configure this max timeout,
we can do this by appending the timeout parameter
at deploy time to override the default timeout of one minute.
Now let's briefly talk about the greater amount of resources
that are now available to your functions in 2nd gen.
Previously you were only able to specify how much memory you
wanted to make available to your instances
up to roughly 8 gigabytes, with only up to 2 CPUs per function
instance.
Now you're able to specify double the amount of CPUs,
up to 4 vCPUs to allocate to your function
while also doubling the amount of memory you can make
available, up to 16 gigabytes.
This can help resource-intensive workloads
that need to process large files,
write to an in-memory file system,
or even perform parallel processing.
Let's move on to traffic-splitting.
In second generation, Cloud Functions as a platform
provides for you automatic traffic splitting
between various revisions of your code.
With this functionality, you can feel safer
rolling out new versions of your code,
knowing 2nd gen can make patterns
like canary deployments or A/B testing
straightforward to implement.
Take this example in the diagram where
we have a new version of our code that we have deployed.
Rather than immediately promoting
it to serve all production traffic,
we can gradually begin sending traffic to it using 2nd gen,
starting with 10%.
Let's simulate the scenario by actually deploying
a simple HTTP function.
There may be a case in which we are
using Cloud Function support for environment variables
to flip a new feature in our code on or off.
But in this example, let's just set the variable version
to 1.0.0.
To deploy the next version of the code,
we supply the new environment variable
value to reflect that we are deploying version 2.0.0.
Once we have both versions of the code deployed
to Cloud Functions, we can actually
see that each revision we've deployed
is being tracked in the underlying infrastructure,
Cloud Run.
Now that Cloud Run has shared with us the unique revision
IDs for our two versions of our function,
we can now declaratively define how traffic
should flow to each revision.
In our case here, we want to send 90% of traffic
to the first version and 10% of traffic to the second version.
We do so by providing a single parameter
to the underlying Cloud Run service,
backing the Cloud Function with those traffic percentages.
Once we begin issuing requests to the function's URL,
we can see that we get a simple response that
shows what version of the app we are accessing.
The majority of our traffic is routed to version 1,
and a small minority of our traffic is routed to version 2,
all configured by simple gcloud commands.
Speaking of functions handling requests,
let's cover one more feature that
comes from the new underlying infrastructure in 2nd gen,
concurrency.
Previously in Cloud Functions, each instance of your code
would handle a single request.
This means that as more requests come in,
the more compute time your function uses.
In Cloud Functions 2nd gen, however, you're
now able to define concurrency, that is,
having one instance of your code handle multiple requests
at the same time.
Let's deploy a simple Python 3.8 function.
Once we've deployed this function,
we can then utilize the underlying Cloud Run
functionality and update that Cloud Function to handle
more than one concurrent request at a time, which
is the default. In this case, we'll set this value to 8.
We can then use the hey utility to demonstrate
how Cloud Functions will handle concurrency.
We'll send 8 requests at once from 8 different workers.
In our demo, our Cloud Function only accepts requests
from authenticated users, so we will obtain our identity token
to pass in our requests using gcloud.
And finally, we'll send the request
to the URL provided to us by Cloud Functions.
The code for this function simply sleeps for 15 seconds.
You'll see we deploy the two revisions, the more recent with
concurrency at 8, and the one before it
with concurrency at 1.
When we hit the function with concurrency at 1,
we see 1 request respond quickly and the remaining 7
take much longer.
This is because we only had one warm instance of our function.
Every other request required a new instance of our function
to spin up, which takes time.
However, after updating concurrency to 8,
we see that all our requests complete roughly
within the expected 15 seconds.
This is because we had one warm instance, that could handle
all 8 requests simultaneously.
This brings up a pattern with which you
can utilize concurrency with.
In both first and second generation,
you have the ability to define a minimum number
of warm instances that are always ready to handle requests
to your function.
This allows for lower latency responses from your function,
since you don't need to pay the penalty
from having your code start up.
Now pair this with concurrency.
Because a function instance can handle more than one request
at a time, you can now actually use concurrency combined
with minimum instances.
This helps you reduce the amount of min instances
you may need to keep around, while still ensuring
that you can serve a certain amount of requests
with lower latency.
Note that, in the example we evaluated prior,
all we have to do is add the min instances parameter when
deploying the Cloud Function.
Cloud Function 2nd gen can support up
to 1,000 concurrent requests.
But this maximum for concurrent requests
varies by language runtime, so it's
advised that you review the documentation depending
on what language you want to use with Cloud Functions.
Last but not least, let's talk about the new eventing
model in Cloud Functions second generation.
The previous generation of Cloud Functions
had a smaller set of events that could
trigger your code's execution, and these were fairly limited.
If you wanted to trigger your code outside of the set,
you typically had to wire code to publish events
to Cloud Pub/Sub.
By integrating with Eventarc, we ensure
that every event that triggers your code
adheres to the CloudEvent specification, an open standard
for event data format.
This means that your code can consistently
handle events in the same format,
regardless of what the source is.
With these Eventarc integrations,
over 90-plus event sources using Cloud
audit logs can now be natively created
as triggers for event-driven functions.
No custom code that you need to wire together
just to trigger your code with Google Cloud events.
Of course, you still can utilize some custom event sources
with Pub/Sub to trigger your functions.
In this demonstration here, we are
using the second generation triggers based on Eventarc
and audit logs to process BigQuery event
data every time a query is run.
We'll use a query in BigQuery now.
This one will roughly query 4.5 terabytes of data
and take less than 30 seconds to complete.
This is a fairly large query, so we
might want to use a Cloud Function to react
to its currents, maybe use something like Notify Admins
that it ran.
With the query finished, we can get back to our Cloud Function
and see that we are writing out the query information
to standard out.
But of course, we could extend this
to send a message to Slack, do some processing, or anything
else that we can imagine.
BigQuery is just one example.
With Cloud audit logs and Eventarc,
we can now trigger off dozens and dozens
of Google Cloud events.
With all of these new features within these two themes,
Cloud Functions second generation considerably
improves the experience for developers when
compared to its preceding version.
But for all of you who have built
atop of the first generation, it's
important to note your function in the first generation
will continue to run as is.
Over time, if you find the features suit your needs
or improve your workflow, you can
begin to deploy these functions to the second generation.
So thanks for spending this time digging into Cloud Functions
2nd gen. If you want to learn more,
then check out the documentation and code labs
to get familiar with the improved developer experience.
This is Stephanie Wong.
See you next time.