From d65b399bf1b8d4ef72fb12eec80ad0364858c0be Mon Sep 17 00:00:00 2001 From: itayw Date: Thu, 10 Apr 2014 10:05:48 +0300 Subject: [PATCH 1/6] #125 remove console.logs --- lib/dispatch/query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dispatch/query.js b/lib/dispatch/query.js index b3d1905f..f64c498d 100644 --- a/lib/dispatch/query.js +++ b/lib/dispatch/query.js @@ -711,7 +711,7 @@ manager.buildQueryPlan = function (query, callback) { } }); - if (true && plan.colQueries && Object.keys(plan.colQueries).length > 0) { + if (false && plan.colQueries && Object.keys(plan.colQueries).length > 0) { Object.keys(plan.colQueries).forEach(function (key) { var colQuery = plan.colQueries[key]; if (colQuery.query.length > 0) { From cb2fedaba934a9ddcad69daeb396a8703a64c4e3 Mon Sep 17 00:00:00 2001 From: itayw Date: Wed, 16 Apr 2014 12:30:52 +0300 Subject: [PATCH 2/6] #118 progress with benchmark. --- benchmark/base.bench.js | 26 +++++++++++++++++--------- benchmark/flows/beacon.large.spec.js | 4 ++-- benchmark/flows/beacon.spec.js | 2 +- benchmark/flows/metadata.spec.js | 2 +- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/benchmark/base.bench.js b/benchmark/base.bench.js index 7c7338f0..0245dc59 100644 --- a/benchmark/base.bench.js +++ b/benchmark/base.bench.js @@ -1,12 +1,17 @@ -var common = require('../lib/common/index'); -var benchrest = require('bench-rest'); +var + util = require('util'), + benchrest = require('bench-rest'), + + common = require('../lib/common/index'); + var flows = [ './flows/beacon.spec.js', - //'./flows/beacon.large.spec.js', + './flows/beacon.large.spec.js', './flows/metadata.spec.js' ]; global.JOOLA_ADDRESS = 'http://127.0.0.1:8080'; +const VERSION = '0.0.1'; var results = {}; results.benchmarkID = common.uuid(); @@ -17,13 +22,16 @@ results.flowCount = flows.length; var actual = 0; var joolaio = require('joola.io.sdk'); -joolaio.init({host: 'http://localhost:8080', APIToken: 'apitoken-root', ajax: true}, function (err) { +joolaio.init({host: JOOLA_ADDRESS, APIToken: 'apitoken-root', ajax: true}, function (err) { + if (err) + throw err; + joolaio.system.nodeDetails(function (err, details) { if (err) throw err; results.nodeDetails = details; - + results.version = VERSION; flows.forEach(function (flow) { console.log('Running Flow', flow); var flowModule = require(flow); @@ -36,10 +44,10 @@ joolaio.init({host: 'http://localhost:8080', APIToken: 'apitoken-root', ajax: tr console.error('Failed in %s with err: ', ctxName, err); }) .on('progress', function (stats, percent, concurrent, ips) { - console.log('Progress: %s complete', percent); + console.log('Progress: %s complete', percent, concurrent, ips); }) .on('end', function (stats, errorCount) { - console.log(stats); + //console.log(stats); stats.name = flowModule.name; results.flows[flowModule.name] = stats; @@ -51,11 +59,11 @@ joolaio.init({host: 'http://localhost:8080', APIToken: 'apitoken-root', ajax: tr }); function alldone() { - console.log(results); + console.log(util.inspect(results, {depth: null, colors: true})); joolaio.beacon.insert('benchmark', results, function (err) { if (err) throw err; process.exit(0); }); } -}); +}); \ No newline at end of file diff --git a/benchmark/flows/beacon.large.spec.js b/benchmark/flows/beacon.large.spec.js index a27d74e3..06116db5 100644 --- a/benchmark/flows/beacon.large.spec.js +++ b/benchmark/flows/beacon.large.spec.js @@ -1,9 +1,9 @@ module.exports = { - name: 'beacon.large', + name: 'beacon_large', runOptions: { limit: 1, // concurrent connections iterations: 1, // number of iterations to perform - progress: 1000 + progress: 5000 //prealloc: 10 }, flow: { diff --git a/benchmark/flows/beacon.spec.js b/benchmark/flows/beacon.spec.js index fc945ee6..2a2fbe27 100644 --- a/benchmark/flows/beacon.spec.js +++ b/benchmark/flows/beacon.spec.js @@ -3,7 +3,7 @@ module.exports = { runOptions: { limit: 1, // concurrent connections iterations: 10, // number of iterations to perform - progress: 1000 + progress: 5000 //prealloc: 10 }, flow: { diff --git a/benchmark/flows/metadata.spec.js b/benchmark/flows/metadata.spec.js index 99a4033b..f845b370 100644 --- a/benchmark/flows/metadata.spec.js +++ b/benchmark/flows/metadata.spec.js @@ -3,7 +3,7 @@ module.exports = { runOptions: { limit: 1, // concurrent connections iterations: 10, // number of iterations to perform - progress: 1000 + progress: 5000 //prealloc: 10 }, flow: { From 4fc986b179ba8e4138e62e8218d9adfa995744af Mon Sep 17 00:00:00 2001 From: itayw Date: Wed, 16 Apr 2014 15:06:03 +0300 Subject: [PATCH 3/6] #118 benchmark page added --- benchmark/base.bench.js | 6 +- benchmark/flows/beacon.spec.js | 6 +- benchmark/flows/metadata.spec.js | 6 +- lib/webserver/public/css/site.css | 9 +- lib/webserver/public/js/benchmark.js | 276 ++++++++++++++++ lib/webserver/public/js/index.js | 225 ++++++++++++++ lib/webserver/public/js/site.js | 223 ------------- lib/webserver/routes/index.js | 12 +- lib/webserver/views/benchmark.jade | 415 +++++++++++++++++++++++++ lib/webserver/views/index.jade | 26 -- lib/webserver/views/layout_footer.jade | 6 +- lib/webserver/views/layout_head.jade | 24 ++ 12 files changed, 963 insertions(+), 271 deletions(-) create mode 100644 lib/webserver/public/js/benchmark.js create mode 100644 lib/webserver/public/js/index.js create mode 100644 lib/webserver/views/benchmark.jade diff --git a/benchmark/base.bench.js b/benchmark/base.bench.js index 0245dc59..6b764008 100644 --- a/benchmark/base.bench.js +++ b/benchmark/base.bench.js @@ -6,7 +6,7 @@ var var flows = [ './flows/beacon.spec.js', - './flows/beacon.large.spec.js', + //'./flows/beacon.large.spec.js', './flows/metadata.spec.js' ]; @@ -16,7 +16,7 @@ const VERSION = '0.0.1'; var results = {}; results.benchmarkID = common.uuid(); results.timestamp = new Date(); -results.flows = {}; +results.flows =[]; results.flowCount = flows.length; var actual = 0; @@ -49,7 +49,7 @@ joolaio.init({host: JOOLA_ADDRESS, APIToken: 'apitoken-root', ajax: true}, funct .on('end', function (stats, errorCount) { //console.log(stats); stats.name = flowModule.name; - results.flows[flowModule.name] = stats; + results.flows.push(stats); if (++actual === results.flowCount) return alldone(); diff --git a/benchmark/flows/beacon.spec.js b/benchmark/flows/beacon.spec.js index 2a2fbe27..a7e6ec51 100644 --- a/benchmark/flows/beacon.spec.js +++ b/benchmark/flows/beacon.spec.js @@ -1,10 +1,10 @@ module.exports = { name: 'beacon', runOptions: { - limit: 1, // concurrent connections - iterations: 10, // number of iterations to perform + limit: 10, // concurrent connections + iterations: 100, // number of iterations to perform progress: 5000 - //prealloc: 10 + //prealloc: 10 }, flow: { before: [], // operations to do before anything diff --git a/benchmark/flows/metadata.spec.js b/benchmark/flows/metadata.spec.js index f845b370..3a90a4e6 100644 --- a/benchmark/flows/metadata.spec.js +++ b/benchmark/flows/metadata.spec.js @@ -1,10 +1,10 @@ module.exports = { name: 'metadata', runOptions: { - limit: 1, // concurrent connections - iterations: 10, // number of iterations to perform + limit: 10, // concurrent connections + iterations: 100, // number of iterations to perform progress: 5000 - //prealloc: 10 + //prealloc: 10 }, flow: { before: [], // operations to do before anything diff --git a/lib/webserver/public/css/site.css b/lib/webserver/public/css/site.css index ce23f5e6..4f47abad 100644 --- a/lib/webserver/public/css/site.css +++ b/lib/webserver/public/css/site.css @@ -221,12 +221,9 @@ div[jio-type="metric"] { } -#metricbox-2 { - padding-left: 15px; -} - -#metricbox-3 { - +#metricbox-2, +#metricbox-3, +#metricbox-4 { padding-left: 15px; } diff --git a/lib/webserver/public/js/benchmark.js b/lib/webserver/public/js/benchmark.js new file mode 100644 index 00000000..28d3ca5a --- /dev/null +++ b/lib/webserver/public/js/benchmark.js @@ -0,0 +1,276 @@ +var timeframe = 'last_30_days'; +var interval = 'day'; +var realtime = true; +var filter = []; + +joolaio.events.on('ready', function () { + var metric1Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: [ + { + key: 'bench-count', + aggregation: 'count', + dependsOn: 'benchmarkID' + } + ], + collection: 'benchmark' + }; + + var metric2Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: [ + { + key: 'flow-count', + aggregation: 'sum', + dependsOn: 'flowCount' + } + ], + collection: 'benchmark' + }; + + var metric3Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: [ + { + key: 'Test-count', + aggregation: 'sum', + dependsOn: 'flows.main.meter.count' + } + ], + collection: 'benchmark' + }; + + var metric4Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: [ + { + key: 'reqrate', + name: 'Avg. Request Time', + formula: { + dependsOn: [ + { + key: 'req-count', + aggregation: 'avg', + dependsOn: 'flows.main.meter.count' + }, + { + key: 'elapsed', + aggregation: 'avg', + dependsOn: 'flows.totalElapsed' + } + ], + run: 'function(count, elapsed){return elapsed/count}' + }, + decimals: 2, + suffix: 'ms' + } + ], + collection: 'benchmark' + }; + + var chart1Query = { + timeframe: timeframe, + interval: interval, + dimensions: [ + {key: 'timestamp'} + ], + metrics: [ + { + key: 'reqrate', + name: 'Avg. Request Time', + formula: { + dependsOn: [ + { + key: 'req-count', + aggregation: 'avg', + dependsOn: 'flows.main.meter.count' + }, + { + key: 'elapsed', + aggregation: 'avg', + dependsOn: 'flows.totalElapsed' + } + ], + run: 'function(count, elapsed){return elapsed/count}' + }, + decimals: 2, + suffix: 'ms' + } + ], + collection: 'benchmark' + }; + + var table1Query = { + timeframe: timeframe, + interval: interval, + dimensions: [ + {key: 'flows.name', name: 'Flow name', collection: 'benchmark'} + ], + metrics: [ + { + key: 'reqrate', + name: 'Avg. Request Time', + formula: { + dependsOn: [ + { + key: 'req-count', + aggregation: 'avg', + dependsOn: 'flows.main.meter.count' + }, + { + key: 'elapsed', + aggregation: 'avg', + dependsOn: 'flows.totalElapsed' + } + ], + run: 'function(count, elapsed){return elapsed/count}' + }, + decimals: 4, + suffix: 'ms' + } + ], + collection: 'benchmark' + }; + + setupMetricbox('#metricbox-1', 'Benchmark Tests', metric1Query); + setupMetricbox('#metricbox-2', 'Flow Executions', metric2Query); + setupMetricbox('#metricbox-3', 'Test Count', metric3Query); + setupMetricbox('#metricbox-4', 'Avg. Request time', metric4Query); + setupChart('#chart-1', chart1Query); + setupTable('#table-1', table1Query); + + function setupMetricbox(container, caption, query) { + if (filter) + query.filter = filter; + query.realtime = realtime; + $(container).Metric({force: true, caption: caption, query: query}); + } + + function setupChart(container, query) { + var metric = $('.metric.active').Metric(); + query.metrics = metric.options.query.metrics; + query.collection = metric.options.query.collection; + query.realtime = realtime; + $(container).Timeline({force: true, + chart: { + chart: { + type: 'column' + }, + plotOptions: { + column: { + color: 'rgba(87,173,104,0.8)', + pointWidth: 15 + } + } + }, + query: query}); + } + + function setupTable(container, query) { + if (filter) + query.filter = filter; + query.realtime = realtime; + $(container).Table({force: true, query: query}); + } + + $('.metric').on('click', function () { + $('.metric').removeClass('active'); + $(this).addClass('active'); + + setupChart('#chart-1', chart1Query); + }); +}); + +jQuery(document).ready(function () { + prettyPrint(); +}); + +$().ready(function () { + var editor = ace.edit("editor"); + editor.getSession().setMode("ace/mode/javascript"); + editor.renderer.setShowGutter(false); + + var editor2 = ace.edit("editor2"); + editor2.getSession().setMode("ace/mode/javascript"); + editor2.renderer.setShowGutter(false); + + var editor3 = ace.edit("editor3"); + editor3.getSession().setMode("ace/mode/javascript"); + editor3.renderer.setShowGutter(false); + + + $('#pushEvent').on('click', function () { + var collectionName = $('#collectionName').val(); + if (!collectionName) + return alert('Please enter a collection name'); + + var json = editor.getSession().getValue(); + try { + json = JSON.parse(json); + joolaio.beacon.insert(collectionName, json, function (err, result) { + if (err) + editor2.getSession().setValue(err); + + var text = JSON.stringify(result, null, 4); + editor2.getSession().setValue(text); + }); + } + catch (ex) { + console.log(ex); + console.log(ex.stack); + editor2.getSession().setValue(ex); + } + }); + + $('#visualize').on('click', function () { + var checked = $('.viz-check:checked').attr('data-attr'); + var collectionName = $('#viz-collectionName').val(); + if (!collectionName) + return alert('Please enter a collection name'); + + var json = editor3.getSession().getValue(); + var query = JSON.parse(json); + query.collection = collectionName; + var $container = $('#vizcontainer'); + + switch (checked) { + case 'sparkline': + $container.Sparkline({force: true, query: query}); + break; + case 'metricbox': + query.dimensions = []; + $container.Metric({force: true, query: query}); + break; + case 'table': + $container.Table({force: true, query: query}); + break; + case 'pie': + $container.Pie({force: true, query: query}); + break; + default: + break; + } + }); + + $('.viz-check').on('click', function () { + var $this = $(this); + var checked = $this.attr('data-attr'); + $('.viz-check').each(function (i, elem) { + var $elem = $(elem); + if ($elem.attr('data-attr') !== checked) + $elem.prop('checked', false); + }); + }); +}); diff --git a/lib/webserver/public/js/index.js b/lib/webserver/public/js/index.js new file mode 100644 index 00000000..772b1bf1 --- /dev/null +++ b/lib/webserver/public/js/index.js @@ -0,0 +1,225 @@ +var timeframe = 'last_hour'; +var interval = 'minute'; +var realtime = true; +var filter = []; +var userid = joolaio.common.uuid(); + +joolaio.events.on('ready', function () { + joolaio.beacon.insert('demo-visits', { + timestamp: null, + browser: $.ua.browser.name, + device: $.ua.device.name, + engine: $.ua.engine.name, + os: $.ua.os.name, + userid: userid, + ip: codehelper_ip.IP, + referrer: document.referrer, + visits: 1, + loadtime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart + }, {}, function (err, result) { + if (err) + throw err; + }); + + $(document).click(function (event) { + joolaio.beacon.insert('demo-clicks', { + timestamp: null, + browser: $.ua.browser.name, + device: $.ua.device.name, + engine: $.ua.engine.name, + os: $.ua.os.name, + userid: userid, + ip: codehelper_ip.IP, + referrer: document.referrer, + clicks: 1 + }, {}); + }); + + var iTimeoutMoves = 0; + var moves = 0; + $(document).mousemove(function (event) { + function pushMoves() { + if (moves > 0) { + var doc = { + timestamp: null, + browser: $.ua.browser.name, + device: $.ua.device.name, + engine: $.ua.engine.name, + os: $.ua.os.name, + userid: userid, + ip: codehelper_ip.IP, + referrer: document.referrer, + mousemoves: moves + }; + joolaio.beacon.insert('demo-mousemoves', doc, {}); + moves = 0; + } + } + + clearTimeout(iTimeoutMoves); + + moves++; + if (moves > 100) + pushMoves(); + iTimeoutMoves = setTimeout(pushMoves, 100); + }); +}); + +joolaio.events.on('ready', function () { + var metric1Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: ['mousemoves'], + collection: 'demo-mousemoves' + }; + + var metric2Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: ['visits'], + collection: 'demo-visits' + }; + + var metric3Query = { + timeframe: timeframe, + interval: interval, + filter: filter, + dimensions: [], + metrics: [ + {key: 'clicks'} + ], + collection: 'demo-clicks' + }; + + var chart1Query = { + timeframe: 'last_hour', + interval: 'minute', + dimensions: [ + {key: 'timestamp', collection: 'demo-mousemoves'} + ], + metrics: [ + {key: 'mousemoves', name: 'Mouse moves', collection: 'demo-mousemoves'} + ] + }; + + setupMetricbox('#metricbox-1', 'Mouse Moves', metric1Query); + setupMetricbox('#metricbox-2', 'Visits', metric2Query); + setupMetricbox('#metricbox-3', 'Clicks', metric3Query); + setupChart('#chart-1', chart1Query); + + function setupMetricbox(container, caption, query) { + if (filter) + query.filter = filter; + query.realtime = realtime; + $(container).Metric({force: true, caption: caption, query: query}); + } + + function setupChart(container, query) { + var metric = $('.metric.active').Metric(); + query.metrics = metric.options.query.metrics; + query.collection = metric.options.query.collection; + query.realtime = realtime; + $(container).Timeline({force: true, + chart: { + chart: { + type: 'column' + }, + plotOptions: { + column: { + color: 'rgba(87,173,104,0.8)', + pointWidth: 15 + } + } + }, + query: query}); + } + + $('.metric').on('click', function () { + $('.metric').removeClass('active'); + $(this).addClass('active'); + + setupChart('#chart-1', chart1Query); + }); +}); + +$().ready(function () { + var editor = ace.edit("editor"); + editor.getSession().setMode("ace/mode/javascript"); + editor.renderer.setShowGutter(false); + + var editor2 = ace.edit("editor2"); + editor2.getSession().setMode("ace/mode/javascript"); + editor2.renderer.setShowGutter(false); + + var editor3 = ace.edit("editor3"); + editor3.getSession().setMode("ace/mode/javascript"); + editor3.renderer.setShowGutter(false); + + + $('#pushEvent').on('click', function () { + var collectionName = $('#collectionName').val(); + if (!collectionName) + return alert('Please enter a collection name'); + + var json = editor.getSession().getValue(); + try { + json = JSON.parse(json); + joolaio.beacon.insert(collectionName, json, function (err, result) { + if (err) + editor2.getSession().setValue(err); + + var text = JSON.stringify(result, null, 4); + editor2.getSession().setValue(text); + }); + } + catch (ex) { + console.log(ex); + console.log(ex.stack); + editor2.getSession().setValue(ex); + } + }); + + $('#visualize').on('click', function () { + var checked = $('.viz-check:checked').attr('data-attr'); + var collectionName = $('#viz-collectionName').val(); + if (!collectionName) + return alert('Please enter a collection name'); + + var json = editor3.getSession().getValue(); + var query = JSON.parse(json); + query.collection = collectionName; + var $container = $('#vizcontainer'); + + switch (checked) { + case 'sparkline': + $container.Sparkline({force: true, query: query}); + break; + case 'metricbox': + query.dimensions = []; + $container.Metric({force: true, query: query}); + break; + case 'table': + $container.Table({force: true, query: query}); + break; + case 'pie': + $container.Pie({force: true, query: query}); + break; + default: + break; + } + }); + + $('.viz-check').on('click', function () { + var $this = $(this); + var checked = $this.attr('data-attr'); + $('.viz-check').each(function (i, elem) { + var $elem = $(elem); + if ($elem.attr('data-attr') !== checked) + $elem.prop('checked', false); + }); + }); +}); diff --git a/lib/webserver/public/js/site.js b/lib/webserver/public/js/site.js index d95705a3..2a16f280 100644 --- a/lib/webserver/public/js/site.js +++ b/lib/webserver/public/js/site.js @@ -1,230 +1,7 @@ -var timeframe = 'last_hour'; -var interval = 'minute'; -var realtime = true; -var filter = []; -var userid = joolaio.common.uuid(); - -joolaio.events.on('ready', function () { - joolaio.beacon.insert('demo-visits', { - timestamp: null, - browser: $.ua.browser.name, - device: $.ua.device.name, - engine: $.ua.engine.name, - os: $.ua.os.name, - userid: userid, - ip: codehelper_ip.IP, - referrer: document.referrer, - visits: 1, - loadtime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart - }, {}); - - $(document).click(function (event) { - joolaio.beacon.insert('demo-clicks', { - timestamp: null, - browser: $.ua.browser.name, - device: $.ua.device.name, - engine: $.ua.engine.name, - os: $.ua.os.name, - userid: userid, - ip: codehelper_ip.IP, - referrer: document.referrer, - clicks: 1 - }, {}); - }); - - var iTimeoutMoves = 0; - var moves = 0; - $(document).mousemove(function (event) { - function pushMoves() { - if (moves > 0) { - var doc = { - timestamp: null, - browser: $.ua.browser.name, - device: $.ua.device.name, - engine: $.ua.engine.name, - os: $.ua.os.name, - userid: userid, - ip: codehelper_ip.IP, - referrer: document.referrer, - mousemoves: moves - }; - joolaio.beacon.insert('demo-mousemoves', doc, {}); - moves = 0; - } - } - - clearTimeout(iTimeoutMoves); - - moves++; - if (moves > 100) - pushMoves(); - iTimeoutMoves = setTimeout(pushMoves, 100); - }); -}); - -joolaio.events.on('ready', function () { - var metric1Query = { - timeframe: timeframe, - interval: interval, - filter: filter, - dimensions: [], - metrics: ['mousemoves'], - collection: 'demo-mousemoves' - }; - - var metric2Query = { - timeframe: timeframe, - interval: interval, - filter: filter, - dimensions: [], - metrics: ['visits'], - collection: 'demo-visits' - }; - - var metric3Query = { - timeframe: timeframe, - interval: interval, - filter: filter, - dimensions: [], - metrics: [ - {key: 'clicks'} - ], - collection: 'demo-clicks' - }; - - var chart1Query = { - timeframe: 'last_hour', - interval: 'minute', - dimensions: [ - {key: 'timestamp', collection: 'demo-mousemoves'} - ], - metrics: [ - {key: 'mousemoves', name: 'Mouse moves', collection: 'demo-mousemoves'} - ] - }; - - setupMetricbox('#metricbox-1', 'Mouse Moves', metric1Query); - setupMetricbox('#metricbox-2', 'Visits', metric2Query); - setupMetricbox('#metricbox-3', 'Clicks', metric3Query); - setupChart('#chart-1', chart1Query); - - function setupMetricbox(container, caption, query) { - if (filter) - query.filter = filter; - query.realtime = realtime; - $(container).Metric({force: true, caption: caption, query: query}); - } - - function setupChart(container, query) { - var metric = $('.metric.active').Metric(); - query.metrics = metric.options.query.metrics; - query.collection = metric.options.query.collection; - query.realtime = realtime; - $(container).Timeline({force: true, - chart: { - chart: { - type: 'column' - }, - plotOptions: { - column: { - color: 'rgba(87,173,104,0.8)', - pointWidth: 15 - } - } - }, - query: query}); - } - - $('.metric').on('click', function () { - $('.metric').removeClass('active'); - $(this).addClass('active'); - - setupChart('#chart-1', chart1Query); - }); -}); - jQuery(document).ready(function () { prettyPrint(); }); -$().ready(function () { - var editor = ace.edit("editor"); - editor.getSession().setMode("ace/mode/javascript"); - editor.renderer.setShowGutter(false); - - var editor2 = ace.edit("editor2"); - editor2.getSession().setMode("ace/mode/javascript"); - editor2.renderer.setShowGutter(false); - - var editor3 = ace.edit("editor3"); - editor3.getSession().setMode("ace/mode/javascript"); - editor3.renderer.setShowGutter(false); - - - $('#pushEvent').on('click', function () { - var collectionName = $('#collectionName').val(); - if (!collectionName) - return alert('Please enter a collection name'); - - var json = editor.getSession().getValue(); - try { - json = JSON.parse(json); - joolaio.beacon.insert(collectionName, json, function (err, result) { - if (err) - editor2.getSession().setValue(err); - - var text = JSON.stringify(result, null, 4); - editor2.getSession().setValue(text); - }); - } - catch (ex) { - console.log(ex); - console.log(ex.stack); - editor2.getSession().setValue(ex); - } - }); - - $('#visualize').on('click', function () { - var checked = $('.viz-check:checked').attr('data-attr'); - var collectionName = $('#viz-collectionName').val(); - if (!collectionName) - return alert('Please enter a collection name'); - - var json = editor3.getSession().getValue(); - var query = JSON.parse(json); - query.collection = collectionName; - var $container = $('#vizcontainer'); - - switch (checked) { - case 'sparkline': - $container.Sparkline({force: true, query: query}); - break; - case 'metricbox': - query.dimensions = []; - $container.Metric({force: true, query: query}); - break; - case 'table': - $container.Table({force: true, query: query}); - break; - case 'pie': - $container.Pie({force: true, query: query}); - break; - default: - break; - } - }); - - $('.viz-check').on('click', function () { - var $this = $(this); - var checked = $this.attr('data-attr'); - $('.viz-check').each(function (i, elem) { - var $elem = $(elem); - if ($elem.attr('data-attr') !== checked) - $elem.prop('checked', false); - }); - }); -}); - $(document).on('click', '.dropdown-menu li', function (e) { e.stopPropagation(); }); \ No newline at end of file diff --git a/lib/webserver/routes/index.js b/lib/webserver/routes/index.js index f9fd235d..48b9a8a7 100644 --- a/lib/webserver/routes/index.js +++ b/lib/webserver/routes/index.js @@ -106,10 +106,6 @@ exports.responseSuccess = function (data, req, res) { } }; -exports.index = function (req, res) { - res.render('index', {version: joola.VERSION}); -}; - exports.route = function (req, res) { //check if we're stopped (emergency was called) if (global.stopped) @@ -278,8 +274,12 @@ exports.generateerror = function (req, res) { exports.setup = function (app) { //main entry point - app.get('/', this.index); - + app.get('/', function (req, res) { + res.render('index', {version: joola.VERSION, index: true}); + }); + app.get('/benchmark', function (req, res) { + res.render('benchmark', {version: joola.VERSION, benchmark: true}); + }); app.get('/ip', function (req, res) { res.send('var codehelper_ip= {IP: \'' + req.connection.remoteAddress + '\'}'); }); diff --git a/lib/webserver/views/benchmark.jade b/lib/webserver/views/benchmark.jade new file mode 100644 index 00000000..89b5d1bb --- /dev/null +++ b/lib/webserver/views/benchmark.jade @@ -0,0 +1,415 @@ +include layout_head + +.container + hr + .row + //.col-md-12 + .bs-docs-section(id="demo") + h1.page-header joola.io Benchmark + p.lead Benchmark performance results over past 30 days. + + .col-md-12 + .row + .col-md-3 + #metricbox-1.metric.active + .col-md-3 + #metricbox-2.metric + .col-md-3 + #metricbox-3.metric + .col-md-3 + #metricbox-4.metric + .row + .col-md-12 + #chart-1.chart + + .col-md-12 + #table-1.table + + p + //.row + .col-md-12 + p The demo responds to actions and events on this page. Everytime you move your mouse, visit the page or click on it, an event is pushed into joola.io and then visualized. + .col-md-6 + p When you move your mouse the following document is compiled and pushed to joola.io: + pre.prettyprint(lang="javascript"). + var doc = { + timestamp: null, + browser: 'Chrome', + os: 'Linux', + userid: 'demo user', + mousemoves: 1 + }; + + joolaio.beacon.insert('demo-mousemoves', doc); + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/pushing-data", role="button") Learn more about pushing data + .col-md-6 + p Drawing visualizations is as easy as describing what you would like to see. + pre.prettyprint(lang="javascript"). + var query = { + timeframe: 'last_hour', + interval: 'minute', + dimensions: ['timestamp'], + metrics: ['mousemoves'], + collection: 'demo-mousemoves' + }; + + $('#container').Timeline({query: query}); + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/wiki/analytics-and-visualization", role="button") Learn more about visualization + .row + .col-md-12 + .bs-docs-section(id="gettingstarted") + h1.page-header What is this benchmark? + p.lead An overview of joola.io, how to setup, configure and use. + p. + joola.io was designed by developers for developers. We did the heavy-lifting with all that is data and framework related, + this is what makes joola.io so easy and fun to play with.
+ The project is rich with documentation, examples and workshops. + p. + Below is a quick guide for getting started with your first visualization and some basic concepts used throughout. + .bs-callout.bs-callout-warning + h4 Assumptions + p. + Please note that for this getting started guide we'll be assuming localhost as your server and apitoken-root as your APIToken (default). +
Also, when referring to dependencies such as MongoDB, Redis, etc... we assume that these are installed on your localhost. + p. + For a complete setup and configuration guide, please refer to our wiki. + h2 Installation + p. + joola.io is written in JavaScript and runs using Node.JS, an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. + In addition we use additional components as part of the stack, all required to be installed prior to installing joola.io. +
Remember: we'll assume localhost is used throughout this page. If you wish to use another machine, please refer to the configuration section on the wiki. + .row + .col-md-12 + h3 Before installing joola.io + .col-md-3 + .logo3rd + a(href="http://nodejs.org") + img(src="img/nodejs-logo.png") + p.download-intro. + joola.io is written in JavaScript and runs using Node.JS, an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://nodejs.org/download/", role="button") Install Node.JS + .col-md-3 + .logo3rd + a(href="http://redis.io") + img(src="img/redis-logo.png") + p.download-intro. + Redis is an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. + joola.io uses Redis to store its configuration and metadata. + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://redis.io/download", role="button") Install Redis + .col-md-3 + .logo3rd + a(href="http://mongodb.org") + img(src="img/mongodb-logo.png") + p.download-intro. + Documents pushed into joola.io are stored in a cache layer which is currently based on MongoDB, an open-source document database, and the leading NoSQL database. In the future, we'll support additional providers such as Cassandra, RethinkDB and more. + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://www.mongodb.org/downloads", role="button") Install MongoDB + .col-md-3 + .logo3rd + a(href="http://www.rabbitmq.com") + img(src="img/rabbitmq-logo.png") + p.download-intro. + In order to provide a scalabale framework that can handle very high loads of data consumption, joola.io uses STOMP to communicate with leading Message Queues. + We have chosen RabbitMQ as the default MQ provider due to its simplicity and power. + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://www.rabbitmq.com/download.html", role="button") Install RabbitMQ + .row + .col-md-12 + h3 Installing joola.io + p. joola.io can be installed from either the NPM registry or directly from the GitHub repository. + .row + .col-md-6 + h4 Installing from NPM registry + pre.prettyprint(lang="bash"). + $ npm install -g joola.io #for global install + # OR + $ npm install joola.io + .col-md-6 + h4 Installing from GitHub repository + pre.prettyprint(lang="bash"). + $ git clone http://github.com/joola/joola.io/tarball/master . + # OR + $ npm install http://github.com/joola/joola.io/tarball/master + .row + .col-md-12 + h4 First time run + pre.prettyprint(lang="bash"). + # for global installations + $ joola.io + # for local installations + $ node node_modules/joola.io/joola.io.js + + 08:50:44.179Z WARN joola.io: Found an empty configuration store, building initial... + 08:50:44.360Z INFO joola.io: joola.io version 0.4.0 started [CH3mIJWhy]. + h2 Connecting to joola.io + p. + There are several ways to communicate with your joola.io node and they all eventually (bar the REPL) use joola.io's API endpoints to pass instructions and data.
+ Our main method of communication is using the SDK, it exposes a standard method to consume joola.io's API endpoints. + .row + .col-md-6 + h3 Using the SDK + p. + The SDK is a single Javascript file containing all that is needed to operate ALL aspects of joola.io. The SDK is not only used to control joola.io, but also responsible for visualizing data on screen. + pre.prettyprint(lang="javascript"). + var joolaio = require('joola.io.sdk'); + joolaio.init({ + host:'http://localhost:8080', APIToken: 'apitoken-root' + }, function(err){ + if (err) throw err; + console.log('joola.io ready, version', joolaio.VERSION); + //joola.io ready, version 0.4.0 + }); + .col-md-6 + h3 Using the CLI (Command Line Interface) + p. + The CLI is based on the SDK and offers an easy manner for communication with joola.io without needing to write lines of code. + In addition, the CLI offers a shorthand call of built-in joola.io functions, it will automatically add callback and print out to your calls. + pre.prettyprint(lang="bash"). + $ joola.io.cli -h host -u username -p password + + joola.io.cli (0.0.1) starting... + Connecting to joola.io @ http://localhost:8080... + + joola.io root@localhost:8080> joolaio.VERSION + '0.4.0' + joola.io root@localhost:8080> + .row + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/sdk", role="button") Learn more about the SDK + h2 Configuration + p. + joola.io has a wide range of configurable settings. Setting these options can be done manually with joola.io launch or during runtime. + Runtime configuration effect all nodes on the grid immediately and allows in-place scalability and security changes to be put in place. + .row + .col-md-6 + h3 Baseconfig.json + p. + joola.io's initial configuration is loaded from config/baseconfig.json. + By changing these values and calling $ joola.io --baseconfig, you can apply an initial configuration to your joola.io node. + pre.prettyprint(lang="bash"). + configuration/ + ├── interfaces/ + ├── authentcation/ + ├── store/ + │ ├── configuration/ + │ ├── cache/ + │ └── logger/ + ├── workspaces/ + │ ├── users/ + │ ├── roles/ + │ ├── collections/ + │ └── canvases/ + └── alerts + .col-md-6 + h3 Runtime configuration + p. + Runtime configuration allows for nodes to be updated without a service restart. By connecting to a single node, you are connected to the grid, so + issuing a config change will effect all at once. + pre.prettyprint(lang="bash"). + $ joola.io.cli -h host -u username -p password + + joola.io.cli (0.0.1) starting... + Connecting to joola.io @ http://localhost:8080... + joola.io> joolaio.config.get('store:logger:mongo') + { host: 'localhost', + password: null, + level: 'trace', + unique: false, + port: 27017, + user: null, + db: 'logger', + options: { capped: true, size: 10240000 } } + .row + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/wiki/configuration", role="button") Learn more about configuration + h2 Authentication + p. + joola.io is a secure, multi-tenant framework. This means that any action carried out requires a security context and users must authenticate before they + can communicate with the system. There are several authentication methods available. + .bs-callout.bs-callout-warning + h4 Security Note + p. + Please make sure you change the default collections, passwords, roles, users, etc... before making your node available publicly. + p To change the default root password: + pre.prettyprint(lang="javascript"). + joolaio.users.update('root', {username: 'root', _password: 'newpassword'})); + p Read more about security and authentication in our wiki. + .row + .col-md-12 + h3 API Tokens + p. + An API token is a simple string that is assosiciated with a user account. Accessing joola.io with this API token will create a security context for the user and allow them to access the system according to their permissions and filters. + strong HTML + pre.prettyprint(lang="html"). + <script src="http://localhost:8080/joola.io.min.js?APIToken=apitoken-root"> + strong Node.JS + pre.prettyprint(lang="javascript"). + var joolaio = require('joola.io.sdk'); + joolaio.init({ + host: 'http://localhost:8080', + APIToken: 'apitoken-root' + }, function(err, result) {}); + .row + .col-md-12 + h3 Server-side authentication + p. + API tokens are useful, but not very every scenario. On some, the admin wish to avoid users holding long-term tokens and instead wishes to issue a short-term, "single-use" token for accessing joola.io. + pre.prettyprint(lang="javascript"). + var joolaio = require('joola.io.sdk'); + joolaio.authenticate('workspace', 'username', 'password', function(err, token) { + //we're now logged in as root + console.log('we now have a token for the user', token); + }); + .row + .col-md-12 + h3 Command Line Interface (CLI) + p. + The CLI uses the SDK for communicating with a joola.io node, so you can apply the same authentication methods as used throughout the SDK. +
However, it does support a few switches that make it easier to use. + pre.prettyprint(lang="bash"). + $ joola.io.cli --help + # Shows a list of all possible commands and options + $ joola.io.cli -h "http://localhost:8080" -u root -p password + # Logged in as root on localhost:8080 + .row + .col-md-12 + h3 Single Sign On (SSO) + p. + Often admins already have their own userbase and would like to display data to signed users. In such cases, joola.io's internal user management is redundant and we should avoid duplicating user details. +
Therefore, when we want to apply SSO we impersonate the user by creating a token for it. The example below shows generating a token for a user, which will be allowed to see only items where the browser='Chrome'. + pre.prettyprint(lang="javascript"). + var user = { + username: 'demo user', + _roles: ['user'], + _filter: [ + ['browser', 'eq', 'Chrome'] + ] + }; + joolaio.users.generateToken('workspace', user, function(err, token) { + console.log('Here is our new token', token); + }); + .row + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/wiki/security-and-authentication", role="button") Learn more about authentication + .row + h2 Pushing data + p. + After the framework is setup (if you see the demo above, then it's fine), we can move on to push data into joola.io. + Data to be pushed is described as an array of JSON documents, where each document describes all details needed for later retrieval. + Collections are used to batch data into logical containers, they store valualble metadata needed when querying information. + Once data is pushed it's immediately available for query and visualization and pushing it cannot get easier. + h3 A quick demo + .row + .col-md-6 + p. + Describe your data as a JSON document and choose a collection to push to. +
When you're done click on the 'Push' button to see results on the right pane. + form(role="form") + .form-group + label Collection name + div.input-group + input.form-control(type="text" ,id="collectionName", placeholder="Enter a collection name") + span.input-group-btn + button.btn.btn-default(type="button", id="pushEvent") Push! + #editor.editor. + { + "timestamp": null, + "article": "Sample Analytics", + "browser": "Chrome", + "device": "Desktop", + "engine": "Webkit", + "os": "Linux", + "userid": "demo@joo.la", + "ip": "127.0.0.1", + "referrer": "http://joo.la", + "visits": 1, + "loadtime": 123 + } + .col-md-6 + p(style="padding-bottom:15px"). + Beacon transforms documents during the 'push' process. It adds time buckets and other helpers to allow faster querying and visualization. + Data is then crunched to several time resolutions, each allowing cached data to be purged after a certain period of time, thus reducing the overall disk usage and query time. + Following the push of data, you will see a transformed document below. + #editor2.editor. + Use the editor on the left to describe the data you want to push. + Results will show up here. + .row(style="margin-top:15px;") + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/wiki/pushing-data", role="button") Learn more about pushing data + h2 Visualization + p. + Now we get to the real deal, visualizing your data. It's simple and fun! We create a simple JSON to describe the query and choose a visualization type to plot it. +
There are several different visualization types to play with and the open nature of the framework makes it easy to write your own and add more visualizations. + h3 A quick demo + .row + .col-md-6 + p. + Describe the data you would like to visualize. Specify the timeframe, interval, dimensions and metrics participating. + You can also ask for realtime updates of the visualization. +
When you're done describing, don't forget to specify which collection to query and choose a visualization type. + Results will be shown on the right pane. + form(role="form") + .form-group + label Collection name + div.input-group + input.form-control(type="text" ,id="viz-collectionName", placeholder="Enter a collection name") + span.input-group-btn + button.btn.btn-default(type="button", id="visualize") Visualize! + button.btn.btn-default.dropdown-toggle(type="button", data-toggle="dropdown", id="visualize") + span.caret + ul.dropdown-menu + li + label.checkbox + input.viz-check(type="checkbox", data-attr="sparkline", checked="true") + | Sparkline + li + label.checkbox + input.viz-check(type="checkbox", data-attr="metricbox") + | Metric Box + li + label.checkbox + input.viz-check(type="checkbox", data-attr="table") + | Table + li + label.checkbox + input.viz-check(type="checkbox", data-attr="pie") + | Pie + #editor3.editor. + { + "timeframe": "last_hour", + "interval": "minute", + "dimensions": ["timestamp"], + "metrics": ["visits"] + } + .col-md-6 + #vizcontainer.active + .row(style="margin-top:15px;") + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/wiki/analytics-and-visualization", role="button") Learn more about visualizations + //.row + .col-md-12 + .bs-docs-section(id="examples") + h1.page-header Examples + p.lead All examples include needed HTML, CSS and JS as Pens on CodePen.io. + //.row + .col-md-6.codepen-container + p.codepen(data-height="268", data-theme-id="0", data-slug-hash="usxht", data-default-tab="result") + .col-md-6.codepen-container + p.codepen(data-height="268", data-theme-id="0", data-slug-hash="usxht", data-default-tab="html") + //.row + .col-md-6.codepen-container + p.codepen(data-height="268", data-theme-id="0", data-slug-hash="usxht", data-default-tab="css") + .col-md-6.codepen-container + p.codepen(data-height="268", data-theme-id="0", data-slug-hash="usxht", data-default-tab="js") + + //.row(style="margin-top:15px;") + p.center-block.text-center + a.btn.btn-lg.btn-outline(href="http://github.com/joola/joola.io/wiki/examples", role="button") Browse all examples + + +//script(async, src="//codepen.io/assets/embed/ei.js") +include layout_footer \ No newline at end of file diff --git a/lib/webserver/views/index.jade b/lib/webserver/views/index.jade index 001d350b..e96db140 100644 --- a/lib/webserver/views/index.jade +++ b/lib/webserver/views/index.jade @@ -1,30 +1,5 @@ include layout_head -nav.navbar.navbar-default(role="navigation") - .container - .navbar-header - button.navbar-toggle(type="button", data-toggle="collapse", data-target="#main-nav") - span.sr-only Toggle navigation - span.icon-bar - span.icon-bar - span.icon-bar - a.navbar-brand(href="#") joola.io - .collapse.navbar-collapse(id="main-nav") - ul.nav.navbar-nav - li - a(href="#demo") Demo - li - a(href="#gettingstarted") Getting started - //li - // a(href="#examples") Examples - li - a(href="http://github.com/joola/joola.io/wiki") Documentation - ul.nav.navbar-nav.navbar-right - li - a(href="http://blog.joola.io") Blog - li - a(href="http://github.com/joola/joola.io") joola.io - main.bs-docs-masthead(id="content", role="main") .container img(src="img/hero.png") @@ -53,7 +28,6 @@ main.bs-docs-masthead(id="content", role="main") .row .col-md-12 #chart-1.chart - p .row .col-md-12 diff --git a/lib/webserver/views/layout_footer.jade b/lib/webserver/views/layout_footer.jade index 02a19703..1ebfa17e 100644 --- a/lib/webserver/views/layout_footer.jade +++ b/lib/webserver/views/layout_footer.jade @@ -47,4 +47,8 @@ script(src="joola.io.js?APIToken=apitoken-reader") script(src="/ip") script(src="js/ua-parser.min.js") script(src="js/tablesort.min.js") -script(src="js/site.js") \ No newline at end of file +script(src="js/site.js") +- if (index === true) + script(src="js/index.js") +- else if (benchmark === true) + script(src="js/benchmark.js") \ No newline at end of file diff --git a/lib/webserver/views/layout_head.jade b/lib/webserver/views/layout_head.jade index eef71241..8f7a2bab 100644 --- a/lib/webserver/views/layout_head.jade +++ b/lib/webserver/views/layout_head.jade @@ -12,3 +12,27 @@ html(lang='en') link(rel="stylesheet", href="css/pretty.css") link(rel="stylesheet", href="css/site.css") body + nav.navbar.navbar-default(role="navigation") + .container + .navbar-header + button.navbar-toggle(type="button", data-toggle="collapse", data-target="#main-nav") + span.sr-only Toggle navigation + span.icon-bar + span.icon-bar + span.icon-bar + a.navbar-brand(href="/#") joola.io + .collapse.navbar-collapse(id="main-nav") + ul.nav.navbar-nav + li + a(href="/#demo") Demo + li + a(href="/#gettingstarted") Getting started + li + a(href="/benchmark") Benchmark + li + a(href="http://github.com/joola/joola.io/wiki") Documentation + ul.nav.navbar-nav.navbar-right + li + a(href="http://blog.joola.io") Blog + li + a(href="http://github.com/joola/joola.io") joola.io \ No newline at end of file From 868ca568610466b699281a1fc470459336ca57c2 Mon Sep 17 00:00:00 2001 From: itayw Date: Wed, 16 Apr 2014 22:16:44 +0300 Subject: [PATCH 4/6] #392 initial work on `n last items` --- lib/dispatch/query.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dispatch/query.js b/lib/dispatch/query.js index c4456d7f..5f902312 100644 --- a/lib/dispatch/query.js +++ b/lib/dispatch/query.js @@ -290,6 +290,7 @@ manager.parse = function (context, options, callback) { if (typeof query.timeframe.end === 'string') query.timeframe.end = new Date(query.timeframe.end); } + /* else query.timframe = { start: new Date(1970, 1, 1), @@ -298,6 +299,7 @@ manager.parse = function (context, options, callback) { query.timeframe.start.setMilliseconds(0); query.timeframe.end.setMilliseconds(999); + */ query._interval = query.interval; query.interval = manager.translateInterval(query.interval); @@ -599,6 +601,8 @@ manager.buildQueryPlan = function (query, callback) { if (!query.metrics) query.metrics = []; + console.log('q', query); + if (query.timeframe) { if (typeof query.timeframe.start === 'string') query.timeframe.start = new Date(query.timeframe.start); From 8d807deacc35864e22710fd2b6a06fd8cbe828f2 Mon Sep 17 00:00:00 2001 From: itayw Date: Thu, 17 Apr 2014 00:17:39 +0300 Subject: [PATCH 5/6] #118 changed benchmark viz to be last n items based --- lib/webserver/public/js/benchmark.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/webserver/public/js/benchmark.js b/lib/webserver/public/js/benchmark.js index 28d3ca5a..3ca644fa 100644 --- a/lib/webserver/public/js/benchmark.js +++ b/lib/webserver/public/js/benchmark.js @@ -1,4 +1,4 @@ -var timeframe = 'last_30_days'; +var timeframe = 'last_30_items'; var interval = 'day'; var realtime = true; var filter = []; @@ -83,9 +83,7 @@ joolaio.events.on('ready', function () { var chart1Query = { timeframe: timeframe, interval: interval, - dimensions: [ - {key: 'timestamp'} - ], + dimensions: ['benchmarkID'], metrics: [ { key: 'reqrate', @@ -154,7 +152,7 @@ joolaio.events.on('ready', function () { function setupMetricbox(container, caption, query) { if (filter) query.filter = filter; - query.realtime = realtime; + //query.realtime = realtime; $(container).Metric({force: true, caption: caption, query: query}); } @@ -162,7 +160,7 @@ joolaio.events.on('ready', function () { var metric = $('.metric.active').Metric(); query.metrics = metric.options.query.metrics; query.collection = metric.options.query.collection; - query.realtime = realtime; + //query.realtime = realtime; $(container).Timeline({force: true, chart: { chart: { @@ -181,7 +179,7 @@ joolaio.events.on('ready', function () { function setupTable(container, query) { if (filter) query.filter = filter; - query.realtime = realtime; + //query.realtime = realtime; $(container).Table({force: true, query: query}); } From 6522df4ef45aa77dca1efcf96d642ed5fe4a08cb Mon Sep 17 00:00:00 2001 From: itayw Date: Thu, 17 Apr 2014 00:20:53 +0300 Subject: [PATCH 6/6] #392 added partial support last_n_items query --- lib/dispatch/index.js | 52 +++++++++++--- lib/dispatch/query.js | 160 +++++++++++++++++++++++------------------- 2 files changed, 132 insertions(+), 80 deletions(-) diff --git a/lib/dispatch/index.js b/lib/dispatch/index.js index 9c765c3f..71799b6d 100644 --- a/lib/dispatch/index.js +++ b/lib/dispatch/index.js @@ -13,6 +13,7 @@ var joola = require('../joola.io'), + domain = require('domain'), util = require('util'), fs = require('fs'), path = require('path'), @@ -334,7 +335,19 @@ dispatch.processRequest = function (message, headers) { return l._dispatch.message === headers.destination.replace('/queue/joola.dispatch.', '').replace('.', ':'); }); listeners.forEach(function (listener) { - listener.run.apply(message, _params); + + var d = domain.create(); + d.on('error', function (err) { + joola.logger.warn(err, 'Failed to process dispatch request: ' + err); + _result = { + err: err, + message: null + }; + dispatch.fulfill(message, _result, headers); + }); + d.run(function () { + listener.run.apply(message, _params); + }); }); } catch (ex) { @@ -342,6 +355,7 @@ dispatch.processRequest = function (message, headers) { err: ex, message: null }; + dispatch.fulfill(message, _result, headers); } }); }; @@ -362,16 +376,34 @@ dispatch.processResponse = function (message, headers) { args.push(result.message[key]); }); } - else + else if (joola.common.typeof(result.err) === 'object') { + joola.logger.debug(result.err, 'Failed to process dispatch response: ' + result.err); + args.push(result.err); + } + else { console.log('Weird result', message); + } delete message.result; delete message.payload; args.push(message); if (listener[message.id]) { - listener[message.id].apply(listener, args); - delete listener[message.id]; + var d = domain.create(); + d.on('error', function (err) { + joola.logger.warn(err, 'Failed to process dispatch response: ' + err); + //TODO: The assumption here is that the same function that was called and failed will handle the error properly. this is a risky assumption, but it allows for proper error messages to be handled. + try { + listener[message.id].apply(listener, [err]); + } + catch (ex) { + //there's nothing we can do at this point. the request will timeout and die. + } + }); + d.run(function () { + listener[message.id].apply(listener, args); + delete listener[message.id]; + }); } } }); @@ -415,7 +447,8 @@ dispatch.request = function (token, channel, params, callback) { dispatch.fulfill = function (message, result, headers, callback) { var self = dispatch; - callback = callback || function(){}; + callback = callback || function () { + }; message.fulfilled = 1; message['fulfilled-by'] = joola.UID; @@ -430,7 +463,8 @@ dispatch.fulfill = function (message, result, headers, callback) { }; dispatch.on = function (channel, callback, next) { - callback = callback || function(){}; + callback = callback || function () { + }; var exists = _.find(dispatch.listeners, function (listener) { return listener._dispatch.message == channel && (listener._dispatch.cb ? listener._dispatch.cb.toString() : null) == (callback ? callback.toString() : null); @@ -454,7 +488,8 @@ dispatch.on = function (channel, callback, next) { }; dispatch.once = function (channel, callback, next) { - callback = callback || function(){}; + callback = callback || function () { + }; var exists = _.find(dispatch.listeners, function (listener) { return listener._dispatch.message == channel && (listener._dispatch.cb ? listener._dispatch.cb.toString() : null) == (callback ? callback.toString() : null); @@ -483,7 +518,8 @@ dispatch.once = function (channel, callback, next) { }; dispatch.emit = function (channel, message, callback) { - callback = callback || function(){}; + callback = callback || function () { + }; if (dispatch.publisher) { dispatch.publisher.publish(channel, JSON.stringify(message), callback); joola.logger.trace('[dispatch] emit on: ' + channel); diff --git a/lib/dispatch/query.js b/lib/dispatch/query.js index 5f902312..46be7388 100644 --- a/lib/dispatch/query.js +++ b/lib/dispatch/query.js @@ -273,33 +273,35 @@ manager.parse = function (context, options, callback) { if (typeof query.timeframe === 'string') query.timeframe = manager.translateTimeframe(query.timeframe, query.interval); - if (query.timeframe_force_end) - query.timeframe.end = new Date(query.timeframe_force_end); + if (query.timeframe && !query.timeframe.hasOwnProperty('last_n_items')) { + if (query.timeframe_force_end) + query.timeframe.end = new Date(query.timeframe_force_end); - if (query.timeframe_force_start) { - query.timeframe.start = new Date(query.timeframe_force_start); - } + if (query.timeframe_force_start) { + query.timeframe.start = new Date(query.timeframe_force_start); + } - if (!query.timeframe.start || !query.timeframe.end) - return setImmediate(function () { - return callback(new Error('Failed to translate timeframe provided into proper timeframe object')); - }); + if (!query.timeframe.start || !query.timeframe.end) + return setImmediate(function () { + return callback(new Error('Failed to translate timeframe provided into proper timeframe object')); + }); - if (typeof query.timeframe.start === 'string') - query.timeframe.start = new Date(query.timeframe.start); - if (typeof query.timeframe.end === 'string') - query.timeframe.end = new Date(query.timeframe.end); + if (typeof query.timeframe.start === 'string') + query.timeframe.start = new Date(query.timeframe.start); + if (typeof query.timeframe.end === 'string') + query.timeframe.end = new Date(query.timeframe.end); + + query.timeframe.start.setMilliseconds(0); + query.timeframe.end.setMilliseconds(999); + } } /* - else - query.timframe = { - start: new Date(1970, 1, 1), - end: new Date(2099, 1, 1) - }; - - query.timeframe.start.setMilliseconds(0); - query.timeframe.end.setMilliseconds(999); - */ + else + query.timframe = { + start: new Date(1970, 1, 1), + end: new Date(2099, 1, 1) + }; + */ query._interval = query.interval; query.interval = manager.translateInterval(query.interval); @@ -564,6 +566,13 @@ manager.translateTimeframe = function (timeframe, interval) { end: new Date(_enddate) }; } + + m = /last_(\d+)_items/.exec(timeframe); + if (m && m.length > 0) { + return { + last_n_items: parseInt(m[1], 10) + }; + } }; manager.translateInterval = function (interval) { @@ -595,21 +604,23 @@ manager.buildQueryPlan = function (query, callback) { var $match = {}; var $project = {}; var $group = {}; + var $limit; if (!query.dimensions) query.dimensions = []; if (!query.metrics) query.metrics = []; - console.log('q', query); - - if (query.timeframe) { + if (query.timeframe && !query.timeframe.hasOwnProperty('last_n_items')) { if (typeof query.timeframe.start === 'string') query.timeframe.start = new Date(query.timeframe.start); if (typeof query.timeframe.end === 'string') query.timeframe.end = new Date(query.timeframe.end); $match.timestamp = {$gte: query.timeframe.start, $lt: query.timeframe.end}; } + else if (query.timeframe && query.timeframe.hasOwnProperty('last_n_items')) { + $limit = {$limit: query.timeframe.last_n_items}; + } if (query.filter) { query.filter.forEach(function (f) { @@ -714,6 +725,10 @@ manager.buildQueryPlan = function (query, callback) { {$group: _$group} ]; } + + if ($limit) { + colQuery.query.push($limit); + } } else { var _$group2; @@ -736,30 +751,8 @@ manager.buildQueryPlan = function (query, callback) { } }); - if (false && plan.colQueries && Object.keys(plan.colQueries).length > 0) { - Object.keys(plan.colQueries).forEach(function (key) { - var colQuery = plan.colQueries[key]; - if (colQuery.query.length > 0) { - if (colQuery.query.length === 4) { - var _$match = ce.clone(colQuery.query[0].$match); - if (_$match.timestamp) { - _$match.timestamp.$gte = _$match.timestamp.$gte.toISOString(); - _$match.timestamp.$lt = _$match.timestamp.$lt.toISOString(); - } - console.log(new Date().toISOString(), '$match', _$match); - console.log('$unwind', colQuery.query[1]); - console.log('$sort', colQuery.query[2]); - console.log('$group', colQuery.query[3]); - } - else { - console.log('$match', colQuery.query[0]); - console.log('$sort', colQuery.query[1]); - console.log('$group', colQuery.query[2]); - console.log('$unwind', colQuery.query[3]); - console.log('$group2', colQuery.query[4]); - } - } - }); + if (true) { + console.log('plan', require('util').inspect(plan.colQueries, {depth: null, colors: true})); } plan.dimensions = query.dimensions; @@ -1092,6 +1085,22 @@ manager.formatResults = function (results, callback) { } results.documents = _documents; } + else if (!timestampDimension && query.timeframe.hasOwnProperty('last_n_items')) { + //console.log('qq', query); + _documents = results.documents; + if (!itr && query.timeframe && query.timeframe.hasOwnProperty('last_n_items')) { + //we need to fill a simple integer range. + /* + for (var i = results.documents.length; i < query.timeframe.last_n_items; i++) { + _templateItem = joola.common.extend({}, templateItem); + if (query.dimensions && query.dimensions.length > 0) { + _templateItem.values[query.dimensions[0].key] = null; + _templateItem.fvalues[query.dimensions[0].key] = null; + } + _documents.unshift(ce.clone(_templateItem)); + }*/ + } + } } else if (results.documents.length === 0 && timestampDimension) { interval = query.interval.replace('timebucket.', ''); @@ -1099,11 +1108,12 @@ manager.formatResults = function (results, callback) { interval = 'day'; if (!query.timeframe) { query.timeframe = {}; - query.timeframe.start = results.documents[results.documents.length - 1].values.timestamp; - query.timeframe.end = results.documents[0].values.timestamp; + if (results.documents.length > 0) { + query.timeframe.start = results.documents[results.documents.length - 1].values.timestamp; + query.timeframe.end = results.documents[0].values.timestamp; + } } - itr = moment.twix(query.timeframe.start, query.timeframe.end).iterate(interval); _documents = []; if (results.documents.length > 0) { Object.keys(results.documents[0].values).forEach(function (key) { @@ -1124,24 +1134,28 @@ manager.formatResults = function (results, callback) { }); } - while (itr.hasNext()) { - _d = new Date(itr.next()._d.getTime()); - if (interval === 'day') - exists = checkExists(timestampDimension, results.documents, _d, true); - else - exists = checkExists(timestampDimension, results.documents, _d); - /* - exists = _.find(results.documents, function (document) { - return document.values[timestampDimension.key].getTime() === _d.getTime(); - });*/ - - if (exists) - _documents.push(exists); - else { - _templateItem = joola.common.extend({}, templateItem); - _templateItem.values[timestampDimension.key] = new Date(_d); - _templateItem.fvalues[timestampDimension.key] = new Date(_d.getTime()); - _documents.push(ce.clone(_templateItem)); + if (query.timeframe && query.timeframe.start && query.timeframe.end) { + itr = moment.twix(query.timeframe.start, query.timeframe.end).iterate(interval); + + while (itr.hasNext()) { + _d = new Date(itr.next()._d.getTime()); + if (interval === 'day') + exists = checkExists(timestampDimension, results.documents, _d, true); + else + exists = checkExists(timestampDimension, results.documents, _d); + /* + exists = _.find(results.documents, function (document) { + return document.values[timestampDimension.key].getTime() === _d.getTime(); + });*/ + + if (exists) + _documents.push(exists); + else { + _templateItem = joola.common.extend({}, templateItem); + _templateItem.values[timestampDimension.key] = new Date(_d); + _templateItem.fvalues[timestampDimension.key] = new Date(_d.getTime()); + _documents.push(ce.clone(_templateItem)); + } } } results.documents = _documents; @@ -1179,7 +1193,7 @@ manager.formatResults = function (results, callback) { results.cost = results.queryplan.query.cost; results.resultCount = results.documents.length; - if (results.queryplan.query.timeframe) { + if (results.queryplan.query.timeframe && results.queryplan.query.timeframe.start && results.queryplan.query.timeframe.end) { results.queryplan.query.timeframe.start = new Date(results.queryplan.query.timeframe.start).toISOString(); results.queryplan.query.timeframe.end = new Date(results.queryplan.query.timeframe.end).toISOString(); } @@ -1315,7 +1329,9 @@ exports.fetch = { else return router.responseError(new router.ErrorTemplate('Failed to route action [' + 'fetch' + ']: ' + (typeof(err) === 'object' ? err.message : err)), req, res); - lastQueryEndDate = new Date(result.query.timeframe.end); + if (result.query && result.query.timeframe && result.query.timeframe.end) { + lastQueryEndDate = new Date(result.query.timeframe.end); + } timestampDimension = _.find(result.dimensions, function (d) { return d.datatype == 'date'; });