import { logger } from '../utils/logger.js'; import dbManager from './databaseManager.js'; const PARENT_TABLE = 'rcu_action.rcu_action_events'; const PARENT_INDEX_STATEMENTS = [ 'CREATE INDEX IF NOT EXISTS idx_rcu_action_hotel_id ON rcu_action.rcu_action_events (hotel_id);', 'CREATE INDEX IF NOT EXISTS idx_rcu_action_room_id ON rcu_action.rcu_action_events (room_id);', 'CREATE INDEX IF NOT EXISTS idx_rcu_action_device_id ON rcu_action.rcu_action_events (device_id);', 'CREATE INDEX IF NOT EXISTS idx_rcu_action_direction ON rcu_action.rcu_action_events (direction);', 'CREATE INDEX IF NOT EXISTS idx_rcu_action_cmd_word ON rcu_action.rcu_action_events (cmd_word);', 'CREATE INDEX IF NOT EXISTS idx_rcu_action_action_type ON rcu_action.rcu_action_events (action_type);', 'CREATE INDEX IF NOT EXISTS idx_rcu_action_query_main ON rcu_action.rcu_action_events (hotel_id, room_id, ts_ms DESC);' ]; class PartitionManager { async ensureParentIndexes(client) { for (const sql of PARENT_INDEX_STATEMENTS) { await client.query(sql); } } /** * Calculate the start and end timestamps (milliseconds) for a given date. * @param {Date} date - The date to calculate for. * @returns {Object} { startMs, endMs, partitionSuffix } */ getPartitionInfo(date) { const yyyy = date.getFullYear(); const mm = String(date.getMonth() + 1).padStart(2, '0'); const dd = String(date.getDate()).padStart(2, '0'); const partitionSuffix = `${yyyy}${mm}${dd}`; const start = new Date(date); start.setHours(0, 0, 0, 0); const startMs = start.getTime(); const end = new Date(date); end.setDate(end.getDate() + 1); end.setHours(0, 0, 0, 0); const endMs = end.getTime(); return { startMs, endMs, partitionSuffix }; } /** * Ensure partitions exist for the next N days. * @param {number} daysAhead - Number of days to pre-create. */ async ensurePartitions(daysAhead = 30) { const client = await dbManager.pool.connect(); try { logger.info(`Starting partition check for the next ${daysAhead} days...`); await this.ensureParentIndexes(client); const now = new Date(); for (let i = 0; i < daysAhead; i++) { const targetDate = new Date(now); targetDate.setDate(now.getDate() + i); const { startMs, endMs, partitionSuffix } = this.getPartitionInfo(targetDate); const partitionName = `rcu_action.rcu_action_events_${partitionSuffix}`; // Check if partition exists const checkSql = ` SELECT to_regclass($1) as exists; `; const checkRes = await client.query(checkSql, [partitionName]); if (!checkRes.rows[0].exists) { logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`); const createSql = ` CREATE TABLE IF NOT EXISTS ${partitionName} PARTITION OF ${PARENT_TABLE} FOR VALUES FROM (${startMs}) TO (${endMs}); `; await client.query(createSql); } } logger.info('Partition check completed.'); } catch (err) { logger.error('Error ensuring partitions:', err); throw err; } finally { client.release(); } } } export default new PartitionManager();