mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
profiler charts
This commit is contained in:
parent
9a2631dc09
commit
2e37788471
@ -193,46 +193,83 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
extractTimelineChart_meta: true,
|
extractTimelineChart_meta: true,
|
||||||
async extractTimelineChart({ jslid, formatterFunction, measures }) {
|
async extractTimelineChart({ jslid, timestampFunction, aggregateFunction, measures }) {
|
||||||
const formater = requirePluginFunction(formatterFunction);
|
const timestamp = requirePluginFunction(timestampFunction);
|
||||||
const datastore = new JsonLinesDatastore(getJslFileName(jslid), formater);
|
const aggregate = requirePluginFunction(aggregateFunction);
|
||||||
|
const datastore = new JsonLinesDatastore(getJslFileName(jslid));
|
||||||
let mints = null;
|
let mints = null;
|
||||||
let maxts = null;
|
let maxts = null;
|
||||||
// pass 1 - counts stats, time range
|
// pass 1 - counts stats, time range
|
||||||
await datastore.enumRows(row => {
|
await datastore.enumRows(row => {
|
||||||
if (!mints || row.ts < mints) mints = row.ts;
|
const ts = timestamp(row);
|
||||||
if (!maxts || row.ts > maxts) maxts = row.ts;
|
if (!mints || ts < mints) mints = ts;
|
||||||
|
if (!maxts || ts > maxts) maxts = ts;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
const minTime = new Date(mints).getTime();
|
const minTime = new Date(mints).getTime();
|
||||||
const maxTime = new Date(maxts).getTime();
|
const maxTime = new Date(maxts).getTime();
|
||||||
const duration = maxTime - minTime;
|
const duration = maxTime - minTime;
|
||||||
const STEPS = 100;
|
const STEPS = 100;
|
||||||
const step = duration / STEPS;
|
let stepCount = duration > 100 * 1000 ? STEPS : Math.round((maxTime - minTime) / 1000);
|
||||||
const labels = _.range(STEPS).map(i => new Date(minTime + step / 2 + step * i));
|
if (stepCount < 2) {
|
||||||
|
stepCount = 2;
|
||||||
|
}
|
||||||
|
const stepDuration = duration / stepCount;
|
||||||
|
const labels = _.range(stepCount).map(i => new Date(minTime + stepDuration / 2 + stepDuration * i));
|
||||||
|
|
||||||
const datasets = measures.map(m => ({
|
// const datasets = measures.map(m => ({
|
||||||
label: m.label,
|
// label: m.label,
|
||||||
data: Array(STEPS).fill(0),
|
// data: Array(stepCount).fill(0),
|
||||||
|
// }));
|
||||||
|
|
||||||
|
const mproc = measures.map(m => ({
|
||||||
|
...m,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const data = Array(stepCount)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => ({}));
|
||||||
|
|
||||||
// pass 2 - count measures
|
// pass 2 - count measures
|
||||||
await datastore.enumRows(row => {
|
await datastore.enumRows(row => {
|
||||||
if (!mints || row.ts < mints) mints = row.ts;
|
const ts = timestamp(row);
|
||||||
if (!maxts || row.ts > maxts) maxts = row.ts;
|
let part = Math.round((new Date(ts).getTime() - minTime) / stepDuration);
|
||||||
|
if (part < 0) part = 0;
|
||||||
for (let i = 0; i < measures.length; i++) {
|
if (part >= stepCount) part - stepCount - 1;
|
||||||
const part = Math.round((new Date(row.ts).getTime() - minTime) / step);
|
if (data[part]) {
|
||||||
datasets[i].data[part] += row[measures[i].field];
|
data[part] = aggregate(data[part], row, stepDuration);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
datastore._closeReader();
|
datastore._closeReader();
|
||||||
|
|
||||||
|
// const measureByField = _.fromPairs(measures.map((m, i) => [m.field, i]));
|
||||||
|
|
||||||
|
// for (let mindex = 0; mindex < measures.length; mindex++) {
|
||||||
|
// for (let stepIndex = 0; stepIndex < stepCount; stepIndex++) {
|
||||||
|
// const measure = measures[mindex];
|
||||||
|
// if (measure.perSecond) {
|
||||||
|
// datasets[mindex].data[stepIndex] /= stepDuration / 1000;
|
||||||
|
// }
|
||||||
|
// if (measure.perField) {
|
||||||
|
// datasets[mindex].data[stepIndex] /= datasets[measureByField[measure.perField]].data[stepIndex];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (let i = 0; i < measures.length; i++) {
|
||||||
|
// if (measures[i].hidden) {
|
||||||
|
// datasets[i] = null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
labels,
|
labels,
|
||||||
datasets,
|
datasets: mproc.map(m => ({
|
||||||
|
label: m.label,
|
||||||
|
data: data.map(d => d[m.field] || 0),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
3
packages/types/engines.d.ts
vendored
3
packages/types/engines.d.ts
vendored
@ -79,7 +79,8 @@ export interface EngineDriver {
|
|||||||
supportsServerSummary?: boolean;
|
supportsServerSummary?: boolean;
|
||||||
supportsDatabaseProfiler?: boolean;
|
supportsDatabaseProfiler?: boolean;
|
||||||
profilerFormatterFunction?: string;
|
profilerFormatterFunction?: string;
|
||||||
profilerChartFormatterFunction?: string;
|
profilerTimestampFunction?: string;
|
||||||
|
profilerChartAggregateFunction?: string;
|
||||||
profilerChartMeasures?: { label: string; field: string }[];
|
profilerChartMeasures?: { label: string; field: string }[];
|
||||||
isElectronOnly?: boolean;
|
isElectronOnly?: boolean;
|
||||||
supportedCreateDatabase?: boolean;
|
supportedCreateDatabase?: boolean;
|
||||||
|
@ -215,7 +215,8 @@
|
|||||||
props: {
|
props: {
|
||||||
jslid: `archive://${data.folderName}/${data.fileName}`,
|
jslid: `archive://${data.folderName}/${data.fileName}`,
|
||||||
profilerFormatterFunction: eng.profilerFormatterFunction,
|
profilerFormatterFunction: eng.profilerFormatterFunction,
|
||||||
profilerChartFormatterFunction: eng.profilerChartFormatterFunction,
|
profilerTimestampFunction: eng.profilerTimestampFunction,
|
||||||
|
profilerChartAggregateFunction: eng.profilerChartAggregateFunction,
|
||||||
profilerChartMeasures: eng.profilerChartMeasures,
|
profilerChartMeasures: eng.profilerChartMeasures,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -58,7 +58,8 @@
|
|||||||
export let database;
|
export let database;
|
||||||
export let jslid;
|
export let jslid;
|
||||||
export let profilerFormatterFunction;
|
export let profilerFormatterFunction;
|
||||||
export let profilerChartFormatterFunction;
|
export let profilerTimestampFunction;
|
||||||
|
export let profilerChartAggregateFunction;
|
||||||
export let profilerChartMeasures;
|
export let profilerChartMeasures;
|
||||||
|
|
||||||
let profiling = false;
|
let profiling = false;
|
||||||
@ -128,7 +129,8 @@
|
|||||||
|
|
||||||
const data = await apiCall('jsldata/extract-timeline-chart', {
|
const data = await apiCall('jsldata/extract-timeline-chart', {
|
||||||
jslid,
|
jslid,
|
||||||
formatterFunction: profilerChartFormatterFunction || engine.profilerChartFormatterFunction,
|
timestampFunction: profilerTimestampFunction || engine.profilerTimestampFunction,
|
||||||
|
aggregateFunction: profilerChartAggregateFunction || engine.profilerChartAggregateFunction,
|
||||||
measures: profilerChartMeasures || engine.profilerChartMeasures,
|
measures: profilerChartMeasures || engine.profilerChartMeasures,
|
||||||
});
|
});
|
||||||
chartData = {
|
chartData = {
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
const driver = require('./driver');
|
const driver = require('./driver');
|
||||||
const formatProfilerEntry = require('../frontend/formatProfilerEntry');
|
const {
|
||||||
const formatProfilerChartEntry = require('../frontend/formatProfilerChartEntry');
|
formatProfilerEntry,
|
||||||
|
extractProfileTimestamp,
|
||||||
|
aggregateProfileChartEntry,
|
||||||
|
} = require('../frontend/profilerFunctions');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
packageName: 'dbgate-plugin-mongo',
|
packageName: 'dbgate-plugin-mongo',
|
||||||
drivers: [driver],
|
drivers: [driver],
|
||||||
functions: {
|
functions: {
|
||||||
formatProfilerEntry,
|
formatProfilerEntry,
|
||||||
formatProfilerChartEntry,
|
extractProfileTimestamp,
|
||||||
|
aggregateProfileChartEntry,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -35,10 +35,15 @@ const driver = {
|
|||||||
supportsServerSummary: true,
|
supportsServerSummary: true,
|
||||||
supportsDatabaseProfiler: true,
|
supportsDatabaseProfiler: true,
|
||||||
profilerFormatterFunction: 'formatProfilerEntry@dbgate-plugin-mongo',
|
profilerFormatterFunction: 'formatProfilerEntry@dbgate-plugin-mongo',
|
||||||
profilerChartFormatterFunction: 'formatProfilerChartEntry@dbgate-plugin-mongo',
|
profilerTimestampFunction: 'extractProfileTimestamp@dbgate-plugin-mongo',
|
||||||
|
profilerChartAggregateFunction: 'aggregateProfileChartEntry@dbgate-plugin-mongo',
|
||||||
profilerChartMeasures: [
|
profilerChartMeasures: [
|
||||||
{ label: 'Req count', field: 'count' },
|
{ label: 'Req count/s', field: 'countPerSec' },
|
||||||
{ label: 'Duration', field: 'millis' },
|
{ label: 'Avg duration', field: 'avgDuration' },
|
||||||
|
|
||||||
|
// { label: 'Req count/s', field: 'countPerSec', perSecond: true },
|
||||||
|
// { field: 'countAll', hidden: true },
|
||||||
|
// { label: 'Avg duration', field: 'millis', perField: 'countAll' },
|
||||||
],
|
],
|
||||||
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
|
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
|
||||||
|
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
const _ = require('lodash');
|
|
||||||
const formatProfilerEntry = require('./formatProfilerEntry');
|
|
||||||
|
|
||||||
function formatProfilerChartEntry(obj) {
|
|
||||||
const fmt = formatProfilerEntry(obj);
|
|
||||||
|
|
||||||
return {
|
|
||||||
ts: fmt.ts,
|
|
||||||
millis: fmt.stats.millis,
|
|
||||||
count: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = formatProfilerChartEntry;
|
|
@ -1,12 +1,12 @@
|
|||||||
import driver from './driver';
|
import driver from './driver';
|
||||||
import formatProfilerEntry from './formatProfilerEntry';
|
import { formatProfilerEntry, extractProfileTimestamp, aggregateProfileChartEntry } from './profilerFunctions';
|
||||||
import formatProfilerChartEntry from './formatProfilerChartEntry';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
packageName: 'dbgate-plugin-mongo',
|
packageName: 'dbgate-plugin-mongo',
|
||||||
drivers: [driver],
|
drivers: [driver],
|
||||||
functions: {
|
functions: {
|
||||||
formatProfilerEntry,
|
formatProfilerEntry,
|
||||||
formatProfilerChartEntry,
|
extractProfileTimestamp,
|
||||||
|
aggregateProfileChartEntry,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -69,4 +69,33 @@ function formatProfilerEntry(obj) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = formatProfilerEntry;
|
function extractProfileTimestamp(obj) {
|
||||||
|
return obj.ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function aggregateProfileChartEntry(aggr, obj, stepDuration) {
|
||||||
|
// const fmt = formatProfilerEntry(obj);
|
||||||
|
|
||||||
|
const countAll = (aggr.countAll || 0) + 1;
|
||||||
|
const sumMillis = (aggr.sumMillis || 0) + obj.millis;
|
||||||
|
|
||||||
|
return {
|
||||||
|
countAll,
|
||||||
|
sumMillis,
|
||||||
|
countPerSec: (countAll / stepDuration) * 1000,
|
||||||
|
avgDuration: sumMillis / countAll,
|
||||||
|
};
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ts: fmt.ts,
|
||||||
|
// millis: fmt.stats.millis,
|
||||||
|
// countAll: 1,
|
||||||
|
// countPerSec: 1,
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
formatProfilerEntry,
|
||||||
|
extractProfileTimestamp,
|
||||||
|
aggregateProfileChartEntry,
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user