Every subscription add/change/remove recomputed the full geo union
over all 7k+ subscriptions with a synchronous turf.union chain,
freezing the single Node event loop (and thus DDP/HTTP) for minutes.
The same recompute also runs at startup ("Subs union outdated"),
so every restart froze the site too.
calcUnionAsync yields to the event loop periodically during the
union chain, and subsUnion.js now fires recomputes without blocking
Meteor.startup or the observer callbacks, serializing overlapping
triggers instead of stacking them.
52 lines
2.1 KiB
JavaScript
52 lines
2.1 KiB
JavaScript
import tcircle from '@turf/circle';
|
|
import tunion from '@turf/union';
|
|
import ttrunc from '@turf/truncate';
|
|
|
|
const truncOptions = { precision: 6, coordinates: 2 };
|
|
// How many polygons to process before yielding to the event loop once.
|
|
const YIELD_EVERY = 100;
|
|
|
|
const yieldToEventLoop = () => new Promise((resolve) => { setImmediate(resolve); });
|
|
|
|
/**
|
|
* Circle-union of subscriptions, equivalent to map-common-utils' calcUnion(typeCircle=true),
|
|
* but split into a `for` loop that yields to the event loop every YIELD_EVERY polygons.
|
|
* The vendored calcUnion runs the same turf.union chain fully synchronously, which for
|
|
* thousands of subscriptions blocks Meteor's single-threaded DDP/HTTP handling for minutes
|
|
* (see plan-modernizacion notes on the subsUnion hang). This keeps the exact same geometry
|
|
* output while letting other requests interleave between chunks.
|
|
*/
|
|
const calcUnionAsync = async (subs, decorated) => {
|
|
const unionGroup = [];
|
|
for (let i = 0; i < subs.length; i += 1) {
|
|
const osub = subs[i];
|
|
try {
|
|
if (osub.location && osub.location.lat && osub.location.lon && osub.distance) {
|
|
const dsub = decorated(osub);
|
|
unionGroup.push(tcircle(
|
|
[dsub.location.lon, dsub.location.lat],
|
|
dsub.distance,
|
|
{ units: 'kilometers', steps: 144 }
|
|
));
|
|
} else {
|
|
console.info(`Wrong element to do union ${JSON.stringify(osub)}`);
|
|
}
|
|
} catch (e) {
|
|
console.error(e, `Wrong element trying to make union ${JSON.stringify(osub)}`);
|
|
}
|
|
// Deliberate: yield to the event loop periodically instead of blocking it.
|
|
// eslint-disable-next-line no-await-in-loop
|
|
if (i % YIELD_EVERY === 0) await yieldToEventLoop();
|
|
}
|
|
|
|
let unionTemp = unionGroup.length > 0 ? ttrunc(unionGroup[0], truncOptions) : null;
|
|
for (let i = 1; i < unionGroup.length; i += 1) {
|
|
unionTemp = ttrunc(tunion(unionTemp, unionGroup[i]), truncOptions);
|
|
// Deliberate: yield to the event loop periodically instead of blocking it.
|
|
// eslint-disable-next-line no-await-in-loop
|
|
if (i % YIELD_EVERY === 0) await yieldToEventLoop();
|
|
}
|
|
return unionTemp;
|
|
};
|
|
|
|
export default calcUnionAsync;
|