farmcontrol-ws/src/lock/lockmanager.js
Tom Butcher d695772a3a Refactor LockManager to improve key handling and event broadcasting
- Updated constructor to use socketClient instead of socketManager for better clarity.
- Changed key management methods to use a consistent naming convention for setting and deleting locks.
- Enhanced event listener setup to utilize socketClient for broadcasting lock updates.
- Improved logging for lock and unlock operations to provide clearer insights into the locking mechanism.
2025-08-18 01:08:49 +01:00

107 lines
3.0 KiB
JavaScript

import { etcdServer } from '../database/etcd.js';
import log4js from 'log4js';
import { loadConfig } from '../config.js';
const config = loadConfig();
// Setup logger
const logger = log4js.getLogger('Lock Manager');
logger.level = config.server.logLevel;
/**
* LockManager handles distributed locking using Etcd and broadcasts lock events via websockets.
*/
export class LockManager {
constructor(socketClient) {
this.socketClient = socketClient;
this.setupLocksListeners();
}
async lockObject(object) {
// Add a 'lock' event to the 'locks' stream
logger.debug('Locking object:', object._id);
try {
await etcdServer.setKey(`/${object.type}s/${object._id}/lock`, {
...object,
locked: true
});
logger.info(`Lock event to id: ${object._id}`);
return true;
} catch (err) {
logger.error(`Error adding lock event to: ${object._id}:`, err);
throw err;
}
}
async unlockObject(object) {
// Add an 'unlock' event to the 'locks' stream
const key = `/${object.type}s/${object._id}/lock`;
try {
logger.debug('Checking user can unlock:', object._id);
const lockEvent = await etcdServer.getKey(key);
if (lockEvent?.user === object.user) {
logger.debug('Unlocking object:', object._id);
await etcdServer.deleteKey(key);
logger.info(`Unlocked object: ${object._id}`);
return true;
}
} catch (err) {
logger.error(`Error unlocking object ${object._id}:`, err);
throw err;
}
}
async getObjectLock(object) {
// Get the current lock status of an object and broadcast it
logger.info('Getting lock status for object:', object._id);
try {
const lockKey = `/${object.type}s/${object._id}/lock`;
const lockValue = await etcdServer.getKey(lockKey);
if (lockValue) {
// Object is locked
logger.debug(`Object ${object._id} is locked`);
return {
...lockValue,
locked: true
};
} else {
// Object is not locked
logger.debug(`Object ${object._id} is not locked`);
return {
_id: object._id,
locked: false
};
}
} catch (err) {
logger.error(`Error getting lock status for object ${object._id}:`, err);
throw err;
}
}
setupLocksListeners() {
etcdServer.onPrefixPutEvent(
'/locks',
this.socketClient.id,
(key, value) => {
const id = key.split('/').pop();
logger.debug('Lock object event:', id);
this.socketManager.broadcast('notify_lock_update', {
...value,
locked: true
});
}
);
etcdServer.onPrefixDeleteEvent('/locks', this.socketClient.id, key => {
const id = key.split('/').pop();
logger.debug('Unlock object event:', id);
this.socketManager.broadcast('notify_lock_update', {
_id: id,
locked: false
});
});
logger.info('Subscribed to Etcd stream for lock changes.');
}
}