State Persistence

Common state persistence pattern with gstate.

  1. Call async functions to persist data into database.
  2. If the persist action successful, use state.set to update internal state.
  3. Call async function to read data from database.
  4. When the read action completed, use state.set to update internal state.
// persist user object
function insert_new_user(state, user_data){
    return db.collection("users").insert(user_data).then(user_id => {
        state.set(["users", user_id], user_data);
    });
}

// reload user object
function read_user(state, user_id){
    return db.collection("users").find({_id: user_id}).then(user_data => {
        state.set(["users", user_id], user_data);
    });
}

Besides the pattern, gstate provides a high optional persistence interface.

Persistence interface

const persist = require("gstate/persist");
const db = persist(adapter, options);

//Rebuild persistent state. 
await db.find(state); 

//Insert new object at path and update state
await db.insert(state, path, value);

//Update object at path and update state
await db.update(state, path, value);

//Delete object at path and update state
await db.remove(state, path);

Adapter is implementation for actual data storage.

function adapter(options){
    return {
        _find(query){},
        _insert(path, value){},
        _update(path, value){},
        _remove(path, value){},
        _sync(callback){} // callback = (op, path, value) => ();
    }
}

The sample Nedb adapter:

const Datastore = require("nedb");

function path_to_id(path) {
    return path.join(".");
}

class NedbAdapter {
    constructor(options = {}) {
        this._db = new Datastore(options);
    }

    _find(query) {
        return new Promise((ok, fail) => {
            this._db.find({}, (err, docs) => {
                if (err) return fail(err);
                const values = Array(docs.length);
                for (let i = 0; i < docs.length; i++) {
                    const path = docs[i]._id;
                    const v = docs[i].v;
                    values[i] = [path, v];
                }
                ok(values);
            });
        });
    }

    _insert(path, value) {
        path = path_to_id(path);
        return new Promise((ok, fail) => {
            this._db.insert(
                { _id: path, v: value },
                err => (err ? fail(err) : ok())
            );
        });
    }

    _update(path, value) {
        path = path_to_id(path);
        return new Promise((ok, fail) => {
            this._db.update(
                { _id: path },
                { $set: flatten("v", value) },
                (err, numAffected) =>
                    err || numAffected == 0 ? fail(err) : ok()
            );
        });
    }

    _remove(path) {
        path = path_to_id(path);
        return new Promise((ok, fail) => {
            this._db.remove(
                { _id: path },
                (err, numRemoved) => (err || numRemoved == 0 ? fail(err) : ok())
            );
        });
    }
}

module.exports = function(options) {
    return new NedbAdapter(options);
};

results matching ""

    No results matching ""