Firebase Functions + SWI-Prolog WASM (Node) - Memory build-up issue

A few years ago I wanted to try to plug a serverless Prolog service to a Web app and I successfully managed to have SP running on a Google Cloud Run container (Cloud Run is a service firing container instances to serve requests, allowing one to use any language/environment they want; I used the minimal swipl Docker image provided officially for SP).
I’ve always been eager to find different solutions, though (easier to implement/less expensive etc).

Now, as I’ve seen that SP has WebAssembly support (I found the doc pages and the wiki page and discussion here), I wondered whether Firebase Functions could be used instead.
There are many advantages to using Firebase Functions, as a certain kind of them is callable directly from the client (they’re called “callable” for that) and has automatic support for authorization and a greatly simplified experience.

I tried the provided swipl-wasm package with a local Node project and I was able to use Prolog seamlessy with JS (and this is real “prog porn”, if you ask me).

(I couldn’t run it with the "swipl-wasm/dist/swipl" entry point. It worked simply with "swipl-wasm".)

So I tried to deploy the same test as a Firebase Function.
(You define Firebase Functions by building them with the methods provided by the firebase-functions API, by exporting them and by deploying them to the cloud.)

import functions from "firebase-functions";
import SWIPL from "swipl-wasm";

export const testBetween = functions.https.onCall(async (data, context) => {
  const swipl = await SWIPL({ arguments: ["-q"] });

  const result = [];

  for (const res of swipl.prolog.query("between(1, 20, X)."))
    result.push(res.X / 2);
  
  return result;
});

The problem is that the swipl allocation at the start of the function seems to trigger a memory build-up that surpasses the 256 MB limit imposed on Firebase Functions (and also the next one of 512 MB, I tried), so the function always fails.

I have three different questions:

  1. What’s the reason for the memory issue and is it solvable?

  2. In case it is solved and the function works, I’m not sure things are being properly cleaned up after getting the result; should I call swipl.prolog.call("halt.") or anything after issuing queries?

  3. Firebase Functions documentation suggests keeping expensive objects as global objects outside function definitions so that they may possibly be reused by function instances fired after a cold start (see here)
    Would there be any issues with moving the swipl object to the global scope and it being reused many times by different function calls? (I guess this also intersects the issue of multithreading)

Thanks.

Memory leakage is due to bugs in Emscripten. More info: Question: How to properly destroy `SWIPL` · Issue #23 · SWI-Prolog/npm-swipl-wasm · GitHub

Keeping one instance (or a pool of them) and using a queue (ex. promise-queue) to access might be a solution.

1 Like

Thanks, that has certainly to do with the problem.

I would have guessed that the SWIPL object would be instantiated only once per function call, but I’m not so sure right now. There must be some unfavorable interaction between how the service works and that emscripten issue.

This is a known issue with a fix upstream already (see Question: How to properly destroy `SWIPL` · Issue #23 · SWI-Prolog/npm-swipl-wasm · GitHub)

I’ll try and get a chance to bump the version of emscripten used in the coming weeks

1 Like

Prs are welcome :hugs: