UNPKG

scalra

Version:

node.js framework to prototype and scale rapidly

287 lines (224 loc) 7.45 kB
// // // sync.js // // Syncing mechanism between memory arrays and DB // // This SR component tries to allow the automatic syncing between memory arrays and DB, // once linked to a collection in DB, array elements will be stored to DB at intervals, // stored data can also be loaded from DB to array upon startup // // methods: // set(array, arr_name, config, onDone) // set an in-memory array to sync with a DB collection // load(arrays, config, onDone) // load data from DB to an in-memory array // // history // 2014-05-26 start // 2014-05-29 initial version // /* // init process (check if we need to connect/store to DB) // ex: {DB: 'chat', interval: 5, limit: 1000} if (typeof config === 'object' && typeof config.DB === 'string') { // name of the collection to store message in batch var clt_name = config.DB; // how many seconds to store to DB once var interval = config.interval || 5; // start to utilize a new DB collection SR.addCollection(clt_name, function (result) { // set message limit in each individual queue if (typeof config.limit === 'number') this.setLimit(config.limit); // TODO: load existing records from DB to memory (into 'queues') }); } */ SR.DB.useCollections([SR.Settings.DB_NAME_SYNC]); // names of the arrays to sync var l_names = {}; // // local methods // // store all pending arrays to DB var l_storeAll = function () { // go over each array for (var name in l_names) { l_store(l_names[name]); } } // store a particular record to DB var l_store = function (arr) { // extract collection name if (arr && typeof arr._name === 'string') { var size = arr.length - arr._index; if (size <= 0) return false; LOG.sys('try to store to array [' + arr._name + '], # of elements to store: ' + size, 'SR.Sync'); // TODO: wasteful of space? // NOTE: only new array entries will be stored to DB var elements = []; for (var i = arr._index; i < arr.length; i++) elements.push(arr[i]); // update index count (regardless of update success or not?) arr._index = arr.length; // TODO: find right way to store // store away // NOTE: $each is used here (try to hide it?) SR.DB.updateArray(SR.Settings.DB_NAME_SYNC, { name: arr._name }, { data: elements }, function (result) { LOG.sys('update array success: ' + result, 'SR.Sync'); }, function (result) { LOG.error('update array fail: ' + result, 'SR.Sync'); } ); return true; } else LOG.error('cannot store to DB, arr_name unavailable', 'SR.Sync'); return false; } /* // store a particular record to DB var l_store = function (arr, length, element) { // extract collection name if (arr && typeof arr._name === 'string') { // put timestamp to element stored // NOTE: put it earlier? element._time = new Date(); // store away SR.DB.updateArray(SR.Settings.DB_NAME_SYNC, {name: arr._name}, {data: element}, function (result) { //LOG.sys('update array success: ' + result, 'SR.Sync'); }, function (result) { LOG.error('update array fail: ' + result, 'SR.Sync'); } ); } else LOG.error('cannot store to DB, arr_name unavailable', 'SR.Sync'); } */ // custom event-notifying push // ref: http://stackoverflow.com/questions/5100376/how-to-watch-for-array-changes var l_push = function () { var now = new Date(); for (var i = 0, l = arguments.length; i < l; i++) { var item = UTIL.clone(arguments[i]); // put timestamp to element stored if (item && item !== null) { item._time = now; this[this.length] = item; } else LOG.error('push data is null', 'SR.Sync'); // NOTE: do not store right away, but rather periodically //l_store(this, this.length, this[this.length-1]); } return this.length; } // set an in-memory array to sync with a DB collection // config may contain: // {interval: 5, limit: 1000} // returns true/false on whether set is successful var l_set = function (arr, arr_name, config) { if (l_names.hasOwnProperty(arr_name)) { LOG.warn('array [' + arr_name + '] already sync with DB', 'SR.Sync'); //return UTIL.safeCall(onDone, false); return false; } // first override the array's functions arr.push = l_push; // store array's name arr._name = arr_name; // store last stored index arr._index = 0; // store to list of arrays l_names[arr_name] = arr; // NOTE: the DB creation may finish *after* this function returns // check if the array has been created inside DB, if not then create new record SR.DB.getArray(SR.Settings.DB_NAME_SYNC, function (result) { // NOTE: if array does not exist it'll return success with an empty array if (result === null || result.length === 0) { LOG.sys('array [' + arr_name + '] does not exist, create...', 'SR.Sync'); SR.DB.setData(SR.Settings.DB_NAME_SYNC, { name: arr_name, data: [] }, function (r) { LOG.sys('array [' + arr_name + '] create success', 'SR.Sync'); //UTIL.safeCall(onDone, true); }, function (r) { LOG.error('array [' + arr_name + '] create fail', 'SR.Sync'); //UTIL.safeCall(onDone, false); }); } else { LOG.sys('array [' + arr_name + '] exists', 'SR.Sync'); LOG.warn(result); //UTIL.safeCall(onDone, true); } }, function (result) { //UTIL.safeCall(onDone, false); }, { name: arr_name }); return true; // TODO: consider config's effect } // load data from DB to an in-memory array // returns a list of the names of arrays loaded var l_load = function (arrays, config, onDone) { var names = []; // load array's content from DB, if any SR.DB.getArray(SR.Settings.DB_NAME_SYNC, function (result) { // NOTE: if array does not exist it'll return success with an empty array if (result === null || result.length === 0) { LOG.warn('no arrays found, cannot load', 'SR.Sync'); } else { var array_limit = (typeof config.limit === 'number' ? config.limit : 0); LOG.sys('arrays exist, try to load (limit: ' + array_limit + ')', 'SR.Sync'); for (var i = 0; i < result.length; i++) { var record = result[i]; var arr = arrays[record.name] = []; // load data (clone is better?) var limit = (record.data.length > array_limit ? array_limit : record.data.length); var start = record.data.length - limit; for (var j = 0; j < limit; j++) arr[j] = UTIL.clone(record.data[start + j]); // override array's default behavior arr.push = l_push; // store array's name arr._name = record.name; // store last stored index (next index to store) arr._index = j; LOG.sys('[' + record.name + '] DB record length: ' + record.data.length + ' start: ' + start + ' actual limit: ' + limit + ' _index: ' + arr._index, 'SR.Sync'); l_names[record.name] = arr; names.push(record.name); } } UTIL.safeCall(onDone, names); }, function (result) { UTIL.safeCall(onDone, names); }, {}); } var l_timer = undefined; SR.Callback.onStart(function () { // init periodic storage to DB LOG.warn('init periodic syncing to DB for arrays at intervals (ms): ' + SR.Settings.TIMEOUT_SYNC_PERIOD * 1000, 'SR.Sync'); l_timer = setInterval(l_storeAll, SR.Settings.TIMEOUT_SYNC_PERIOD * 1000); }); SR.Callback.onStop(function () { // stop timer if (l_timer) { clearTimeout(l_timer); l_timer = undefined; } }); // exported functions exports.set = l_set; exports.load = l_load;