mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: add support for key namespacePrefixes in a RedisLocker instance
This commit is contained in:
parent
09afebbc84
commit
d690cc7ed0
@ -50,11 +50,19 @@ export class RedisLocker implements ReadWriteLocker, ResourceLocker, Initializab
|
|||||||
private readonly redisRw: RedisReadWriteLock;
|
private readonly redisRw: RedisReadWriteLock;
|
||||||
private readonly redisLock: RedisResourceLock;
|
private readonly redisLock: RedisResourceLock;
|
||||||
private readonly attemptSettings: Required<AttemptSettings>;
|
private readonly attemptSettings: Required<AttemptSettings>;
|
||||||
|
private readonly namespacePrefix: string;
|
||||||
private finalized = false;
|
private finalized = false;
|
||||||
|
|
||||||
public constructor(redisClient = '127.0.0.1:6379', attemptSettings: AttemptSettings = {}) {
|
/**
|
||||||
|
* Creates a new RedisClient
|
||||||
|
* @param redisClient - Redis connection string of a standalone Redis node
|
||||||
|
* @param attemptSettings - Override default AttemptSettings
|
||||||
|
* @param namespacePrefix - Override default namespacePrefixes (used to prefix keys in Redis)
|
||||||
|
*/
|
||||||
|
public constructor(redisClient = '127.0.0.1:6379', attemptSettings: AttemptSettings = {}, namespacePrefix = '') {
|
||||||
this.redis = this.createRedisClient(redisClient);
|
this.redis = this.createRedisClient(redisClient);
|
||||||
this.attemptSettings = { ...attemptDefaults, ...attemptSettings };
|
this.attemptSettings = { ...attemptDefaults, ...attemptSettings };
|
||||||
|
this.namespacePrefix = namespacePrefix;
|
||||||
|
|
||||||
// Register lua scripts
|
// Register lua scripts
|
||||||
for (const [ name, script ] of Object.entries(REDIS_LUA_SCRIPTS)) {
|
for (const [ name, script ] of Object.entries(REDIS_LUA_SCRIPTS)) {
|
||||||
@ -94,7 +102,7 @@ export class RedisLocker implements ReadWriteLocker, ResourceLocker, Initializab
|
|||||||
* @returns A scoped Redis key that allows cleanup afterwards without affecting other keys.
|
* @returns A scoped Redis key that allows cleanup afterwards without affecting other keys.
|
||||||
*/
|
*/
|
||||||
private getReadWriteKey(identifier: ResourceIdentifier): string {
|
private getReadWriteKey(identifier: ResourceIdentifier): string {
|
||||||
return `${PREFIX_RW}${identifier.path}`;
|
return `${this.namespacePrefix}${PREFIX_RW}${identifier.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +111,7 @@ export class RedisLocker implements ReadWriteLocker, ResourceLocker, Initializab
|
|||||||
* @returns A scoped Redis key that allows cleanup afterwards without affecting other keys.
|
* @returns A scoped Redis key that allows cleanup afterwards without affecting other keys.
|
||||||
*/
|
*/
|
||||||
private getResourceKey(identifier: ResourceIdentifier): string {
|
private getResourceKey(identifier: ResourceIdentifier): string {
|
||||||
return `${PREFIX_LOCK}${identifier.path}`;
|
return `${this.namespacePrefix}${PREFIX_LOCK}${identifier.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ReadWriteLocker methods */
|
/* ReadWriteLocker methods */
|
||||||
@ -199,12 +207,12 @@ export class RedisLocker implements ReadWriteLocker, ResourceLocker, Initializab
|
|||||||
* Remove any lock still open
|
* Remove any lock still open
|
||||||
*/
|
*/
|
||||||
private async clearLocks(): Promise<void> {
|
private async clearLocks(): Promise<void> {
|
||||||
const keysRw = await this.redisRw.keys(`${PREFIX_RW}*`);
|
const keysRw = await this.redisRw.keys(`${this.namespacePrefix}${PREFIX_RW}*`);
|
||||||
if (keysRw.length > 0) {
|
if (keysRw.length > 0) {
|
||||||
await this.redisRw.del(...keysRw);
|
await this.redisRw.del(...keysRw);
|
||||||
}
|
}
|
||||||
|
|
||||||
const keysLock = await this.redisLock.keys(`${PREFIX_LOCK}*`);
|
const keysLock = await this.redisLock.keys(`${this.namespacePrefix}${PREFIX_LOCK}*`);
|
||||||
if (keysLock.length > 0) {
|
if (keysLock.length > 0) {
|
||||||
await this.redisLock.del(...keysLock);
|
await this.redisLock.del(...keysLock);
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,15 @@ const redis: jest.Mocked<Redis & RedisResourceLock & RedisReadWriteLock> = {
|
|||||||
jest.mock('ioredis', (): any => jest.fn().mockImplementation((): Redis => redis));
|
jest.mock('ioredis', (): any => jest.fn().mockImplementation((): Redis => redis));
|
||||||
|
|
||||||
describe('A RedisLocker', (): void => {
|
describe('A RedisLocker', (): void => {
|
||||||
|
it('will generate keys with the given namespacePrefix.', async(): Promise<void> => {
|
||||||
|
const identifier = { path: 'http://test.com/resource' };
|
||||||
|
const lockerPrefixed = new RedisLocker('6379', {}, 'MY_PREFIX');
|
||||||
|
await lockerPrefixed.acquire(identifier);
|
||||||
|
const allLocksPrefixed = Object.keys(store.internal).every((key): boolean => key.startsWith('MY_PREFIX'));
|
||||||
|
await lockerPrefixed.release(identifier);
|
||||||
|
expect(allLocksPrefixed).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
describe('with Read-Write logic', (): void => {
|
describe('with Read-Write logic', (): void => {
|
||||||
const resource1 = { path: 'http://test.com/resource' };
|
const resource1 = { path: 'http://test.com/resource' };
|
||||||
const resource2 = { path: 'http://test.com/resource2' };
|
const resource2 = { path: 'http://test.com/resource2' };
|
||||||
@ -392,8 +401,8 @@ describe('A RedisLocker', (): void => {
|
|||||||
const emitter = new EventEmitter();
|
const emitter = new EventEmitter();
|
||||||
const promise = locker.withWriteLock(resource1, (): any =>
|
const promise = locker.withWriteLock(resource1, (): any =>
|
||||||
new Promise<void>((resolve): any => emitter.on('release', resolve)));
|
new Promise<void>((resolve): any => emitter.on('release', resolve)));
|
||||||
await redis.releaseWriteLock(`__RW__${resource1.path}`);
|
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
await redis.releaseWriteLock(`__RW__${resource1.path}`);
|
||||||
emitter.emit('release');
|
emitter.emit('release');
|
||||||
await expect(promise).rejects.toThrow('Redis operation error detected (value was null).');
|
await expect(promise).rejects.toThrow('Redis operation error detected (value was null).');
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user