Ejecuta el worker de producción contra suscripciones sintéticas realistas (península, Canarias, Baleares; radios de 5 a 100 km) y mide lo que la fase 12 pide: duración del recreate completo y del incrementalAdd, retraso máximo del event loop del proceso padre —que es donde vive DDP y por tanto la pregunta real de "¿se congela la web?"—, tamaño del documento frente al límite de 16 MiB y pico de RSS. `--spread world` reparte las suscripciones por todo el mundo: es el peor caso para el tamaño, porque casi no se solapan y la unión conserva un polígono por suscripción en vez de fundirlos. treeUnionWorker.js es una variante solo del banco (une en árbol en vez de en cadena) para poder comparar estrategias con números antes de tocar el worker de verdad. El worker de producción solo cambia en que ahora acepta `steps` por workerData, con el mismo 144 de siempre por defecto.
65 lines
3 KiB
JavaScript
65 lines
3 KiB
JavaScript
// Plain CommonJS worker_threads entry point, NOT part of the Meteor imports
|
|
// graph (lives under private/ so `meteor build` copies it verbatim into
|
|
// programs/server/assets/app/workers/unionWorker.js instead of compiling it).
|
|
//
|
|
// Runs the CPU-heavy part of the subscriptions geo-union (circle generation +
|
|
// sequential turf.union chain) off the main thread. A single tunion() call
|
|
// against an already-complex merged polygon can itself take seconds once
|
|
// there are thousands of subscriptions — yielding between iterations on the
|
|
// main thread doesn't help when one iteration alone blocks that long, so this
|
|
// needs a real worker thread, not just cooperative scheduling.
|
|
//
|
|
// workerData.subs must already be validated (location.lat/lon/distance
|
|
// present) and decorated (addNoisy/noNoisy already applied) by the caller.
|
|
// workerData.baseUnion, if given, is an already-computed union GeoJSON that
|
|
// subs gets merged into (the incremental-add fast path) instead of starting
|
|
// from scratch — for a single new subscription this is one tunion() call
|
|
// instead of redoing the whole chain over every subscription again.
|
|
// workerData.turfPaths gives absolute paths to @turf/circle, @turf/union and
|
|
// @turf/truncate — this file lives under programs/server/assets/, while the
|
|
// app's own npm deps live in the sibling programs/server/npm/node_modules,
|
|
// which plain require('@turf/circle') can never find by walking up parent
|
|
// directories. The caller resolves the paths (it's Meteor-compiled code that
|
|
// knows how to find them) and hands them over instead.
|
|
const { parentPort, workerData } = require('worker_threads');
|
|
|
|
// eslint-disable-next-line import/no-dynamic-require
|
|
const tcircle = require(workerData.turfPaths.circle).default;
|
|
// eslint-disable-next-line import/no-dynamic-require
|
|
const tunion = require(workerData.turfPaths.union).default;
|
|
// eslint-disable-next-line import/no-dynamic-require
|
|
const ttrunc = require(workerData.turfPaths.truncate).default;
|
|
|
|
const truncOptions = { precision: 6, coordinates: 2 };
|
|
|
|
// Vértices por círculo. 144 es lo que se ha venido usando; es también la
|
|
// palanca más directa sobre el tamaño del documento y sobre lo que tarda la
|
|
// cadena de uniones, así que el banco de carga (bench/union-bench.js) necesita
|
|
// poder moverlo. Sin `steps` en workerData el comportamiento es el de siempre.
|
|
const DEFAULT_STEPS = 144;
|
|
|
|
const run = () => {
|
|
const { subs, baseUnion, steps } = workerData;
|
|
const circles = subs.map(s => tcircle(
|
|
[s.location.lon, s.location.lat],
|
|
s.distance,
|
|
{ units: 'kilometers', steps: steps || DEFAULT_STEPS }
|
|
));
|
|
|
|
let unionTemp = baseUnion || null;
|
|
let start = 0;
|
|
if (!unionTemp && circles.length > 0) {
|
|
unionTemp = ttrunc(circles[0], truncOptions);
|
|
start = 1;
|
|
}
|
|
for (let i = start; i < circles.length; i += 1) {
|
|
unionTemp = ttrunc(tunion(unionTemp, circles[i]), truncOptions);
|
|
}
|
|
return unionTemp;
|
|
};
|
|
|
|
try {
|
|
parentPort.postMessage({ union: run() });
|
|
} catch (e) {
|
|
parentPort.postMessage({ error: e.message });
|
|
}
|