DEV Community

Cover image for c# Debounce and Throttle
Rodion Shlomo Solomonyk
Rodion Shlomo Solomonyk

Posted on

c# Debounce and Throttle

In software development, managing the frequency of function executions is crucial to ensure efficiency, avoid redundant operations, and prevent system overloads. Whether you're working on a distributed system or a local application, implementing debounce and throttle mechanisms can significantly improve performance and reliability. In this article, we introduce two powerful .NET libraries designed for these purposes: DistributedDebounceThrottle and DebounceThrottle.

Links:

DistributedDebounceThrottle: A Distributed Solution

DistributedDebounceThrottle is a .NET library designed to facilitate debounce and throttle mechanisms in distributed system environments. Leveraging Redis for state management and distributed locking, this library ensures that function executions are properly debounced or throttled across multiple instances, preventing excessive or unintended operations.

Key Features

  • Debounce: Ensures a function is executed only once after a specified interval since the last call, minimizing redundant operations.
  • Throttle: Limits the execution frequency of a function, ensuring it's not executed more than once within a specified timeframe.
  • Distributed Locks: Implements the RedLock algorithm for distributed locking to coordinate debounce and throttle logic across distributed systems.
  • Redis Integration: Utilizes Redis for managing timestamps and locks, offering a scalable solution for state synchronization.

Getting Started

Installation

Install DistributedDebounceThrottle via NuGet:

dotnet add package DistributedDebounceThrottle
Enter fullscreen mode Exit fullscreen mode

Usage

To integrate DistributedDebounceThrottle in your application, ensure you have a Redis instance ready for connection. Here's how to get started:

  1. Configure Services: In your application's startup configuration, register DistributedDebounceThrottle:
public void ConfigureServices(IServiceCollection services)
{
    // Using an existing IConnectionMultiplexer instance:
    services.AddDistributedDebounceThrottle(settings);

    // Or, initiating a new IConnectionMultiplexer with a connection string:
    services.AddDistributedDebounceThrottle(redisConnectionString, settings);
}
Enter fullscreen mode Exit fullscreen mode
  1. Inject and Use IDebounceThrottle: Inject IDebounceThrottle to access debounce and throttle dispatchers:
public class SampleService
{
    private readonly IDispatcher _throttler;

    public SampleService(IDebounceThrottle debounceThrottle)
    {
        _throttler = debounceThrottle.ThrottleDispatcher("uniqueDispatcherId", TimeSpan.FromSeconds(5));
    }

    public Task ExecuteThrottledOperation()
    {
        return _throttler.DispatchAsync(async () =>
        {
            // Operation logic here.
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Configuration

Customize your debounce and throttle settings via DebounceThrottleSettings:

  • RedisKeysPrefix: A prefix for all Redis keys (default "debounce-throttle:").
  • RedLockExpiryTime: The expiry time for the distributed locks (default TimeSpan.FromSeconds(10)).

DebounceThrottle: A Local Solution

For developers who don't need distributed functionalities and are looking for a local solution, DebounceThrottle provides simple yet effective debounce and throttle dispatchers. This library supports asynchronous actions and handles exceptions, ensuring that all Task results from dispatcher calls are consistent with the result of a single invocation.

Debounce Demo

The following example shows how to display entered text after stopping pressing keys for 1000 milliseconds:

class Program
{
    static void Main(string[] args)
    {
        string str = "";
        var debounceDispatcher = new DebounceDispatcher(1000);
        while(true)
        {
            var key = Console.ReadKey(true);

            //trigger when to stop and exit
            if (key.Key == ConsoleKey.Escape) break;

            str += key.KeyChar;

            //every keypress iteration call dispatcher but the Action will be invoked only after stopping pressing and waiting 1000 milliseconds
            debounceDispatcher.Debounce(() =>
            {
                Console.WriteLine($"{str} - {DateTime.UtcNow.ToString("hh:mm:ss.fff")}");
                str = "";
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Throttle Demo

The following example shows how to call an action every 100 milliseconds but invoke it only once in 1000 milliseconds (after the last invoking completed):

class Program
{
    static void Main(string[] args)
    {
        bool stop = false;
        //trigger when to stop and exit
        Task.Run(() => { Console.ReadKey(true); stop = true; });

        var throttleDispatcher = new ThrottleDispatcher(1000);
        do
        {
            //every iteration call dispatcher but the Action will be invoked only once in 1500 milliseconds (500 action work time + 1000 interval)
            throttleDispatcher.ThrottleAsync(async () =>
            {
                Console.WriteLine($"{ DateTime.UtcNow.ToString("hh:mm:ss.fff") }");
                await Task.Delay(500);
            });

            //iteration every 100 milliseconds
            Thread.Sleep(100);
        }
        while (!stop); //wait trigger to stop and exit
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Whether you need a distributed solution with Redis integration or a simple local library, both DistributedDebounceThrottle and DebounceThrottle provide robust debounce and throttle mechanisms for .NET applications. By integrating these libraries, you can enhance your application's performance and reliability, effectively managing function executions to prevent system overloads and redundant operations.

Links:

Top comments (0)