Repository navigation
Expand file tree
/
Copy pathOracleDistributedLock.cs
More file actions
86 lines (69 loc) · 3.95 KB
/
Copy pathOracleDistributedLock.cs
File metadata and controls
86 lines (69 loc) · 3.95 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
using Medallion.Threading.Internal;
using Medallion.Threading.Internal.Data;
using System.Data;
namespace Medallion.Threading.Oracle;
/// <summary>
/// Implements a distributed lock for Oracle databse based on the DBMS_LOCK package
/// </summary>
public sealed partial class OracleDistributedLock : IInternalDistributedLock<OracleDistributedLockHandle>
{
internal const int MaxNameLength = 128;
private readonly IDbDistributedLock _internalLock;
/// <summary>
/// Constructs a lock with the given <paramref name="name"/> that connects using the provided <paramref name="connectionString"/> and
/// <paramref name="options"/>.
///
/// Unless <paramref name="exactName"/> is specified, <paramref name="name"/> will be escaped/hashed to ensure name validity.
/// </summary>
public OracleDistributedLock(string name, string connectionString, Action<OracleConnectionOptionsBuilder>? options = null, bool exactName = false)
: this(name, exactName, n => CreateInternalLock(n, connectionString, options))
{
}
/// <summary>
/// Constructs a lock with the given <paramref name="name"/> that connects using the provided <paramref name="connection" />.
///
/// Unless <paramref name="exactName"/> is specified, <paramref name="name"/> will be escaped/hashed to ensure name validity.
/// </summary>
public OracleDistributedLock(string name, IDbConnection connection, bool exactName = false)
: this(name, exactName, n => CreateInternalLock(n, connection))
{
}
private OracleDistributedLock(string name, bool exactName, Func<string, IDbDistributedLock> internalLockFactory)
{
this.Name = GetName(name, exactName);
this._internalLock = internalLockFactory(this.Name);
}
internal static string GetName(string name, bool exactName)
{
if (name == null) { throw new ArgumentNullException(nameof(name)); }
if (exactName)
{
if (name.Length > MaxNameLength) { throw new FormatException($"{nameof(name)}: must be at most {MaxNameLength} characters"); }
// Oracle treats NULL as the empty string. See https://stackoverflow.com/questions/13278773/null-vs-empty-string-in-oracle
if (name.Length == 0) { throw new FormatException($"{nameof(name)} must not be empty"); }
return name;
}
return DistributedLockHelpers.ToSafeName(name, MaxNameLength, s => s.Length == 0 ? "EMPTY" : s);
}
/// <summary>
/// Implements <see cref="IDistributedLock.Name"/>
/// </summary>
public string Name { get; }
ValueTask<OracleDistributedLockHandle?> IInternalDistributedLock<OracleDistributedLockHandle>.InternalTryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken) =>
this._internalLock.TryAcquireAsync(timeout, OracleDbmsLock.ExclusiveLock, cancellationToken, contextHandle: null).Wrap(h => new OracleDistributedLockHandle(h));
internal static IDbDistributedLock CreateInternalLock(string name, string connectionString, Action<OracleConnectionOptionsBuilder>? options)
{
if (connectionString == null) { throw new ArgumentNullException(nameof(connectionString)); }
var (keepaliveCadence, useMultiplexing) = OracleConnectionOptionsBuilder.GetOptions(options);
if (useMultiplexing)
{
return new OptimisticConnectionMultiplexingDbDistributedLock(name, connectionString, OracleMultiplexedConnectionLockPool.Instance, keepaliveCadence);
}
return new DedicatedConnectionOrTransactionDbDistributedLock(name, () => new OracleDatabaseConnection(connectionString), useTransaction: false, keepaliveCadence);
}
internal static IDbDistributedLock CreateInternalLock(string name, IDbConnection connection)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
return new DedicatedConnectionOrTransactionDbDistributedLock(name, () => new OracleDatabaseConnection(connection));
}
}