feat: Add auxiliary support to LockingResourceStore

This way locks are always on the associated resource
if there is a request on an auxiliary resource.
This commit is contained in:
Joachim Van Herwegen
2021-01-26 17:29:44 +01:00
parent 7f34fe6ae3
commit 061c856161
4 changed files with 122 additions and 34 deletions

View File

@@ -1,6 +1,7 @@
import type { Readable } from 'stream';
import { promisify } from 'util';
import eos from 'end-of-stream';
import type { AuxiliaryIdentifierStrategy } from '../ldp/auxiliary/AuxiliaryIdentifierStrategy';
import type { Patch } from '../ldp/http/Patch';
import { BasicRepresentation } from '../ldp/representation/BasicRepresentation';
import type { Representation } from '../ldp/representation/Representation';
@@ -17,46 +18,61 @@ const endOfStream = promisify(eos);
* Store that for every call acquires a lock before executing it on the requested resource,
* and releases it afterwards.
* In case the request returns a Representation the lock will only be released when the data stream is finished.
*
* For auxiliary resources the lock will be applied to the associated resource.
* The actual operation is still executed on the auxiliary resource.
*/
export class LockingResourceStore implements AtomicResourceStore {
protected readonly logger = getLoggerFor(this);
private readonly source: ResourceStore;
private readonly locks: ExpiringReadWriteLocker;
private readonly strategy: AuxiliaryIdentifierStrategy;
public constructor(source: ResourceStore, locks: ExpiringReadWriteLocker) {
public constructor(source: ResourceStore, locks: ExpiringReadWriteLocker, strategy: AuxiliaryIdentifierStrategy) {
this.source = source;
this.locks = locks;
this.strategy = strategy;
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return this.lockedRepresentationRun(identifier,
return this.lockedRepresentationRun(this.getLockIdentifier(identifier),
async(): Promise<Representation> => this.source.getRepresentation(identifier, preferences, conditions));
}
public async addResource(container: ResourceIdentifier, representation: Representation,
conditions?: Conditions): Promise<ResourceIdentifier> {
return this.locks.withWriteLock(container,
return this.locks.withWriteLock(this.getLockIdentifier(container),
async(): Promise<ResourceIdentifier> => this.source.addResource(container, representation, conditions));
}
public async setRepresentation(identifier: ResourceIdentifier, representation: Representation,
conditions?: Conditions): Promise<void> {
return this.locks.withWriteLock(identifier,
return this.locks.withWriteLock(this.getLockIdentifier(identifier),
async(): Promise<void> => this.source.setRepresentation(identifier, representation, conditions));
}
public async deleteResource(identifier: ResourceIdentifier, conditions?: Conditions): Promise<void> {
return this.locks.withWriteLock(identifier,
return this.locks.withWriteLock(this.getLockIdentifier(identifier),
async(): Promise<void> => this.source.deleteResource(identifier, conditions));
}
public async modifyResource(identifier: ResourceIdentifier, patch: Patch, conditions?: Conditions): Promise<void> {
return this.locks.withWriteLock(identifier,
return this.locks.withWriteLock(this.getLockIdentifier(identifier),
async(): Promise<void> => this.source.modifyResource(identifier, patch, conditions));
}
/**
* Acquires the correct identifier to lock this resource.
* For auxiliary resources this means the associated identifier.
*/
protected getLockIdentifier(identifier: ResourceIdentifier): ResourceIdentifier {
return this.strategy.isAuxiliaryIdentifier(identifier) ?
this.strategy.getAssociatedIdentifier(identifier) :
identifier;
}
/**
* Acquires a lock that is only released when all data of the resulting representation data has been read,
* an error occurs, or the timeout has been triggered.