unified-worker.mjs 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. /** Resident RAM query projection; PostgreSQL is the only persistent data source. */
  2. import { spawn } from 'node:child_process';
  3. import { fileURLToPath } from 'node:url';
  4. import { createInterface } from 'node:readline';
  5. export function unifiedReader({ python, unifiedConfig, timeoutMs = 120000 } = {}) {
  6. let child, nextId = 0, closed = false;
  7. const pending = new Map();
  8. function start() {
  9. const worker = spawn(python, ['-B', '-X', 'utf8', fileURLToPath(new URL('./voc-library/unified_worker.py', import.meta.url))],
  10. { windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'],
  11. env: { ...process.env, KSF_UNIFIED_DB_CONFIG: unifiedConfig } });
  12. child = worker;
  13. worker.stderr.resume();
  14. worker.stdin.on('error', () => {});
  15. const terminate = () => {
  16. if (child !== worker) return;
  17. for (const item of pending.values()) { clearTimeout(item.timer); item.reject(Error('UNIFIED_WORKER_UNAVAILABLE')); }
  18. pending.clear(); child = undefined;
  19. };
  20. worker.on('error', terminate); worker.on('exit', terminate);
  21. const lines = createInterface({ input: worker.stdout });
  22. lines.on('line', line => {
  23. if (child !== worker) return;
  24. if (Buffer.byteLength(line) > 6000000) return worker.kill();
  25. try {
  26. const data = JSON.parse(line), item = pending.get(data.id);
  27. if (item) { clearTimeout(item.timer); pending.delete(data.id); item.resolve(data.result); }
  28. } catch { worker.kill(); }
  29. });
  30. }
  31. const read = (engine, config, request) => new Promise((resolve, reject) => {
  32. if (closed) return reject(Error('UNIFIED_WORKER_CLOSED'));
  33. if (pending.size >= 4) return reject(Error('LIBRARY_BUSY'));
  34. if (!child) start();
  35. const id = ++nextId;
  36. const worker = child;
  37. const timer = setTimeout(() => { worker.kill(); }, timeoutMs);
  38. pending.set(id, { resolve, reject, timer });
  39. child.stdin.write(JSON.stringify({ id, engine, config, request }) + '\n');
  40. });
  41. read.close = () => { closed = true; child?.kill(); };
  42. return read;
  43. }