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.'); } }