Introducing a Redis binding for Azure Functions

One of the things I have been looking at it recent weeks is Event Streaming, or basically democratizing data within a system so that it can be freely accessed by any number of microservices. It is a wonderful pattern and Azure Functions are an ideal platform for implementation. However, over the course of this process I came to realize that while Azure Functions has a bevy of bindings available to it, one that is very clearly missing is for Redis. So I set about building one.

I am pleased to say I have released version 1.1.0 on nuget.org – https://www.nuget.org/packages/Farrellsoft.Azure.Functions.Extensions.Redis/

Under the hood there are two supporting libraries:

  • NewtonSoft Json
  • Stack Exchange Redis

Reading Values

public static async Task<IActionResult> Run(
[Redis(key: "value", Connection = "RedisConnectionString")] string value)
{
}
view raw simple-read.cs hosted with ❤ by GitHub

Here, the binding is reading a string value from Redis (connected using the RedisConnectionString app setting value) with the key of value. The binding is, right now, limited to only reading data from either a Redis string or a Redis list. However, it can support reading C# classes – which are stored as JSON strings and deserializable using Newtonsoft’s Json.NET. For example:

public static async Task<IActionResult> Run(
[Redis(key: "object", Connection = "RedisConnectionString")] Person person)
{
}
view raw object-read.cs hosted with ❤ by GitHub

When we get into reading lists, the internal type can either be string or a normal C# class. For example:

public static async Task<IActionResult> Run(
[Redis(key: "objects", Connection = "RedisConnectionString")] List<Person> people)
{
}
view raw objects-read.cs hosted with ❤ by GitHub

As when reading a single object our of the cache, the underlying value is stored as a JSON string and will be deserialized using Newtonsoft Json.NET.

Writing Values

This is currently the newest use case I have added to the binding, and it, like reading, only supports the string and basic C# types saved using either ICollector<T> or IAsyncCollector<T>, currently out parameter is NOT supported, I plan to add it in the future.

public async Task<IActionResult> Run(
[Redis(key: "value", valueType: RedisValueType.Single, Connection = "RedisConnectionString")] ICollector<string> values)
{
values.Add("testvalue")
}
view raw string-write.cs hosted with ❤ by GitHub

When doing an output (write) case, you must specify the underlying type the value is stored as, right now either Single or Collection. In the above example, the use of Single will invoke JSON serialization logic and store the value given using StringSet. If the given key already has a value, the new value sent through the collector will overwrite the existing value.

When using Collection the underlying code will use List functions against the Redis class, with two potential execution paths. For example, the following code will append JSON strings for objects given:

public async Task<IActionResult> Run(
[Redis(key: "values", valueType: RedisValueType.Collection, Connection = "RedisConnectionString")] ICollector<Person> values)
{
values.Add(new Person { Name = "Name 1" });
values.Add(new Person { Name = "Name 2" });
}

Its important to understand that, the above code will keep adding values to the end of the Redis list. If you want to update values in a list, you need to have your C# object implement the IRedisListItem interface, which will force an Id property. Note this approach is NOT available for strings.

public class Person : IRedisListItem
{
public string Name { get; set; }
public string Id { get; set; }
}
view raw person.cs hosted with ❤ by GitHub

The binding will key off this Id value when it receives a value for the specific Redis key. The one drawback in the current implementation is the entire list has to be pulled, so if you are adding a lot of values for a C# class you will notice performance degradation. Here is an example of this approach being used:

public async Task<IActionResult> Run(
[Redis(key: "values", valueType: RedisValueType.Collection, Connection = "RedisConnectionString")] ICollector<Person> values)
{
values.Add(new Person { Id = "1", Name = "Name 1" });
values.Add(new Person { Id = "1", Name = "Name 2" });
}

In the example above, because the Id value is found in the list, the final value for Name will be Name 2.

The source code for the binding is located at https://github.com/farrellsoft/azure-functions-redis-binding and I am open to taking feedback and I look forward to people having a positive usage experience.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s