Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure Functions: Singleton for expensive object

Tags:

I've created some very simple Azure functions. They read and write data from Couchbase (which is running in Azure on a VM).

I'm concerned about the connection(s) that I make to Couchbase in an Azure Function. I create a Cluster object each time. This is an expensive operation, and I would typically only do it once in a normal web app. But in the Azure Function, I'm newing it up every time.

There are a lot of expensive to instantiate objects like this beyond just Couchbase. Is there way to create a singleton, or some sort of shared object that Azure Functions can reuse between calls?

  • You can see a description of what I'm doing here: https://blog.couchbase.com/azure-functions-couchbase-server/
  • The full source code here: https://github.com/couchbaselabs/blog-source-code/tree/master/Groves/074AzureFunctions/src/CouchbaseWithAzureFunctions
like image 681
Matthew Groves Avatar asked Sep 11 '17 18:09

Matthew Groves


2 Answers

When dealing with singletons on Azure Functions, there are several considerations. One is that global state is shared among AF invocations. So, if a function is invoked once and then again later (soon enough that the host hasn't unloaded your code), then the initialization only happens once. Another consideration is that AF is perfectly free to start multiple AF invocations simultaneously - so any singletons need to be threadsafe (including their initialization).

This means you'll want to use Lazy<T> / AsyncLazy<T>. However, bear in mind that AF (with these types) will preserve the singleton state (post-initialization) for your next invocation even if it fails. This can be a problem particularly with cloud computing because if there's a network (or configuration) error when your AF is starting up, you want the initialization to be retried on the next AF invocation.

In conclusion, you want to use Lazy<T> / AsyncLazy<T> in such a way that is threadsafe and does not preserve failures.

With Lazy<T>, this means you have to use the LazyThreadSafetyMode.PublicationOnly flag and pass a function to the constructor (not just implicitly use the default constructor for T). Note that this means you need to ensure your initialization function itself is threadsafe because it can be executed by multiple threads simultaneously.

With AsyncLazy<T>, you have to use the AsyncLazyFlags.RetryOnFailure flag. Since AsyncLazy<T> is essentially a Lazy<Task<T>>, the async initialization task is "shared" among all simultaneous callers, and then atomically replaced with a new Lazy<Task<T>> instance if it fails. So the async initialization function does not need to be threadsafe.

Since getting this all right (especially for multiple singletons) is rather copy-and-pasteish, I abstracted this out for the AF project I'm working on:

  • First I have a singleton abstraction to make the construction simpler and more uniform across sync and async singletons. My particular implementation uses AsyncLazy<T> for both sync and async singletons so that a sync initialization function will "share" its work, so it's only run once even if multiple threads simultaneously request an instance.
  • This abstraction is then used to create the actual static singleton instances, and then when the function is actually invoked, I resolve the instances at that time and register them with my DI container (in this case, SimpleInjector), also treating the DI container itself as a singleton.

It took a while to get to this point, but I'm pretty pleased at how it's turned out. Been meaning to blog about this, too...

like image 199
Stephen Cleary Avatar answered Sep 28 '22 10:09

Stephen Cleary


Static properties for your expensive connection objects will work fine, but I recommend wrapping them in Lazy<> so that you get guaranteed thread safety out of the box.

Based on the sample blog post that you linked to an example of making the bucket reusable across all your function calls in a guaranteed thread safe way might look something like this:

public class FunctionClass {     private static Lazy<IBucket> LazyBucket = new Lazy<IBucket>(() =>     {         var uri = ConfigurationManager.AppSettings["couchbaseUri"];         var cluster = new Cluster(new ClientConfiguration         {             Servers = new List<Uri> { new Uri(uri) }         });          var bucketName = ConfigurationManager.AppSettings["couchbaseBucketName"];         var bucketPassword = ConfigurationManager.AppSettings["couchbaseBucketPassword"];          return cluster.OpenBucket(bucketName, bucketPassword);     });      // Your actual function implementation     public static async Task Run()     {         // Here you are guaranteed to get back a shared connection object to your bucket that has been         // initalized only once in a thread safe way         var initalizedOnceBucket = LazyBucket.Value;          // do something with the bucket     } } 

If the construction of your expensive object that should be shared relies on some async calls (I suspect the Couchbase C# client might have async versions of it's methods). You can use the AsyncLazy<> from the awesome Nito.AsyncEx Nuget package written by Stephen Cleary. Regular Lazy<> is built into .NET so don't require any external dependencies.

like image 21
Jesse Carter Avatar answered Sep 28 '22 10:09

Jesse Carter