/** * Open linked peer HyperDBs (read-only) after Corestore replication. * * Local agent always owns `peardata-meta`. Linked peers' bees are opened by * `dbKeyHex` so warm `@peardata/metric-point` rows become queryable without * re-scraping the remote host. */ import b4a from 'b4a' import { PearDataModel } from './model.js' import { getCorestore } from './index.js' import logger from '../utils/logger.js' const log = logger.child('db:remote') /** @type {Map} */ const remotes = new Map() /** * @param {string} dbKeyHex * @returns {Promise} */ export async function openRemoteDb(dbKeyHex) { const keyHex = String(dbKeyHex || '').toLowerCase() if (!/^[0-9a-f]{64}$/.test(keyHex)) return null if (remotes.has(keyHex)) return remotes.get(keyHex) const store = getCorestore() if (!store) { log.warn('openRemoteDb: Corestore not open') return null } const core = store.get({ key: b4a.from(keyHex, 'hex') }) const model = new PearDataModel(core, { writable: false, autoUpdate: true }) await model.ready() remotes.set(keyHex, model) log.info('Opened remote HyperDB', { dbKeyHex: keyHex.slice(0, 16) }) return model } /** * @param {string} dbKeyHex */ export function getRemoteDb(dbKeyHex) { return remotes.get(String(dbKeyHex || '').toLowerCase()) || null } export function listRemoteDbs() { return [...remotes.entries()].map(([dbKeyHex, model]) => ({ dbKeyHex, discoveryKeyHex: model.discoveryKeyHex, length: model.db?.core?.length ?? null, })) } /** * Query warm metric points across all open remote DBs (first hit wins per call site). * @param {{ chart: string, afterMs: number, beforeMs: number, limit?: number, tier?: number }} opts */ export async function queryRemoteMetricPoints(opts) { /** @type {Array<{ chart: string, context: string, ts: number, tier: number, values: object }>} */ const all = [] for (const model of remotes.values()) { try { const rows = await model.queryMetricPoints(opts) if (rows?.length) all.push(...rows) } catch { // ignore per-remote failures } } all.sort((a, b) => a.ts - b.ts) const limit = opts.limit || 10_000 return all.length > limit ? all.slice(-limit) : all } export async function closeRemoteDbs() { for (const [key, model] of remotes) { try { await model.close() } catch { // ignore } remotes.delete(key) } }