Added locking expiration time and tests
MODIFY
src/locking/LockingHelper.ts
src/locking/LockingHelper.ts
@@ -17,7 +17,7 @@ export default class LockingHelper {
}
public static async createSiteAreaSmartChargingLock(tenantID: string, siteArea: SiteArea, timeout: number): Promise<Lock | null> {
- const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.SITE_AREA, `${siteArea.id}-smart-charging`);
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.SITE_AREA, `${siteArea.id}-smart-charging`, 18000);
if (!(await LockingManager.acquire(lock, timeout))) {
return null;
}

MODIFY
src/locking/LockingManager.ts
src/locking/LockingManager.ts
@@ -17,8 +17,8 @@ const MODULE_NAME = 'LockingManager';
* - E = mutually exclusive
*/
export default class LockingManager {
- public static createExclusiveLock(tenantID: string, entity: LockEntity, key: string): Lock {
- return this.createLock(tenantID, entity, key, LockType.EXCLUSIVE);
+ public static createExclusiveLock(tenantID: string, entity: LockEntity, key: string, expirationTimeSeconds?: number): Lock {
+ return this.createLock(tenantID, entity, key, LockType.EXCLUSIVE, expirationTimeSeconds);
}
public static async acquire(lock: Lock, timeout?: number): Promise<boolean> {
@@ -86,6 +86,8 @@ export default class LockingManager {
message: `The lock '${lock.entity}' ('${lock.key}') of type '${lock.type}' in Tenant ID ${lock.tenantID} is expired. It was released automatically`,
detailedMessages: { lock, error: error.message, stack: error.stack }
});
+ await LockingStorage.insertLock(lock);
+ return true;
} catch {
await Logging.logError({
tenantID: lock.tenantID,
@@ -134,7 +136,7 @@ export default class LockingManager {
}
}
- private static createLock(tenantID: string, entity: LockEntity, key: string, type: LockType = LockType.EXCLUSIVE, timeout?: number): Lock {
+ private static createLock(tenantID: string, entity: LockEntity, key: string, type: LockType = LockType.EXCLUSIVE, expirationTimeSeconds?: number): Lock {
if (!tenantID) {
throw new BackendError({
action: ServerAction.LOCKING,
@@ -170,8 +172,8 @@ export default class LockingManager {
hostname: Utils.getHostname()
};
// Set expiration date
- if (timeout > 0) {
- lock.expirationDate = moment(lock.timestamp).add(timeout, 'seconds').toDate();
+ if (expirationTimeSeconds > 0) {
+ lock.expirationDate = moment(lock.timestamp).add(expirationTimeSeconds, 'seconds').toDate();
}
// Return the built lock
return lock;

MODIFY
src/storage/mongodb/LockingStorage.ts
src/storage/mongodb/LockingStorage.ts
@@ -69,6 +69,7 @@ export default class LockingStorage {
key: lockToSave.key,
type: lockToSave.type,
timestamp: Utils.convertToDate(lockToSave.timestamp),
+ expirationDate: lockToSave.expirationDate,
hostname: lockToSave.hostname
};
// Create

MODIFY
test/api/LockingTest.ts
test/api/LockingTest.ts
@@ -18,6 +18,7 @@ import chaiDatetime from 'chai-datetime';
import chaiSubset from 'chai-subset';
import config from '../config';
import global from '../../src/types/GlobalType';
+import moment from 'moment';
import responseHelper from '../helpers/responseHelper';
chai.use(chaiDatetime);
@@ -36,7 +37,7 @@ const testData = new TestData();
const lockKey = 'test-lock';
describe('Locking Tests', function() {
- this.timeout(30000);
+ this.timeout(100000);
before(async () => {
// Start MongoDB
@@ -130,4 +131,42 @@ describe('Locking Tests', function() {
expect(result).to.eql(false);
});
});
+
+ describe('Test automatic release of locks', () => {
+ it('Should create a Site Area exclusive lock with expiration of 5 seconds', async () => {
+ // Get the Site Area
+ const siteArea = testData.siteAreaContext.getSiteArea();
+ // Create and Acquire lock
+ testData.siteExclusiveLock = LockingManager.createExclusiveLock(testData.tenantContext.getTenant().id, LockEntity.SITE_AREA, `${siteArea.id}-smart-charging`, 5);(
+ testData.tenantContext.getTenant().id, siteArea, 30 * 1000);
+ expect(testData.siteExclusiveLock).not.null;
+ expect(testData.siteExclusiveLock.id).not.null;
+ expect(testData.siteExclusiveLock.hostname).not.null;
+ expect(testData.siteExclusiveLock.timestamp).not.null;
+ expect(testData.siteExclusiveLock.tenantID).to.eql(testData.tenantContext.getTenant().id);
+ expect(testData.siteExclusiveLock.entity).to.eql(LockEntity.SITE_AREA);
+ expect(testData.siteExclusiveLock.key).to.eql(siteArea.id + '-smart-charging');
+ expect(testData.siteExclusiveLock.type).to.eql(LockType.EXCLUSIVE);
+ expect(testData.siteExclusiveLock.expirationDate).to.eql(moment(testData.siteExclusiveLock.timestamp).add(5, 'seconds').toDate());
+ await LockingManager.acquire(testData.siteExclusiveLock);
+ });
+
+ it('Should not acquire a second time a Site Area exclusive lock', async () => {
+ const result = await LockingManager.acquire(testData.siteExclusiveLock);
+ expect(result).not.null;
+ expect(result).to.eql(false);
+ });
+
+ it('Should acquire the lock with a timeout of 30 seconds. First lock should be released after 10 seconds ', async () => {
+ const result = await LockingManager.acquire(testData.siteExclusiveLock, 30 * 1000);
+ expect(result).not.null;
+ expect(result).to.eql(true);
+ });
+
+ it('Should release the latest Site Area exclusive lock', async () => {
+ const result = await LockingManager.release(testData.siteExclusiveLock);
+ expect(result).not.null;
+ expect(result).to.eql(true);
+ });
+ });
});

Added locking expiration time and tests