| 1234567891011121314151617181920212223242526272829303132333435363738394041424344 |
- /** Resident RAM query projection; PostgreSQL is the only persistent data source. */
- import { spawn } from 'node:child_process';
- import { fileURLToPath } from 'node:url';
- import { createInterface } from 'node:readline';
- export function unifiedReader({ python, unifiedConfig, timeoutMs = 120000 } = {}) {
- let child, nextId = 0, closed = false;
- const pending = new Map();
- function start() {
- const worker = spawn(python, ['-B', '-X', 'utf8', fileURLToPath(new URL('./voc-library/unified_worker.py', import.meta.url))],
- { windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'],
- env: { ...process.env, KSF_UNIFIED_DB_CONFIG: unifiedConfig } });
- child = worker;
- worker.stderr.resume();
- worker.stdin.on('error', () => {});
- const terminate = () => {
- if (child !== worker) return;
- for (const item of pending.values()) { clearTimeout(item.timer); item.reject(Error('UNIFIED_WORKER_UNAVAILABLE')); }
- pending.clear(); child = undefined;
- };
- worker.on('error', terminate); worker.on('exit', terminate);
- const lines = createInterface({ input: worker.stdout });
- lines.on('line', line => {
- if (child !== worker) return;
- if (Buffer.byteLength(line) > 6000000) return worker.kill();
- try {
- const data = JSON.parse(line), item = pending.get(data.id);
- if (item) { clearTimeout(item.timer); pending.delete(data.id); item.resolve(data.result); }
- } catch { worker.kill(); }
- });
- }
- const read = (engine, config, request) => new Promise((resolve, reject) => {
- if (closed) return reject(Error('UNIFIED_WORKER_CLOSED'));
- if (pending.size >= 4) return reject(Error('LIBRARY_BUSY'));
- if (!child) start();
- const id = ++nextId;
- const worker = child;
- const timer = setTimeout(() => { worker.kill(); }, timeoutMs);
- pending.set(id, { resolve, reject, timer });
- child.stdin.write(JSON.stringify({ id, engine, config, request }) + '\n');
- });
- read.close = () => { closed = true; child?.kill(); };
- return read;
- }
|