Skip to content

Commit

Permalink
added workaround to mapnik-node memory leak (see mapnik/node-mapnik#950)
Browse files Browse the repository at this point in the history
  • Loading branch information
zdila committed Jul 6, 2020
1 parent c577304 commit 4f07d96
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 13 deletions.
14 changes: 11 additions & 3 deletions lib/httpServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const mimeType = config.get('format.mimeType');
let generateMapnikConfig;
let mapFeatureProperties;

const white = new mapnik.Color('white');

const images = new Map();

router.get('/:zz/:xx/:yy', async (ctx) => {
const { zz, xx, yy } = ctx.params;

Expand Down Expand Up @@ -74,11 +78,15 @@ router.get('/:zz/:xx/:yy', async (ctx) => {

ctx.type = 'image/png';

const im = new mapnik.Image(256 * scale, 256 * scale);
let body = images.get(scale);

await im.fill(new mapnik.Color('white'));
if (!body) {
const im = new mapnik.Image(256 * scale, 256 * scale);
await im.fill(white);
images.set(scale, await im.encodeAsync('png8:c=1:t=0'));
}

ctx.body = await im.encodeAsync('png8:c=1:t=0');
ctx.body = body;

// white 1x1
// ctx.body = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=', 'base64');
Expand Down
24 changes: 22 additions & 2 deletions lib/mapnikPool.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const { promisify } = require('util');
const { cpus } = require('os');
const process = require('process');

const mapnik = require('mapnik');
const config = require('config');
Expand Down Expand Up @@ -38,17 +37,38 @@ function initPool(mapnikConfig) {
mapnik.Image.prototype.demultiply,
);

mapnik.Image.prototype.resizeAsync = promisify(mapnik.Image.prototype.resize);

mapnik.Image.prototype.fillAsync = promisify(mapnik.Image.prototype.fill);

mapnik.Image.prototype.filterAsync = promisify(mapnik.Image.prototype.filter);

mapnik.Image.prototype.clearAsync = promisify(mapnik.Image.prototype.clear);

const nCpus = cpus().length;

const factory = {
async create() {
const map = new mapnik.Map(256, 256);
await map.fromStringAsync(mapnikConfig);
return { map, useCount: 0 };
let images = new Map();

return {
map,
useCount: 0,
async getImage(key, scale) {
let im = images.get(key);

if (!im) {
im = new mapnik.Image(256 * scale, 256 * scale);
images.set(scale, im);
} else {
await im.clearAsync();
}

return im;
},
};
},

async destroy() {
Expand Down
19 changes: 11 additions & 8 deletions lib/renderrer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ module.exports = { renderTile, exportMap };

mapnik.registerFonts(config.get('dirs.fonts'), { recurse: true });

const white = new mapnik.Color('white');

let cnt = 0;

// TODO if out of prerender area and reqScale is provided then render only that scale
Expand Down Expand Up @@ -89,16 +91,16 @@ async function renderSingleScale(p, zoom, x, y, scale, prerender) {
} tile ${spec}: `;

if (prerender) {
const a = dirtyTiles.get(tile2key({ zoom, x, y }));
if (!a) {
const dirtyTile = dirtyTiles.get(tile2key({ zoom, x, y }));
if (!dirtyTile) {
console.warn(`${logPrefix}no dirty meta found`);
return;
}

try {
const { mtimeMs } = await stat(`${ps}.${extension}`);
if (
mtimeMs > a.dt &&
mtimeMs > dirtyTile.dt &&
(!rerenderOlderThanMs || mtimeMs > rerenderOlderThanMs)
) {
console.log(`${logPrefix}fresh`);
Expand All @@ -111,10 +113,10 @@ async function renderSingleScale(p, zoom, x, y, scale, prerender) {

console.log(`${logPrefix}rendering`);

let im = new mapnik.Image(256 * scale, 256 * scale);

const maps = await getPool().acquire(prerender ? 1 : 0);
const { map } = maps;
const { map, getImage } = maps;

let im = await getImage('main', scale);

/** @type number */
let t;
Expand All @@ -138,8 +140,9 @@ async function renderSingleScale(p, zoom, x, y, scale, prerender) {
measure('render', Date.now() - t);

// this is to get rid of transparency because of edge blurring and JPEG
const bgIm = new mapnik.Image(256 * scale, 256 * scale);
await bgIm.fill(new mapnik.Color('white'));

const bgIm = await getImage('bg', scale);
await bgIm.fill(white);
await bgIm.premultiplyAsync();
await im.premultiplyAsync();
// dst.compositeAsync(src
Expand Down

0 comments on commit 4f07d96

Please sign in to comment.