"use strict"; var Strings = require("./strings"), Err = require("./utils/error"), object = require("./utils/object"), async = require("async"); function write(replica, method, args) { if (replica.writeStrategy === "rollback") { replica.manager.transaction.set(function (err, trans) { if (err) { args.callback(err); } replica.collections.forEach(function (el) { trans[el][method](args.item); }); trans.commit(args.context, args.callback); }); } else { var fns = []; replica.collections.forEach(function (el) { fns.push(function (cb) { replica.manager[el][method](args.context, args.item, cb); }); }); async.parallel(fns, args.callback); } } /** * Constructor for the Replica class. * * Replica sets are used just like regular data collections. * Please see {@link Provider} for usage reference. * * Options: * * - `master` {string} - Defines the master collection in the replica set. * If omitted the first collection in the set is assumed to be the master. * - `readStrategy` {string} - Defines the read strategy for the replica set. * See {@link Replica#readStrategy}. * - `writeStrategy` {string} - Defines the write strategy for the replica set. * See {@link Replica#writeStrategy}. * * @class Represents a replica set. * @param {Manager} manager - An EntreeJS instance. * @param {string[]} colls - The names of the data collections participating in the replica set. * There must be at least two collections in the array. * @param {object=} opts - Additional options. */ function Replica(manager, colls, opts) { if (!Array.isArray(colls) || colls.length < 2) { throw new Err(Strings.ERR_COLLS_ARG); } if (!opts) { opts = {}; } /** * The EntreJS instance holding this replica set. */ this.manager = manager; /** * An array of the names of the data collections participating in the replica set. */ this.collections = colls; /** * Defines the master collection in the replica set. * If omitted the first collection in the set is assumed to be the master. */ this.master = opts.master || colls[0]; /** * Defines the read strategy for the replica set. Defaults to `master`. * * - `"master"` - Only the master collection is used for read operations (e.g. get, select). * - `"round-robin"` - All collections in the set are cycled for ever read request. * - `"feedback"` (not available yet) - Collections with lower latency are given higher priority. * * Read strategy allows for simple load and throughput balancing of back-end services. */ this.readStrategy = opts.readStrategy || "master"; /** * Defines the write strategy for the replica set. Defaults to `rollback`. * * - `"rollback"` - Attempts to recover the state of all collections in case any of them fails to write successfully. * - `"none"` - Errors on failures are returned but no actions are taken. */ this.writeStrategy = opts.writeStrategy || "rollback"; this._curr = 0; } Replica.prototype._next = function () { switch (this.readStrategy) { case "master": return this.master; case "round-robin": if (this._curr === this.collections.length) { this._curr = 0; } return this.collections[this._curr++]; case "feedback": // FUTURE: Implement feedback read strategy. throw new Error("Feedback strategy not implemented yet."); default: throw new Error("Invalid read strategy: " + this.readStrategy); } }; Replica.prototype.get = function () { var prov = this.manager[this._next()]; return prov.get.apply(prov, arguments); }; Replica.prototype.select = function () { // TODO: Have to wrap the cursor to handle bulk update and delete. var prov = this.manager[this._next()]; return prov.select.apply(prov, arguments); }; Replica.prototype.insert = function () { var args = object.validateArgs(arguments, "items"); if (args) { write(this, "insert", args); } }; Replica.prototype.upsert = function () { var args = object.validateArgs(arguments, "item"); if (args) { write(this, "upsert", args); } }; Replica.prototype.update = function () { var args = object.validateArgs(arguments, "item"); if (args) { write(this, "update", args); } }; Replica.prototype.delete = function () { var args = object.validateArgs(arguments, "itemOrID"); if (args) { write(this, "delete", args); } }; module.exports = Replica;