Archive for the 'SimpleTasks' Category

SimpleTasks: Ejecutando Tareas en NodeJS (1)

Tuesday, January 12th, 2016

Hace unos años escribí SimpleBus y lo usé como uno de los ejemplo en mi charla en JSConf Uruguay 2015. Puede recibir mensajes y los envía a todo subscriptor interesado. El subscriptor puede especificar las características de los mensajes en los que está interesado, ya sea por valores de propiedades o por una función predicado. Ver el ejemplo distribuido:

https://github.com/ajlopez/SimpleBus/tree/master/samples/Market

Pero en estos días, tengo otro caso de uso en puerta: ejecutar tareas. Una tarea debería ser descripta en un simple objeto JavaScript plano, sin funciones (serializable a JSON), y los subscriptores pueden ejecutar algunos tipos de tareas. Las tareas se distinguen por una propiedad especial, que es el tipo. Como siempre, fui programando todo usando el flujo de trabajo de TDD (Test-Driven Development) en el proyecto:

https://github.com/ajlopez/SimpleTasks

Como práctica de TDD y de programación. Debe haber alguna librería que cumpla con los casos de uso que quiero manejar, pero siempre es bueno practicar, y al practicar, uno comprende mejor el dominio que está tratando de implementar, los problemas que pueden surgir en la implementación. Y también me entreno en el camino de la simplicidad, y en aprender a tomar “baby-steps”, en vez de solucionar todo el problema de una sola vez. Esto permite que la implementación vaya emergiendo de los mini-casos de uso que van planteando los tests.

Un test que prueba el envío y ejecución de una tarea:

exports['post and subscribe task'] = function (test) {
    test.async();
    
    var engine = st.engine();
    
    engine.post({ type: 'process', options: { value: 42 } });
    
    engine.subscribe('process', function (task) {
        test.ok(task);
        test.equal(task.type, 'process');
        test.ok(task.options);
        test.equal(task.options.value, 42);
        
        engine.stop();
        
        test.done();
    });
}

El gran método es el .post: recibe una tarea, y se encarga de enviarla a UN SOLO subscriptor de esa tarea. Esto no sería más que un pasaje de mensaje, pero también hay opciones: el método post recibe un segundo argumento opcional, que describe algunas características de la tarea a ejecutar, por ejemplo, la fecha/hora de comienzo (no ejecutar la tarea hasta llegar a esa fecha/hora). Ejemplo de test:

exports['post and subscribe task with starting option'] = function (test) {
    test.async();
    
    var engine = st.engine();
    
    var start = (new Date()).getTime();
    var starting = dates.toNormalDateTimeString(new Date(start + 2500));
    
    engine.post({ type: 'process', options: { value: 42 } }, { starting: starting });
    
    engine.subscribe('process', function (task) {
        var now = (new Date()).getTime();

        test.ok(task);
        test.equal(task.type, 'process');
        test.ok(task.options);
        test.equal(task.options.value, 42);

        test.ok(now > start);
        test.ok(now > start + 1800);
        test.ok(now < start + 3500);
        
        engine.stop();
        
        test.done();
    });
}

Pero quiero agregarle más opciones: el principal caso de uso que tengo en mente, es tareas que se repiten, por ejemplo, por día, por semana todos los martes, o por hora hasta 5 veces, y así.

Una cosa que no maneja, en las tareas a ejecutar inmediatamente, es el “back pressure”: no detiene a los productores de tareas si se satura. Un tema a discutir si es necesario en algún caso de uso, imagino el uso de una cola bloqueante.

También quiero poder distribuir tareas a consumidores/ejecutores remotos. Podría usar un SimpleQueue, pero esta vez voy a ir experimentando con ActiveMQ y el protocolo Stomp, usando stomp-client.

Próximos posts: implementación de esas opciones, ejemplo local, y ejemplo distribuido (imagino que un webcrawler).

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez