DOUG: Hey, folks.
Welcome back.
My name is Doug.
And in the last video in this series,
I talked about how your code running in Cloud Functions
behaves within a single server instance
and among multiple instances.
In particular, one important thing to know
is that each server instance runs only a single function,
and it repeats in response to incoming events.
So what does that mean for your function code practically
speaking?
Well, a simple mistake could cause your function
to run out of memory and crash unexpectedly.
So what do you need to do to prevent this?
Well, there's two things that could cause problems for you.
And those are global variables and temporary disk space,
both useful, but both dangerous if misused.
First, global variables-- the thing that may not be obvious
is that all the variables you declare at the global scope
retain their values between each function
invocation on a particular server instance.
When I say global scope, I mean everything declared
at the top level and every JavaScript module that's
loaded by your function.
So in this JavaScript function here,
the Firebase SDKs are both held globally
as well as a reference to the Firestore SDK.
Inside the function, the request and response objects,
as well as user agent, are not.
Unlike locally scoped variables that you declare
inside a function, globals never go out
of scope or get cleaned up--
not even after your function code finishes.
The objects will still be there during the next function
invocation on that instance for as long
as that instance is alive.
Second, temporary disk space--
in the Cloud Functions runtime, there's
only one directory that's writable by your code,
and that's temp.
In JavaScript, you typically refer
to it using the OS module using its tempter
method for portability because that location
of temporary storage may be different than when running
your code in the local emulator than when
running in Cloud Functions.
What you need to know is that temp is actually
a memory based file system.
That means every file you write there actually
consumes memory just like any other JavaScript object.
It doesn't actually get persisted on a real disk
somewhere.
And like global variables, it doesn't
get cleaned up automatically when your function finishes.
Now, maybe you can see where I'm going with this.
We have the possibility of both global variables and temp
files holding onto memory after your function finishes.
If these resources aren't properly managed by your code,
you run the risk of exhausting memory in the server instance
after your function executes repeatedly.
And when your function runs out of memory, it'll crash.
And you'll lose all your work for that invocation.
At least Cloud Functions will deallocate the server instance
when your function crashes so that it
won't continue to crash like that due to lack of memory.
So what can we do to defend against this?
Well, the obvious thing is to avoid globals and temp space
altogether if at all possible.
Instead, prefer to store data in locally scoped variables
so they get automatically cleaned up
when the function finishes.
Node has a buffer object that you
can use to store chunks of binary data to use later,
which you could use to avoid temp file storage.
But sometimes, it's valuable to store objects
in global variables, especially if they're
going to get reused for every invocation.
For example, you probably don't want
to pay the cost to initialize the Firebase Admin SDK
with every function invocation.
It's good to initialize that just once
and keep it around for later.
That's an important optimization.
Just don't let objects accumulate in global variables
without strict limits.
I will say that it's sometimes handy to store files
in temp space, especially if you're
working with other tools that need to read and write
those files.
For example, there's a command line tool
called ImageMagick that's pre-installed on Cloud
Functions instances that can read and modify images
that you've stored in temp.
And it will also write out files to temp as well.
But you should be very careful to remove those files
before the function completes.
Because if you don't, they'll just
accumulate over time, use up memory,
and, again, crash your function.
Think carefully if you really need to use temp space.
For example, instead of using ImageMagick
for image manipulation, consider using a library like Sharp,
since it can work with streams of image data
in memory using locally scoped streams of data
rather than with files you have to manually cleanup.
So use your best judgment when dealing with globals
and temp disk space.
They can save you time and effort if used correctly
or backfire and leak memory if used poorly.
Your function code should be ready to execute repeatedly
on a single instance, but it should also
be ready to execute in parallel when scaled up
on multiple instances.
In the next video, I'll share some tips
to make sure your functions do the right thing in the face
of concurrent execution.
So be sure to subscribe here to the Firebase channel on YouTube
to get that when it's ready.
And I'll see you then.