| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- // Copyright 2015 The MemDB Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- // implied. See the License for the specific language governing
- // permissions and limitations under the License. See the AUTHORS file
- // for names of contributors.
- 'use strict';
- var _ = require('lodash');
- var util = require('util');
- var P = require('bluebird');
- var child_process = require('child_process');
- var uuid = require('node-uuid');
- // Add some usefull promise methods
- exports.extendPromise = function(P){
- // This is designed for large array
- // The original map with concurrency option does not release memory
- P.mapLimit = function(items, fn, limit){
- if(!limit){
- limit = 1000;
- }
- var groups = [];
- var group = [];
- items.forEach(function(item){
- group.push(item);
- if(group.length >= limit){
- groups.push(group);
- group = [];
- }
- });
- if(group.length > 0){
- groups.push(group);
- }
- var results = [];
- var promise = P.resolve();
- groups.forEach(function(group){
- promise = promise.then(function(){
- return P.map(group, fn)
- .then(function(ret){
- ret.forEach(function(item){
- results.push(item);
- });
- });
- });
- });
- return promise.thenReturn(results);
- };
- P.mapSeries = function(items, fn){
- var results = [];
- return P.each(items, function(item){
- return P.try(function(){
- return fn(item);
- })
- .then(function(ret){
- results.push(ret);
- });
- })
- .thenReturn(results);
- };
- };
- exports.uuid = function(){
- return uuid.v4();
- };
- exports.isEmpty = function(obj){
- for(var key in obj){
- return false;
- }
- return true;
- };
- exports.getObjPath = function(obj, path){
- var current = obj;
- path.split('.').forEach(function(field){
- if(!!current){
- current = current[field];
- }
- });
- return current;
- };
- exports.setObjPath = function(obj, path, value){
- if(typeof(obj) !== 'object'){
- throw new Error('not object');
- }
- var current = obj;
- var fields = path.split('.');
- var finalField = fields.pop();
- fields.forEach(function(field){
- if(!current.hasOwnProperty(field)){
- current[field] = {};
- }
- current = current[field];
- if(typeof(current) !== 'object'){
- throw new Error('field ' + path + ' exists and not a object');
- }
- });
- current[finalField] = value;
- };
- exports.deleteObjPath = function(obj, path){
- if(typeof(obj) !== 'object'){
- throw new Error('not object');
- }
- var current = obj;
- var fields = path.split('.');
- var finalField = fields.pop();
- fields.forEach(function(field){
- if(!!current){
- current = current[field];
- }
- });
- if(current !== undefined){
- delete current[finalField];
- }
- };
- exports.clone = function(obj){
- return JSON.parse(JSON.stringify(obj));
- };
- exports.isDict = function(obj){
- return typeof(obj) === 'object' && obj !== null && !Array.isArray(obj);
- };
- // escape '$' and '.' in field name
- // '$abc.def\\g' => '\\u0024abc\\u002edef\\\\g'
- exports.escapeField = function(str){
- return str.replace(/\\/g, '\\\\').replace(/\$/g, '\\u0024').replace(/\./g, '\\u002e');
- };
- exports.unescapeField = function(str){
- return str.replace(/\\u002e/g, '.').replace(/\\u0024/g, '$').replace(/\\\\/g, '\\');
- };
- // Async foreach for mongo's cursor
- exports.mongoForEach = function(itor, func){
- var deferred = P.defer();
- var next = function(err){
- if(err){
- return deferred.reject(err);
- }
- // async iterator with .next(cb)
- itor.next(function(err, value){
- if(err){
- return deferred.reject(err);
- }
- if(value === null){
- return deferred.resolve();
- }
- P.try(function(){
- return func(value);
- })
- .nodeify(next);
- });
- };
- next();
- return deferred.promise;
- };
- exports.remoteExec = function(ip, cmd, opts){
- ip = ip || '127.0.0.1';
- opts = opts || {};
- var user = opts.user || process.env.USER;
- var successCodes = opts.successCodes || [0];
- var child = null;
- // localhost with current user
- if((ip === '127.0.0.1' || ip.toLowerCase() === 'localhost') && user === process.env.USER){
- cmd = cmd.replace(/\\/g, '/');
- var pos = cmd.indexOf('node.exe');
- if (pos != -1) {
- cmd = cmd.substring(pos, cmd.length);
- }
- child = child_process.spawn('bash', ['-c', cmd]);
- }
- // run remote via ssh
- else{
- child = child_process.spawn('ssh', ['-o StrictHostKeyChecking=no', user + '@' + ip, 'bash -c \'' + cmd + '\'']);
- }
- var deferred = P.defer();
- var stdout = '', stderr = '';
- child.stdout.on('data', function(data){
- stdout += data;
- });
- child.stderr.on('data', function(data){
- stderr += data;
- });
- child.on('exit', function(code, signal){
- if(successCodes.indexOf(code) !== -1){
- deferred.resolve(stdout);
- }
- else{
- deferred.reject(new Error(util.format('remoteExec return code %s on %s@%s - %s\n%s', code, user, ip, cmd, stderr)));
- }
- });
- return deferred.promise;
- };
- exports.waitUntil = function(fn, checkInterval){
- if(!checkInterval){
- checkInterval = 100;
- }
- var deferred = P.defer();
- var check = function(){
- if(fn()){
- deferred.resolve();
- }
- else{
- setTimeout(check, checkInterval);
- }
- };
- check();
- return deferred.promise;
- };
- exports.rateCounter = function(opts){
- opts = opts || {};
- var perserveSeconds = opts.perserveSeconds || 3600;
- var sampleSeconds = opts.sampleSeconds || 5;
- var counts = {};
- var cleanInterval = null;
- var getCurrentSlot = function(){
- return Math.floor(Date.now() / 1000 / sampleSeconds);
- };
- var beginSlot = getCurrentSlot();
- var counter = {
- inc : function(){
- var slotNow = getCurrentSlot();
- if(!counts.hasOwnProperty(slotNow)){
- counts[slotNow] = 0;
- }
- counts[slotNow]++;
- },
- reset : function(){
- counts = {};
- beginSlot = getCurrentSlot();
- },
- clean : function(){
- var slotNow = getCurrentSlot();
- Object.keys(counts).forEach(function(slot){
- if(slot < slotNow - Math.floor(perserveSeconds / sampleSeconds)){
- delete counts[slot];
- }
- });
- },
- rate : function(lastSeconds){
- var slotNow = getCurrentSlot();
- var total = 0;
- var startSlot = slotNow - Math.floor(lastSeconds / sampleSeconds);
- if(startSlot < beginSlot){
- startSlot = beginSlot;
- }
- for(var slot = startSlot; slot < slotNow; slot++){
- total += counts[slot] || 0;
- }
- return total / ((slotNow - startSlot) * sampleSeconds);
- },
- stop : function(){
- clearInterval(cleanInterval);
- },
- counts : function(){
- return counts;
- }
- };
- cleanInterval = setInterval(function(){
- counter.clean();
- }, sampleSeconds * 1000);
- return counter;
- };
- exports.hrtimer = function(autoStart){
- var total = 0;
- var starttime = null;
- var timer = {
- start : function(){
- if(starttime){
- return;
- }
- starttime = process.hrtime();
- },
- stop : function(){
- if(!starttime){
- return;
- }
- var timedelta = process.hrtime(starttime);
- total += timedelta[0] * 1000 + timedelta[1] / 1000000;
- return total;
- },
- total : function(){
- return total; //in ms
- },
- };
- if(autoStart){
- timer.start();
- }
- return timer;
- };
- exports.timeCounter = function(){
- var counts = {};
- return {
- add : function(name, time){
- if(!counts.hasOwnProperty(name)){
- counts[name] = [0, 0, 0]; // total, count, average
- }
- var count = counts[name];
- count[0] += time;
- count[1]++;
- count[2] = count[0] / count[1];
- },
- reset : function(){
- counts = {};
- },
- getCounts : function(){
- return counts;
- },
- };
- };
- // trick v8 to not use hidden class
- // https://github.com/joyent/node/issues/25661
- exports.forceHashMap = function(){
- var obj = {k : 1};
- delete obj.k;
- return obj;
- };
|