Alessio Biancalana Grab The Blaster di Alessio Biancalana

Mamma, ho costruito una libreria di actor modeling per NodeJS

Hoverlord: actor model in action

Qualche tempo fa in un attimo di follia ho cominciato a farmi qualche schemino per portare alcune delle mie feature preferite di Elixir su NodeJS, per il semplice fatto che potevano essermi utili anche lì. Può sembrare che io stia cercando di avvitare un bullone con un cucchiaio, ma ci sono un paio di verità di cui preferisco fare un bel listato:

Quindi date queste doverose premesse ho sentito di poter dare un microscopico contributo all’ecosistema JavaScript con una libreria che consente di fare spawn di un worker process con del codice arbitrario all’interno. Allo stesso tempo il processo che viene avviato avrà una primitiva receive a disposizione che gli consentirà di ricevere i messaggi che gli vengono inviati. A questo punto data l’infrastruttura di comunicazione sottostante sarà possibile dividere la nostra applicazione in più attori dalla logica separata.

Lo snippet che c’è nel README è abbastanza esplicativo, la libreria (che si chiama hoverlord) consente di fare una cosa del genere:

const http = require('http');
const { spawn, send } = require('hoverlord');

spawn(() => {
  const { receive } = require('hoverlord');
  receive((_state, { content }) => {
    console.log(`log: ${message}`);
  });
}, 'logger');

spawn(() => {
  const { receive, send } = require('hoverlord');
  return receive((state, message) => {
    switch (message.content) {
      case 'ping':
        const newState = state + 1;
        send('logger', newState);
        return newState;
      default:
        return state;
    }
  }, 0);
}, 'stateReducer');

http
  .createServer((req, res) => {
    send('stateReducer', 'ping');
    res.writeHead(200);
    res.end(`Current process\n ${process.pid}`);
  })
  .listen(4000);

In pratica cosa abbiamo qui? Abbiamo un processo principale che avvia il webserver per un’applicazione web (tramite l’HTTP server di Node, niente di esoterico), il quale risponde a ogni richiesta sulla rotta principale con il PID del processo facendo però aumentare un contatore mantenuto in un processo separato, e facendo loggare quello stesso stato a un altro processo ancora.

Ovviamente il lavoro non è terminato, ma per cose semplici hoverlord fa il suo lavoro in maniera veramente cristallina, tant’è che io stesso sono rimasto stupito provando a scrivere un po’ di test con Jest e cominciando a stressare un bel po’ il design. Finora è sempre andato tutto bene, quindi se provate a usarla e beccate qualche bel bug vi prego di aprire una issue.

Devo dirmi da solo che questo è il progetto più interessante a cui abbia lavorato ultimamente, e la cosa allucinante è che è tutto merito mio (dell’interessanza, intendo). Ho sempre desiderato scrivere una cosa del genere, ma non sapevo da dove partire. Finalmente dopo quasi un decennio di esperienza sul campo con JavaScript sono riuscito a farmi passare la sindrome dell’impostore, e sono troppo felice di aver trovato il tempo e l’area giusta su cui focalizzare i miei sforzi per un po’.

Direzione del progetto, evoluzioni future eccetera

Una volta dimostrato che è possibile avere delle primitive di spawn e receive simili negli intenti a quelle di Erlang, sia pure con le dovute differenze tecnologiche, la più grande criticità di hoverlord è quella di avere una funzione di require patchata in modo da usare come path primario quello del progetto JavaScript originale e non la /tmp dove viene memorizzato il chunk di codice JavaScript “spawnato”. Questa cosa in un progetto reale mostra delle crepe non banali, e devo studiare meglio come superarla perché allo stadio attuale fare require di alcune funzioni dentro gli attori di hoverlord è un problema.

Questa è anche una grossa chiamata alle armi: se qualcuno avesse lo stomaco per leggersi il codice di hoverlord (be warned) non credo di potercela fare da solo.

Ringraziamenti

  1. Una prima versione di hoverlord usava la Cluster API al posto dei worker process. Matteo Collina e Tomas Della Vedova mi hanno convinto ben presto del fatto che fosse una scelta fallimentare :-) 

comments powered by Disqus

Member of

Previous Random Next