I needed an object I can use to serialize specific operations in an otherwise parallel workflow.
The specific use case I was solving for is request idempotency; a great example would be double-clicking a "Process Payment" button on a web form. I might have 100 payment processing requests in flight (parallel process) at any given time, but if two or three come in with the same credit card info, I want to make sure I process them serially (so I can deduplicate, etc.). In that case, a suitable key
value might be the credit card number.
I came up with the Keybox
class below. (Serializer
is so overloaded...)
public class Keybox : IKeybox
{
private readonly HashSet<string> Keys = new HashSet<string>();
public void Lock(string key, int timeout = 60, int tick = 100)
{
//timeout is specified in seconds, tick in milliseconds
//timeout = ConvertTimeoutToTicks(timeout, tick);
//This logic is not important for the question
timeout = 600;
var @event = new ManualResetEvent(false);
var counter = 0;
do
{
@event.Reset();
if (!Keys.Contains(key))
{
lock (Keys)
{
if (!Keys.Contains(key))
{
Keys.Add(key);
return;
}
}
}
Task.Delay(TimeSpan.FromMilliseconds(tick))
.ContinueWith(t => @event.Set());
@event.WaitOne();
} while (counter++ < timeout);
throw new Exception($"Unable to process '{key}'; timeout expired.");
}
public void Unlock(string key)
{
Keys.Remove(key);
}
}
Test code:
public void Test()
{
var box = new Keybox();
var key = "EFF82671-DE8F-46B9-B1F1-0FB8308052C3";
Parallel.ForEach(
Enumerable.Range(1, 3),
(i) =>
{
box.Lock(key);
Console.WriteLine($"{i}: Running");
Thread.Sleep(3000);
Console.WriteLine($"{i}: Ending");
box.Unlock(key);
}
);
}
which produces output like
1: Running 1: Ending 3: Running 3: Ending 2: Running 2: Ending
The main question is: is this an implementation of a specific design pattern? And the follow-on to that question is: is there a BCL class that already offers this functionality?
I'm also interested in general code review feedback.
in flight
here means parallel ? \$\endgroup\$ – Disappointed May 23 '17 at 8:49in flight
here means "currently being processed". english.stackexchange.com/a/189987/25517 \$\endgroup\$ – Matt Mills May 23 '17 at 12:54