Skip to content

Commit

Permalink
Feature/lock (#158)
Browse files Browse the repository at this point in the history
* Added new 2 methods for ILock contract, acquireBlocking, runBlocking

* Added changeset file
  • Loading branch information
yousif-khalil-abdulkarim authored Feb 18, 2025
1 parent 57e4bb5 commit 89aa51e
Show file tree
Hide file tree
Showing 7 changed files with 841 additions and 40 deletions.
6 changes: 6 additions & 0 deletions .changeset/shy-frogs-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@daiso-tech/core": minor
---

Added 2 new methods for <i>ILock</i> contract, <i>acquireBlocking</i> and <i>runBlocking</i>.
They retry acquiring the lock at a set interval until the timeout is reached."
27 changes: 25 additions & 2 deletions src/lock/contracts/lock.contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,18 @@ import type { LockEvents } from "@/lock/contracts/lock.events.js";
*/
export type ILockListener = IEventListenable<LockEvents>;

export type AquireBlockingSettings = {
time?: TimeSpan;
interval?: TimeSpan;
};
/**
*
* IMPORT_PATH: ```"@daiso-tech/core/lock/contracts"```
* @group Contracts
*/
export type ILock = ILockListener & {
/**
* The <i>run</i> method wraps an async function or <i>{@link LazyPromise}</i> with the <i>acquire</i> and <i>release</i> method.
* @throws {UnableToAquireLockError} {@link UnableToAquireLockError}
* @throws {UnableToReleaseLockError} {@link UnableToReleaseLockError}
*/
Expand All @@ -44,6 +49,7 @@ export type ILock = ILockListener & {
): LazyPromise<Result<TValue, KeyAlreadyAcquiredLockError>>;

/**
* The <i>runOrFail</i> method wraps an async function or <i>{@link LazyPromise}</i> with the <i>acquireOrFail</i> and <i>release</i> method.
* @throws {UnableToAquireLockError} {@link UnableToAquireLockError}
* @throws {UnableToReleaseLockError} {@link UnableToReleaseLockError}
* @throws {KeyAlreadyAcquiredLockError} {@link KeyAlreadyAcquiredLockError}
Expand All @@ -52,14 +58,31 @@ export type ILock = ILockListener & {
asyncFn: LazyPromiseable<TValue>,
): LazyPromise<TValue>;

/**
* The <i>runBlocking</i> method wraps an async function or <i>{@link LazyPromise}</i> with the <i>acquireBlocking</i> and <i>release</i> method.
* @throws {UnableToAquireLockError} {@link UnableToAquireLockError}
* @throws {UnableToReleaseLockError} {@link UnableToReleaseLockError}
*/
runBlocking<TValue = void>(
asyncFn: LazyPromiseable<TValue>,
settings?: AquireBlockingSettings,
): LazyPromise<Result<TValue, KeyAlreadyAcquiredLockError>>;

/**
* The <i>acquire</i> method acquires a lock only if the lock is not already acquired.
* Throws an error if a different owner attempts to release the lock.
* Returns true if the lock is acquired otherwise true is returned.
* Returns true if the lock is acquired otherwise false is returned.
* @throws {UnableToAquireLockError} {@link UnableToAquireLockError}
*/
acquire(): LazyPromise<boolean>;

/**
* The <i>acquireBlocking</i> method acquires a lock only if the lock is not already acquired.
* If the lock is acquired, it retries every <i>settings.interval</i> until <i>settings.time</i> is reached.
* Returns true if the lock is acquired otherwise false is returned.
* @throws {UnableToAquireLockError} {@link UnableToAquireLockError}
*/
acquireBlocking(settings?: AquireBlockingSettings): LazyPromise<boolean>;

/**
* The <i>acquireOrFail</i> method acquires a lock only if the lock is not already acquired.
* Throws an error if already acquired.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,23 @@ export type LockProviderFactorySettings<TAdapters extends string> = {
*/
defaultTtl?: TimeSpan | null;

/**
* The default refresh time used in the <i>{@link ILock}</i> <i>acquireBlocking</i> and <i>runBlocking</i> methods.
* @default {TimeSpan.fromSeconds(1)}
*/
defaultBlockingInterval?: TimeSpan;

/**
* The default refresh time used in the <i>{@link ILock}</i> <i>acquireBlocking</i> and <i>runBlocking</i> methods.
* @default {TimeSpan.fromMinutes(1)}
*/
defaultBlockingTime?: TimeSpan;

/**
* The default refreshtime used in the <i>{@link ILock}</i> <i>extend</i> method.
* @default TimeSpan.fromMinutes(5);`
*/
defaultExtendTime?: TimeSpan;
defaultRefreshTime?: TimeSpan;

/**
* The default retry attempt to use in the returned <i>LazyPromise</i>.
Expand Down Expand Up @@ -132,7 +144,9 @@ export class LockProviderFactory<TAdapters extends string>
private readonly createOwnerId?: () => string;
private readonly eventBus?: IGroupableEventBus<any>;
private readonly defaultTtl?: TimeSpan | null;
private readonly defaultExtendTime?: TimeSpan;
private readonly defaultBlockingInterval?: TimeSpan;
private readonly defaultBlockingTime?: TimeSpan;
private readonly defaultRefreshTime?: TimeSpan;
private readonly retryAttempts?: number | null;
private readonly backoffPolicy?: BackoffPolicy | null;
private readonly retryPolicy?: RetryPolicy | null;
Expand Down Expand Up @@ -179,7 +193,9 @@ export class LockProviderFactory<TAdapters extends string>
createOwnerId,
eventBus,
defaultTtl,
defaultExtendTime,
defaultBlockingInterval,
defaultBlockingTime,
defaultRefreshTime,
retryAttempts,
backoffPolicy,
retryPolicy,
Expand All @@ -193,7 +209,9 @@ export class LockProviderFactory<TAdapters extends string>
this.createOwnerId = createOwnerId;
this.eventBus = eventBus;
this.defaultTtl = defaultTtl;
this.defaultExtendTime = defaultExtendTime;
this.defaultBlockingInterval = defaultBlockingInterval;
this.defaultBlockingTime = defaultBlockingTime;
this.defaultRefreshTime = defaultRefreshTime;
this.retryAttempts = retryAttempts;
this.backoffPolicy = backoffPolicy;
this.retryPolicy = retryPolicy;
Expand Down Expand Up @@ -225,7 +243,9 @@ export class LockProviderFactory<TAdapters extends string>
backoffPolicy: this.backoffPolicy,
retryPolicy: this.retryPolicy,
timeout: this.timeout,
defaultRefreshTime: this.defaultExtendTime,
defaultBlockingInterval: this.defaultBlockingInterval,
defaultBlockingTime: this.defaultBlockingTime,
defaultRefreshTime: this.defaultRefreshTime,
createOwnerId: this.createOwnerId,
});
cacheRecord[key] = lockProvider;
Expand Down
26 changes: 24 additions & 2 deletions src/lock/implementations/derivables/lock-provider/lock-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,25 @@ export type LockProviderSettings = {

/**
* You can decide the default ttl value for <i>{@link ILock}</i> expiration. If null is passed then no ttl will be used by default.
* @default TimeSpan.fromMinutes(5);
* @default {TimeSpan.fromMinutes(5)}
*/
defaultTtl?: TimeSpan | null;

/**
* The default refresh time used in the <i>{@link ILock}</i> <i>acquireBlocking</i> and <i>runBlocking</i> methods.
* @default {TimeSpan.fromSeconds(1)}
*/
defaultBlockingInterval?: TimeSpan;

/**
* The default refresh time used in the <i>{@link ILock}</i> <i>acquireBlocking</i> and <i>runBlocking</i> methods.
* @default {TimeSpan.fromMinutes(1)}
*/
defaultBlockingTime?: TimeSpan;

/**
* The default refresh time used in the <i>{@link ILock}</i> <i>extend</i> method.
* @default TimeSpan.fromMinutes(5);
* @default {TimeSpan.fromMinutes(5)}
*/
defaultRefreshTime?: TimeSpan;

Expand Down Expand Up @@ -146,6 +158,8 @@ export class LockProvider implements IGroupableLockProvider {
private readonly createOwnerId: () => string;
private readonly adapter: ILockAdapter;
private readonly defaultTtl: TimeSpan;
private readonly defaultBlockingInterval: TimeSpan;
private readonly defaultBlockingTime: TimeSpan;
private readonly defaultRefreshTime: TimeSpan;
private readonly retryAttempts: number | null;
private readonly backoffPolicy: BackoffPolicy | null;
Expand Down Expand Up @@ -184,6 +198,8 @@ export class LockProvider implements IGroupableLockProvider {
createOwnerId = () => v4(),
adapter,
defaultTtl = LockProvider.DEFAULT_TTL,
defaultBlockingInterval = TimeSpan.fromSeconds(1),
defaultBlockingTime = TimeSpan.fromMinutes(1),
defaultRefreshTime = LockProvider.DEFAULT_REFRESH_TIME,
retryAttempts = null,
backoffPolicy = null,
Expand All @@ -202,6 +218,8 @@ export class LockProvider implements IGroupableLockProvider {
}
this.serde = serde;
this.defaultTtl = defaultTtl ?? LockProvider.DEFAULT_TTL;
this.defaultBlockingInterval = defaultBlockingInterval;
this.defaultBlockingTime = defaultBlockingTime;
this.defaultRefreshTime = defaultRefreshTime;
this.retryAttempts = retryAttempts;
this.backoffPolicy = backoffPolicy;
Expand All @@ -214,6 +232,8 @@ export class LockProvider implements IGroupableLockProvider {

private registerToSerde(): void {
const transformer = new LockSerdeTransformer({
defaultBlockingInterval: this.defaultBlockingInterval,
defaultBlockingTime: this.defaultBlockingTime,
adapter: this.adapter,
backoffPolicy: this.backoffPolicy,
defaultRefreshTime: this.defaultRefreshTime,
Expand Down Expand Up @@ -576,6 +596,8 @@ export class LockProvider implements IGroupableLockProvider {
const { ttl = this.defaultTtl, owner = this.createOwnerId() } =
settings;
return new Lock({
defaultBlockingInterval: this.defaultBlockingInterval,
defaultBlockingTime: this.defaultBlockingTime,
lockProviderEventDispatcher: this.lockProviderEventBus,
lockEventBus: this.eventBus,
adapter: this.adapter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,40 +28,13 @@ import type { ILockStateRecord } from "@/lock/implementations/derivables/lock-pr
*/
export type LockSerdeTransformerSettings = {
adapter: ILockAdapter;

/**
* In order to listen to events of <i>{@link LockProvider}</i> class you must pass in <i>{@link IGroupableEventBus}</i>.
*/
eventBus: IGroupableEventBus<any>;

/**
* The default refresh time used in the <i>{@link ILock}</i> <i>extend</i> method.
* @default TimeSpan.fromMinutes(5);
*/
defaultBlockingTime: TimeSpan;
defaultBlockingInterval: TimeSpan;
defaultRefreshTime: TimeSpan;

/**
* The default retry attempt to use in the returned <i>LazyPromise</i>.
* @default {null}
*/
retryAttempts?: number | null;

/**
* The default backof policy to use in the returned <i>LazyPromise</i>.
* @default {null}
*/
backoffPolicy?: BackoffPolicy | null;

/**
* The default retry policy to use in the returned <i>LazyPromise</i>.
* @default {null}
*/
retryPolicy?: RetryPolicy | null;

/**
* The default timeout to use in the returned <i>LazyPromise</i>.
* @default {null}
*/
timeout?: TimeSpan | null;
};

Expand All @@ -72,6 +45,8 @@ export class LockSerdeTransformer
implements ISerdeTransformer<Lock, ISerializedLock>
{
private readonly adapter: ILockAdapter;
private readonly defaultBlockingInterval: TimeSpan;
private readonly defaultBlockingTime: TimeSpan;
private readonly defaultRefreshTime: TimeSpan;
private readonly retryAttempts: number | null;
private readonly backoffPolicy: BackoffPolicy | null;
Expand All @@ -84,6 +59,8 @@ export class LockSerdeTransformer
constructor(settings: LockSerdeTransformerSettings) {
const {
adapter,
defaultBlockingInterval,
defaultBlockingTime,
defaultRefreshTime,
retryAttempts = null,
backoffPolicy = null,
Expand All @@ -94,6 +71,8 @@ export class LockSerdeTransformer
}),
} = settings;
this.adapter = adapter;
this.defaultBlockingInterval = defaultBlockingInterval;
this.defaultBlockingTime = defaultBlockingTime;
this.defaultRefreshTime = defaultRefreshTime;
this.retryAttempts = retryAttempts;
this.backoffPolicy = backoffPolicy;
Expand Down Expand Up @@ -124,10 +103,12 @@ export class LockSerdeTransformer
}
const ttl = ttlInMs ? TimeSpan.fromMilliseconds(ttlInMs) : null;
return new Lock({
defaultBlockingInterval: this.defaultBlockingInterval,
defaultBlockingTime: this.defaultBlockingTime,
defaultRefreshTime: this.defaultRefreshTime,
lockProviderEventDispatcher: this.lockProviderEventBus,
lockEventBus: this.eventBus,
adapter,
defaultRefreshTime: this.defaultRefreshTime,
key,
owner,
ttl,
Expand Down
Loading

0 comments on commit 89aa51e

Please sign in to comment.