Posts Tagged: ‘memcached’

node.js, domino-db & Docker (7): The ValueHolder

7. November 2018 Posted by Sven Hasselbach

I am using this for years in Java, so I thought it would be great to use this approach also in the JavaScript world: The ValueHolder. The class allows to easily define „cachable“ code and it’s result, without having to handle the memcached part and – maybe in the future – background processing stuff.

To give you an idea what it is for here is a small example:

const allDummyDocs = new ValueHolder('allDummyDocs', 60, async () => {
  // get all documents with the Form 'dummy'
  return useServer(serverConfig).then(
    async server => {
      const db = await server.useDatabase(databaseConfig);
      const response = await db.bulkReadDocuments({
        query: "Form = 'dummy'"
      });
      return JSON.stringify(response);
    }).catch(err => {
      console.log(err);
      return err;
    });
});

The first parameter is the key used to store/retreive the value from memcached. The second one is the time how long the value should be cached. And the third parameter is the code to execute.

To use the definition in the application, you now have to use the get method of the value holder:

router.get('/showAllDummyDocs', (req, res) => {
  allDummyDocs.get(
      (error, result) => {
        if (error) {
          res.render('error', { title: 'Error', error });
        } else {
          res.render('index', { title: 'Express', result: `Result: ${result}` });
        }
      }
    );
  }
);

The ValueHolder checks now automatically, if the result is stored in the cache. If not, the code is executed and stored in the cache.

Here is the ValueHolder.js file (which has to be created in the /app/classes folder):

const mf = require('../classes/MemcachedFactory');

const nullHelper = '###NULL###';
/**
 * Helper class for cached values
 * 
 * @author Sven Hasselbach
 * @version 0.1
 */
class ValueHolder {

    /**
     * 
     * @param {string} key 
     *  unique identifier
     * @param {number} ttl
     *  time-to-live in seconds 
     * @param {function} code 
     *  code to execute to calculate the value
     */
    constructor(key, ttl, code) {
        this.key = key;
        this.code = code;
        this.ttl = ttl;
    }

    /**
     * loads the value from cache or 
     * computes it and stores it in the cache
     * 
     * @param {function} callback 
     * @returns Promise
     */
    async get(callback) {
        const { code, ttl, key } = this;
    
        // check if value is in cache...
        mf.getInstance().get(key, (error, value) => {
            if (error) {
                callback(error);
                return;
            }
            if (value != null) {
                console.debug(`Found '${key}' in cache.`);
                if (value === nullHelper) {
                    // result is "special", so let's return null
                    callback(error, null);
                } else {
                    console.log(value);
                    callback(error, JSON.parse(value));
                }
            } else {
                console.debug(`Computing '${key}' and adding to cache with ttl ${ttl}.`);

                // execute the computation
                code().then((result) => {
                    // check if result must be stored "special" or not
                    if (result === null) {
                        mf.getInstance().set(key, nullHelper, ttl);
                    } else {
                        mf.getInstance().set(key, JSON.stringify(result), ttl);
                    }   
                    callback(error, result);
                });
            }
        });
     }

}

module.exports = ValueHolder;

node.js, domino-db & Docker (6): Using memcached

7. November 2018 Posted by Sven Hasselbach

mem.js

I am using mem.js as client library for accessing memcached. To use it, the first thing to do is to add the requirement to your package.json:

npm install memjs --save

MemcachedFactory

Then we can create a simple helper class to have an abstraction layer between our code and the library itself.

1. Create a folder in /app named classes

2. Create a new file with the name MemcachedFactory.js

3. Add the following code:

const memjs = require('memjs');
/**
 * Helper class for using Memcache
 * 
 * @author Sven Hasselbach
 * @version 0.1
 */
class MemcachedFactory {

    constructor() {
      this.client = memjs.Client.create('127.0.0.1:11211');
    }

    /**
     * returns a single instance of the class
     */
    static getInstance() {
      if (this.instance == null) {
        this.instance = new MemcachedFactory();
      }
      return this.instance;
    }

    /**
     * stores a value in memcache
     * @param {string} key 
     *  the key used
     * @param {*} value
     *  the value to store
     * @param {number} ttl 
     *  time-to-live in seconds
     */
    set(key, value, ttl) {
      this.client.set(key, value, { expires: ttl }, err => {
        if (err) {
          console.log(err);
          throw err;
        }
      });
    }
    /**
     * gets a value from memcache
     * 
     * @param {string} key 
     *  the key used
     * @param {function} callback
     *  the callback containing the value
     */
    get(key, callback) {
      this.client.get(key, (err, value) => {
        if (err) {
          console.error(err);
          callback(err);
        }
        if (value == null) {
          callback(err, null);
        } else {
          callback(err, value.toString());
        }
      });
  }
}
module.exports = MemcachedFactory;

5. To use the class in our code, we have to add the requirement first:

const mf = require('../classes/MemcachedFactory');

6. Here is a small example how the class is used:

mf.getInstance().get(key, (error, value) => {
  if (error) {
    // handle error here
  }else{
    // we have a value
    console.log(`The value is ${value}`); 
  }
});

The getInstance method returns an instance of the class. Then the get method is used with the key to retreive, and a callback method which is called when the request to memcached is completed.

The set method allows to put a key to memcached, and with ttl we can define how long the key is valid and stored.

node.js, domino-db & Docker (5): memcached

5. November 2018 Posted by Sven Hasselbach

To use memcached in our Docker container, we have to modify the existing Dockerfile a little bit. First it is required to install memcached in the container itself, and then it is required to change the CMD command to start the service and our express application.

1. Create a folder /conf in the project folder

2. Create a file memcached.conf in this folder with the following content:

# Memory: 256 MB
-m 256

# Port to use
-p 11211

# User for the service
-u memcache

# Listening IP Adress 
-l 127.0.0.1

3. Create a script startup.sh in the newly created folder:

#!/bin/bash
service memcached start
npm start

4. Modify the Dockerfile to install memcached

# install memcached
RUN apt-get update
RUN apt-get install -y memcached

5. Copy the startup.sh script and the memcached.conf

COPY ./conf/memcached.conf /etc
COPY ./conf/startup.sh .
RUN chmod +x /usr/src/app/startup.sh

6. Change the CMD to use the startup.sh script

CMD [ "./startup.sh" ]

The reason for the startup script is that in Docker containers the services are disabled by default, so we need to start the service by our own. Also, only one CMD command is allowed, so we have to use a script.