diff --git a/packages/api/src/controllers/jsldata.js b/packages/api/src/controllers/jsldata.js
index dbace5a25..697a561d3 100644
--- a/packages/api/src/controllers/jsldata.js
+++ b/packages/api/src/controllers/jsldata.js
@@ -102,11 +102,12 @@ module.exports = {
async ensureDatastore(jslid, formatterFunction) {
const rowFormatter = requirePluginFunction(formatterFunction);
- let datastore = this.datastores[jslid];
+ const dskey = `${jslid}||${formatterFunction}`;
+ let datastore = this.datastores[dskey];
if (!datastore) {
datastore = new JsonLinesDatastore(getJslFileName(jslid), rowFormatter);
// datastore = new DatastoreProxy(getJslFileName(jslid));
- this.datastores[jslid] = datastore;
+ this.datastores[dskey] = datastore;
}
return datastore;
},
@@ -190,4 +191,48 @@ module.exports = {
await fs.promises.writeFile(getJslFileName(jslid), text);
return true;
},
+
+ extractTimelineChart_meta: true,
+ async extractTimelineChart({ jslid, formatterFunction, measures }) {
+ const formater = requirePluginFunction(formatterFunction);
+ const datastore = new JsonLinesDatastore(getJslFileName(jslid), formater);
+ let mints = null;
+ let maxts = null;
+ // pass 1 - counts stats, time range
+ await datastore.enumRows(row => {
+ if (!mints || row.ts < mints) mints = row.ts;
+ if (!maxts || row.ts > maxts) maxts = row.ts;
+ return true;
+ });
+ const minTime = new Date(mints).getTime();
+ const maxTime = new Date(maxts).getTime();
+ const duration = maxTime - minTime;
+ const STEPS = 100;
+ const step = duration / STEPS;
+ const labels = _.range(STEPS).map(i => new Date(minTime + step / 2 + step * i));
+
+ const datasets = measures.map(m => ({
+ label: m.label,
+ data: Array(STEPS).fill(0),
+ }));
+
+ // pass 2 - count measures
+ await datastore.enumRows(row => {
+ if (!mints || row.ts < mints) mints = row.ts;
+ if (!maxts || row.ts > maxts) maxts = row.ts;
+
+ for (let i = 0; i < measures.length; i++) {
+ const part = Math.round((new Date(row.ts).getTime() - minTime) / step);
+ datasets[i].data[part] += row[measures[i].field];
+ }
+ return true;
+ });
+
+ datastore._closeReader();
+
+ return {
+ labels,
+ datasets,
+ };
+ },
};
diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts
index 1976e9d3b..dae480ed6 100644
--- a/packages/types/engines.d.ts
+++ b/packages/types/engines.d.ts
@@ -79,6 +79,8 @@ export interface EngineDriver {
supportsServerSummary?: boolean;
supportsDatabaseProfiler?: boolean;
profilerFormatterFunction?: string;
+ profilerChartFormatterFunction?: string;
+ profilerChartMeasures?: { label: string; field: string }[];
isElectronOnly?: boolean;
supportedCreateDatabase?: boolean;
showConnectionField?: (field: string, values: any) => boolean;
diff --git a/packages/web/src/appobj/ArchiveFileAppObject.svelte b/packages/web/src/appobj/ArchiveFileAppObject.svelte
index 6eb04de32..629782d52 100644
--- a/packages/web/src/appobj/ArchiveFileAppObject.svelte
+++ b/packages/web/src/appobj/ArchiveFileAppObject.svelte
@@ -214,7 +214,9 @@
tabComponent: 'ProfilerTab',
props: {
jslid: `archive://${data.folderName}/${data.fileName}`,
- formatterFunction: eng.profilerFormatterFunction,
+ profilerFormatterFunction: eng.profilerFormatterFunction,
+ profilerChartFormatterFunction: eng.profilerChartFormatterFunction,
+ profilerChartMeasures: eng.profilerChartMeasures,
},
});
},
diff --git a/packages/web/src/tabs/ProfilerTab.svelte b/packages/web/src/tabs/ProfilerTab.svelte
index 5f2b7caf1..33ae37bc0 100644
--- a/packages/web/src/tabs/ProfilerTab.svelte
+++ b/packages/web/src/tabs/ProfilerTab.svelte
@@ -1,6 +1,4 @@
{#if jslid}
-
+
+
+
+
+
+ {#if isLoadingChart}
+
+ {:else}
+
+ {/if}
+
+
{:else}
{/if}
-
diff --git a/plugins/dbgate-plugin-mongo/src/backend/index.js b/plugins/dbgate-plugin-mongo/src/backend/index.js
index 0f8fbd10c..7b6e0f5d9 100644
--- a/plugins/dbgate-plugin-mongo/src/backend/index.js
+++ b/plugins/dbgate-plugin-mongo/src/backend/index.js
@@ -1,10 +1,12 @@
const driver = require('./driver');
const formatProfilerEntry = require('../frontend/formatProfilerEntry');
+const formatProfilerChartEntry = require('../frontend/formatProfilerChartEntry');
module.exports = {
packageName: 'dbgate-plugin-mongo',
drivers: [driver],
functions: {
formatProfilerEntry,
+ formatProfilerChartEntry,
},
};
diff --git a/plugins/dbgate-plugin-mongo/src/frontend/driver.js b/plugins/dbgate-plugin-mongo/src/frontend/driver.js
index 53887eca6..ab8787c20 100644
--- a/plugins/dbgate-plugin-mongo/src/frontend/driver.js
+++ b/plugins/dbgate-plugin-mongo/src/frontend/driver.js
@@ -35,6 +35,11 @@ const driver = {
supportsServerSummary: true,
supportsDatabaseProfiler: true,
profilerFormatterFunction: 'formatProfilerEntry@dbgate-plugin-mongo',
+ profilerChartFormatterFunction: 'formatProfilerChartEntry@dbgate-plugin-mongo',
+ profilerChartMeasures: [
+ { label: 'Req count', field: 'count' },
+ { label: 'Duration', field: 'millis' },
+ ],
databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname',
getQuerySplitterOptions: () => mongoSplitterOptions,
diff --git a/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerChartEntry.js b/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerChartEntry.js
new file mode 100644
index 000000000..8ee5a27a4
--- /dev/null
+++ b/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerChartEntry.js
@@ -0,0 +1,14 @@
+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;
diff --git a/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerEntry.js b/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerEntry.js
index cb57e8b3a..9b4e4cedb 100644
--- a/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerEntry.js
+++ b/plugins/dbgate-plugin-mongo/src/frontend/formatProfilerEntry.js
@@ -2,6 +2,7 @@ const _ = require('lodash');
function formatProfilerEntry(obj) {
const ts = obj.ts;
+ const stats = { millis: obj.millis };
let op = obj.op;
let doc;
let query;
@@ -64,6 +65,7 @@ function formatProfilerEntry(obj) {
doc,
query,
ext,
+ stats,
};
}
diff --git a/plugins/dbgate-plugin-mongo/src/frontend/index.js b/plugins/dbgate-plugin-mongo/src/frontend/index.js
index fbe5cc8d8..c68b8daa5 100644
--- a/plugins/dbgate-plugin-mongo/src/frontend/index.js
+++ b/plugins/dbgate-plugin-mongo/src/frontend/index.js
@@ -1,10 +1,12 @@
import driver from './driver';
import formatProfilerEntry from './formatProfilerEntry';
+import formatProfilerChartEntry from './formatProfilerChartEntry';
export default {
packageName: 'dbgate-plugin-mongo',
drivers: [driver],
functions: {
formatProfilerEntry,
+ formatProfilerChartEntry,
},
};