patterncsharpMinor
Synchronization of transaction processing
Viewed 0 times
processingsynchronizationtransaction
Problem
There's a server which does the following:
The task is to properly synchronize steps 2, 3 and 4.
Since for each request new instance of transaction object will be created, I decided to create
`class Transaction
{
public int Id { get; set; }
public int Counter { get; set; }
public void Process()
{
Counter++;
}
}
class TransactionService
{
private readonly object syncRoot;
private readonly Dictionary> transactionLockMap; // id -> (referenceCount, lock).
public TransactionService()
{
syncRoot = new object();
transactionLockMap = new Dictionary>();
}
public IDisposable AcquireTransactionLock(int transactionId)
{
return new TransactionLock(transactionId, this);
}
class TransactionLock : IDisposable
{
private readonly int transactionId;
private readonly TransactionService transactionService;
public TransactionLock(int transactionId, TransactionService transactionService)
{
this.transactionId = transactionId;
this.transactionService = transactionService;
Tuple transactionLock;
lock (transactionService.syncRoot)
{
if (!transactionService.transactionLockMap.TryGetValue(transactionId, out transactionLock))
{
transactionLock = Tuple.Create(1, new object());
}
else
{
transactionLock = Tuple.Create(transactionLock.Item1 + 1, transactionLock.Item2);
}
transactionService.transactionLockMap[transactionId] = transactionLock;
}
- Receive request with transaction id
- Load corresponding transaction from storage. New transaction object is returned each time
- Process transaction
- Save updated transaction back to storage
The task is to properly synchronize steps 2, 3 and 4.
Since for each request new instance of transaction object will be created, I decided to create
TransactionService class with AcquireTransactionLock method to be called by request handling code.`class Transaction
{
public int Id { get; set; }
public int Counter { get; set; }
public void Process()
{
Counter++;
}
}
class TransactionService
{
private readonly object syncRoot;
private readonly Dictionary> transactionLockMap; // id -> (referenceCount, lock).
public TransactionService()
{
syncRoot = new object();
transactionLockMap = new Dictionary>();
}
public IDisposable AcquireTransactionLock(int transactionId)
{
return new TransactionLock(transactionId, this);
}
class TransactionLock : IDisposable
{
private readonly int transactionId;
private readonly TransactionService transactionService;
public TransactionLock(int transactionId, TransactionService transactionService)
{
this.transactionId = transactionId;
this.transactionService = transactionService;
Tuple transactionLock;
lock (transactionService.syncRoot)
{
if (!transactionService.transactionLockMap.TryGetValue(transactionId, out transactionLock))
{
transactionLock = Tuple.Create(1, new object());
}
else
{
transactionLock = Tuple.Create(transactionLock.Item1 + 1, transactionLock.Item2);
}
transactionService.transactionLockMap[transactionId] = transactionLock;
}
Solution
I cannot verify this pattern for its correctnes but I find you can simplify and make the
Let the
Now you can even reuse the
TransactionLocker better testable by removing the trasaction logic from it.Let the
Locker do only the locking and delegate the actions by using two lambdas: one for onLocked and the other one for onUnlocking.class Locker : IDisposable
{
private readonly object _syncRoot;
private readonly Func _onLocked;
private readonly Action _onUnlocking;
private object _lockedObject;
public Locker(object syncRoot, Func onLocked, Action onUnlocking)
{
_syncRoot = syncRoot;
_onLocked = onLocked;
_onUnlocking = onUnlocking;
lock (_syncRoot)
{
_lockedObject = _onLocked();
}
Monitor.Enter(_lockedObject);
}
public void Dispose()
{
lock (_syncRoot)
{
_onUnlocking();
}
Monitor.Exit(_lockedObject);
}
}Now you can even reuse the
Locker somewhere else because it's not longer tightly coupled to the transation.Code Snippets
class Locker : IDisposable
{
private readonly object _syncRoot;
private readonly Func<object> _onLocked;
private readonly Action _onUnlocking;
private object _lockedObject;
public Locker(object syncRoot, Func<object> onLocked, Action onUnlocking)
{
_syncRoot = syncRoot;
_onLocked = onLocked;
_onUnlocking = onUnlocking;
lock (_syncRoot)
{
_lockedObject = _onLocked();
}
Monitor.Enter(_lockedObject);
}
public void Dispose()
{
lock (_syncRoot)
{
_onUnlocking();
}
Monitor.Exit(_lockedObject);
}
}Context
StackExchange Code Review Q#150775, answer score: 3
Revisions (0)
No revisions yet.