-
Notifications
You must be signed in to change notification settings - Fork 248
Expand file tree
/
Copy pathPostgresDistributedLock.Transactions.cs
More file actions
136 lines (123 loc) · 8.07 KB
/
Copy pathPostgresDistributedLock.Transactions.cs
File metadata and controls
136 lines (123 loc) · 8.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using Medallion.Threading.Internal;
using System.Data;
namespace Medallion.Threading.Postgres;
public partial class PostgresDistributedLock
{
/// <summary>
/// Attempts to acquire a transaction-scoped advisory lock synchronously with an externally owned transaction. Usage:
/// <code>
/// var transaction = /* create a DB transaction */
///
/// var isLockAcquired = myLock.TryAcquireWithTransaction(..., transaction, ...)
///
/// if (isLockAcquired != null)
/// {
/// /* we have the lock! */
///
/// // Commit or Rollback the transaction, which in turn will release the lock
/// }
/// </code>
///
/// NOTE: The owner of the transaction is the responsible party for it - the owner must commit or rollback the transaction in order to release the acquired lock.
/// </summary>
/// <param name="key">The postgres advisory lock key which will be used to acquire the lock.</param>
/// <param name="transaction">The externally owned transaction which will be used to acquire the lock. The owner of the transaction must commit or rollback it for the lock to be released.</param>
/// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to 0.</param>
/// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
/// <returns>Whether the lock has been acquired</returns>
public static bool TryAcquireWithTransaction(PostgresAdvisoryLockKey key, IDbTransaction transaction, TimeSpan timeout = default, CancellationToken cancellationToken = default) =>
SyncViaAsync.Run(state => TryAcquireWithTransactionAsyncInternal(state.key, state.transaction, state.timeout, state.cancellationToken), (key, transaction, timeout, cancellationToken));
/// <summary>
/// Acquires a transaction-scoped advisory lock synchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Usage:
/// <code>
/// var transaction = /* create a DB transaction */
///
/// myLock.AcquireWithTransaction(..., transaction, ...)
///
/// /* we have the lock! */
///
/// // Commit or Rollback the transaction, which in turn will release the lock
/// </code>
///
/// NOTE: The owner of the transaction is the responsible party for it - the owner must commit or rollback the transaction in order to release the acquired lock.
/// </summary>
/// <param name="key">The postgres advisory lock key which will be used to acquire the lock.</param>
/// <param name="transaction">The externally owned transaction which will be used to acquire the lock. The owner of the transaction must commit or rollback it for the lock to be released.</param>
/// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
/// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
public static void AcquireWithTransaction(PostgresAdvisoryLockKey key, IDbTransaction transaction, TimeSpan? timeout = null, CancellationToken cancellationToken = default) =>
SyncViaAsync.Run(state => AcquireWithTransactionAsyncInternal(state.key, state.transaction, state.timeout, state.cancellationToken), (key, transaction, timeout, cancellationToken));
/// <summary>
/// Attempts to acquire a transaction-scoped advisory lock asynchronously with an externally owned transaction. Usage:
/// <code>
/// var transaction = /* create a DB transaction */
///
/// var isLockAcquired = await myLock.TryAcquireWithTransactionAsync(..., transaction, ...)
///
/// if (isLockAcquired != null)
/// {
/// /* we have the lock! */
///
/// // Commit or Rollback the transaction, which in turn will release the lock
/// }
/// </code>
///
/// NOTE: The owner of the transaction is the responsible party for it - the owner must commit or rollback the transaction in order to release the acquired lock.
/// </summary>
/// <param name="key">The postgres advisory lock key which will be used to acquire the lock.</param>
/// <param name="transaction">The externally owned transaction which will be used to acquire the lock. The owner of the transaction must commit or rollback it for the lock to be released.</param>
/// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to 0.</param>
/// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
/// <returns>Whether the lock has been acquired</returns>
public static ValueTask<bool> TryAcquireWithTransactionAsync(PostgresAdvisoryLockKey key, IDbTransaction transaction, TimeSpan timeout = default, CancellationToken cancellationToken = default) =>
TryAcquireWithTransactionAsyncInternal(key, transaction, timeout, cancellationToken);
/// <summary>
/// Acquires a transaction-scoped advisory lock asynchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Usage:
/// <code>
/// var transaction = /* create a DB transaction */
///
/// await myLock.AcquireWithTransaction(..., transaction, ...)
///
/// /* we have the lock! */
///
/// // Commit or Rollback the transaction, which in turn will release the lock
/// </code>
///
/// NOTE: The owner of the transaction is the responsible party for it - the owner must commit or rollback the transaction in order to release the acquired lock.
/// </summary>
/// <param name="key">The postgres advisory lock key which will be used to acquire the lock.</param>
/// <param name="transaction">The externally owned transaction which will be used to acquire the lock. The owner of the transaction must commit or rollback it for the lock to be released.</param>
/// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
/// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
public static ValueTask AcquireWithTransactionAsync(PostgresAdvisoryLockKey key, IDbTransaction transaction, TimeSpan? timeout = null, CancellationToken cancellationToken = default) =>
AcquireWithTransactionAsyncInternal(key, transaction, timeout, cancellationToken);
internal static ValueTask<bool> TryAcquireWithTransactionAsyncInternal(PostgresAdvisoryLockKey key, IDbTransaction transaction, TimeSpan timeout, CancellationToken cancellationToken)
{
if (key == null) { throw new ArgumentNullException(nameof(key)); }
if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); }
return TryAcquireAsync();
async ValueTask<bool> TryAcquireAsync()
{
var connection = new PostgresDatabaseConnection(transaction);
await using (connection.ConfigureAwait(false))
{
var lockAcquiredCookie = await PostgresAdvisoryLock.ExclusiveLock.TryAcquireAsync(connection, key.ToString(), timeout, cancellationToken).ConfigureAwait(false);
return lockAcquiredCookie != null;
}
}
}
internal static ValueTask AcquireWithTransactionAsyncInternal(PostgresAdvisoryLockKey key, IDbTransaction transaction, TimeSpan? timeout, CancellationToken cancellationToken)
{
if (key == null) { throw new ArgumentNullException(nameof(key)); }
if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); }
return AcquireAsync();
async ValueTask AcquireAsync()
{
var connection = new PostgresDatabaseConnection(transaction);
await using (connection.ConfigureAwait(false))
{
await PostgresAdvisoryLock.ExclusiveLock.TryAcquireAsync(connection, key.ToString(), timeout, cancellationToken).ThrowTimeoutIfNull().ConfigureAwait(false);
}
}
}
}