Skip to content

Commit f97ab84

Browse files
simzzzkonstantinabl
authored andcommitted
feat: create LockService class + relevant interfaces (#4605)
Signed-off-by: Simeon Nakov <[email protected]> Signed-off-by: Konstantina Blazhukova <[email protected]>
1 parent 4a2706c commit f97ab84

File tree

7 files changed

+129
-0
lines changed

7 files changed

+129
-0
lines changed

package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/relay/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"dependencies": {
5454
"@hashgraph/json-rpc-config-service": "file:../config-service",
5555
"@hashgraph/sdk": "^2.63.0",
56+
"async-mutex": "^0.5.0",
5657
"axios": "^1.4.0",
5758
"axios-retry": "^4.5.0",
5859
"better-lookup": "^1.3.0",

packages/relay/src/lib/services/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ export * from './rateLimiterService/RedisRateLimitStore';
1717
export * from './rateLimiterService/rateLimiterService';
1818
export * from './transactionPoolService/LocalPendingTransactionStorage';
1919
export * from './transactionPoolService/transactionPoolService';
20+
export * from './lockService/LockService';
21+
export * from './lockService/LockStrategyFactory';
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
import { LockStrategy } from '../../types/lock';
4+
5+
/**
6+
* Service that manages transaction ordering through distributed locking.
7+
* Uses a strategy pattern to support both local (in-memory) and distributed (Redis) locking.
8+
*/
9+
export class LockService {
10+
/**
11+
* The underlying lock strategy implementation (Local or Redis).
12+
*/
13+
private readonly strategy: LockStrategy;
14+
15+
/**
16+
* Creates a new LockService instance.
17+
*
18+
* @param strategy - The lock strategy implementation to use.
19+
*/
20+
constructor(strategy: LockStrategy) {
21+
this.strategy = strategy;
22+
}
23+
24+
/**
25+
* Acquires a lock for the specified address.
26+
* Blocks until the lock is available (no timeout on waiting).
27+
*
28+
* @param address - The sender address to acquire the lock for.
29+
* @returns A promise that resolves to a unique session key.
30+
*/
31+
async acquireLock(address: string): Promise<string> {
32+
return await this.strategy.acquireLock(address);
33+
}
34+
35+
/**
36+
* Releases a lock for the specified address.
37+
* Only succeeds if the session key matches the current lock holder.
38+
*
39+
* @param address - The sender address to release the lock for.
40+
* @param sessionKey - The session key obtained during lock acquisition.
41+
*/
42+
async releaseLock(address: string, sessionKey: string): Promise<void> {
43+
await this.strategy.releaseLock(address, sessionKey);
44+
}
45+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
import { Logger } from 'pino';
4+
import { RedisClientType } from 'redis';
5+
6+
import { LockStrategy } from '../../types/lock';
7+
8+
/**
9+
* Factory for creating LockStrategy instances.
10+
*
11+
* Encapsulates the logic for selecting the appropriate lock strategy implementation
12+
* based on available infrastructure (Redis vs in-memory).
13+
*/
14+
export class LockStrategyFactory {
15+
/**
16+
* Creates a LockStrategy instance.
17+
*
18+
* @param redisClient - Optional Redis client. If provided, creates Redis-backed lock strategy;
19+
* otherwise creates local in-memory lock strategy.
20+
* @param logger - Logger instance for the lock strategy.
21+
* @returns A LockStrategy implementation.
22+
*/
23+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
24+
static create(redisClient: RedisClientType | undefined, logger: Logger): LockStrategy {
25+
// TODO: Remove placeholder errors once strategies are implemented
26+
if (redisClient) {
27+
throw new Error('Redis lock strategy not yet implemented');
28+
}
29+
30+
throw new Error('Local lock strategy not yet implemented');
31+
}
32+
}

packages/relay/src/lib/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export * from './spendingPlanConfig';
1313
export * from './registry';
1414
export * from './debug';
1515
export * from './rateLimiter';
16+
export * from './lock';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
/**
4+
* Interface for lock strategy implementations.
5+
* Strategies handle the actual locking mechanism (local in-memory or distributed via Redis).
6+
*
7+
* @remarks
8+
* Implementations must normalize addresses (e.g., lowercase) internally to ensure consistency.
9+
*/
10+
export interface LockStrategy {
11+
/**
12+
* Acquires a lock for the specified address.
13+
* Blocks until the lock is available or timeout is reached.
14+
*
15+
* @param address - The address to acquire the lock for (will be normalized by implementation).
16+
* @returns A promise that resolves to a unique session key upon successful acquisition.
17+
*/
18+
acquireLock(address: string): Promise<string>;
19+
20+
/**
21+
* Releases a lock for the specified address.
22+
* Only succeeds if the provided session key matches the current lock holder.
23+
*
24+
* @param address - The address to release the lock for (will be normalized by implementation).
25+
* @param sessionKey - The session key proving ownership of the lock.
26+
* @returns A promise that resolves when the lock is released or rejected if not owner.
27+
*/
28+
releaseLock(address: string, sessionKey: string): Promise<void>;
29+
}

0 commit comments

Comments
 (0)