Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# storing value types in cache

Problem

Given a function that returns the total number of active users on the website:

private static readonly object Lock = new object();
public static int GetTotalActiveUsers()
{
    var cache = HttpContext.Current.Cache;
    if (cache["ActiveUsers"] == null)
    {
        lock (Lock)
        {
            if (cache["ActiveUsers"] == null)
            {
                var activeUsers = 5; // This would actually be an expensive operation
                cache.Add("ActiveUsers", activeUsers, null, Cache.NoAbsoluteExpiration,
                    Cache.NoSlidingExpiration,
                    CacheItemPriority.Normal, null);
            }
        }
    }
    return (int) cache["ActiveUsers"];
}

The problem with storing a ValueType in the cache in this way is it's not updatable. For example:

public static void OnNewActiveUser()
{
    var total = GetTotalActiveUsers();
    total++;
}

Doesn't update the cached value. (This is an expected behaviour).

I'm looking for a thread safe method to update the active user count.

Solution 1

Use a lock

public static void OnNewActiveUser()
{
    lock (UpdateActiveUsersLock)
    {
        var cache = HttpContext.Current.Cache;
        var newTotal = GetTotalActiveUsers() + 1;
        cache.Insert("ActiveUsers", newTotal, null, Cache.NoAbsoluteExpiration,
                    Cache.NoSlidingExpiration,
                    CacheItemPriority.Normal, null);
    }
}

Solution 2

Create a thin class around the int to turn it into a reference type:

public class CachedInt
{
    public int Int { get; set; }
    public CachedInt(int value)
    {
        Int = value;
    }
}

Then:

public static void OnNewActiveUser()
{
    var activeUsers = GetTotalActiveUsers();
    activeUsers.Int++;
}

Question

I'd prefer to avoid Solution 1 if possible (it doesn't fit neatly into my design). Is wrapping the value types in a thin class code smell, or is it a legitimate way to solve the problem?

like image 664
Tom Gullen Avatar asked Apr 24 '26 14:04

Tom Gullen


1 Answers

HttpContext.Current.Cache caches objects (reference types), so you need to wrap the int (value type) in a reference type, as you suggested.

By why not just have a class with a static method and a static member for count if the cached item will never expire?

Also, you should use interlocked increment to ensure the count is correct, and have some way of knowing when a user is not active so that the count can be decremented. As pointed out in a comment, this will only give you the count for a single process. If you have multiple web processes on the same machine, or multiple servers the count will be wrong - maybe that's what the expensive operation is that returns 5 :)

like image 116
Alex Peck Avatar answered Apr 27 '26 03:04

Alex Peck



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!