86 lines
2.4 KiB
JavaScript
86 lines
2.4 KiB
JavaScript
/**
|
|
* 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<string, PearDataModel>} */
|
|
const remotes = new Map()
|
|
|
|
/**
|
|
* @param {string} dbKeyHex
|
|
* @returns {Promise<PearDataModel|null>}
|
|
*/
|
|
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)
|
|
}
|
|
}
|