private static readonly Lazy<IBucket> Bucket = new Lazy<IBucket>(() =>
{
var uri = ConfigurationManager.AppSettings["couchbaseUri"];
var bucketName = ConfigurationManager.AppSettings["couchbaseBucketName"];
var bucketPassword = ConfigurationManager.AppSettings["couchbaseBucketPassword"];
var cluster = new Cluster(new ClientConfiguration
{
Servers = new List<Uri> { new Uri(uri) }
});
return cluster.OpenBucket(bucketName, bucketPassword);
});
Azure Functions and Lazy Initialization with Couchbase Server
This is a repost that originally appeared on the Couchbase Blog: Azure Functions and Lazy Initialization with Couchbase Server.
Azure Functions are still new to me, and I’m learning as I’m going. I blogged about my foray into Azure Functions with Couchbase over a month ago. Right after I posted that, I got some helpful feedback about the way I was instantiating a Couchbase cluster (and bucket).
I had (wrongly) assumed that there was no way to save state between Azure Function calls. This is why I created a GetCluster()
method that was called each time the function ran. But, initializing a Couchbase Cluster
object is an expensive operation. The less often you instantiate it, the better.
You can follow along with the updated source code for this blog post on Github.
Static state
I had a hard time finding documentation on whether I could use a static
object for reuse between function calls. I suppose I should have experimented, like fellow Microsoft MVP Mark Heath did. Instead, I posed the question to StackOverflow.
In short: yes. A Cluster
, instantiated and saved to a static member, can is reusable between function calls. According to Mark’s post above, there’s no guarantee how long this value will survive. But that’s an expected trade-off that you make when going "serverless".
Lazy initializing within Azure Functions
Simply using a static member would work, but it’s not thread-safe. There are a few ways to tackle that issue, but an easy way that’s built right into the .NET framework is to use Lazy Initialization with Lazy<T>
.
First, I removed the GetBucket
and GetCluster
methods. Next, I created a Lazy<IBucket>
property to replace them.
I just made a single property for a bucket, since that’s all I need for this example. But if you need to use the cluster, you can easily make that its own Lazy
property. (Once you have a cluster, getting a bucket is a relatively cheap operation).
Using a Lazy property
When you instantiate a Lazy<T>
object, you supply it with an initialization lambda. That lambda won’t execute until the Value
property is actually called for the first time.
var lazyObject = new Lazy<string>(() =>
{
// this code won't be called until 'lazyObject.Value' is referenced
// for the first time
return "I'm lazy!";
});
For instance, notice the Value
between Bucket
and GetAsync
in the updated version of my Azure Functions:
var doc = await Bucket.Value.GetAsync<MyDocument>(id);
If that’s the first time Value
is used, the cluster will be initialized. Otherwise, it will use the already initialized cluster (try experimenting with a Guid
instead of a Bucket
).
Summary
State can be saved between Azure Function calls by using a static member. Make sure that it’s thread-safe (by using Lazy<T>
or something like it). Don’t make any assumptions about how long that object will be around.
Anything else I missed? Are you using Azure Functions with Couchbase? I would love to hear from you. Please leave a comment below or ping me on Twitter @mgroves.