mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-29 03:53:53 -06:00
Highlighted long running queries on the dashboards. Fixes #1975
This commit is contained in:
parent
d90472014d
commit
ef67409d61
Binary file not shown.
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 156 KiB |
@ -154,6 +154,9 @@ the graphs on the *Dashboard* tab:
|
||||
:alt: Preferences dialog dashboard display options
|
||||
:align: center
|
||||
|
||||
* Set the warning and alert threshold value to highlight the long-running
|
||||
queries on the dashboard.
|
||||
|
||||
* When the *Show activity?* switch is set to *True*, activity tables will be
|
||||
displayed on dashboards.
|
||||
|
||||
|
@ -9,6 +9,7 @@ This release contains a number of bug fixes and new features since the release o
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #1975 <https://redmine.postgresql.org/issues/1975>`_ - Highlighted long running queries on the dashboards.
|
||||
| `Issue #3893 <https://redmine.postgresql.org/issues/3893>`_ - Added support for Reassign/Drop Owned for login roles.
|
||||
| `Issue #3920 <https://redmine.postgresql.org/issues/3920>`_ - Do not block the query editor window when running a query.
|
||||
| `Issue #6559 <https://redmine.postgresql.org/issues/6559>`_ - Added option to provide maximum width of the column when 'Resize by data?’ option in the preferences is set to True.
|
||||
|
@ -8,6 +8,7 @@
|
||||
##########################################################################
|
||||
|
||||
"""A blueprint module implementing the dashboard frame."""
|
||||
import math
|
||||
from functools import wraps
|
||||
from flask import render_template, url_for, Response, g, request
|
||||
from flask_babelex import gettext
|
||||
@ -150,6 +151,15 @@ class DashboardModule(PgAdminModule):
|
||||
'details')
|
||||
)
|
||||
|
||||
self.long_running_query_threshold = self.dashboard_preference.register(
|
||||
'display', 'long_running_query_threshold',
|
||||
gettext('Long running query thresholds'), 'threshold',
|
||||
None, category_label=PREF_LABEL_DISPLAY,
|
||||
help_str=gettext('Set the warning and alert threshold value to '
|
||||
'highlight the long-running queries on the '
|
||||
'dashboard.')
|
||||
)
|
||||
|
||||
def get_exposed_url_endpoints(self):
|
||||
"""
|
||||
Returns:
|
||||
@ -300,13 +310,14 @@ def index(sid=None, did=None):
|
||||
)
|
||||
|
||||
|
||||
def get_data(sid, did, template):
|
||||
def get_data(sid, did, template, check_long_running_query=False):
|
||||
"""
|
||||
Generic function to get server stats based on an SQL template
|
||||
Args:
|
||||
sid: The server ID
|
||||
did: The database ID
|
||||
template: The SQL template name
|
||||
check_long_running_query:
|
||||
|
||||
Returns:
|
||||
|
||||
@ -324,12 +335,47 @@ def get_data(sid, did, template):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
# Check the long running query status and set the row type.
|
||||
if check_long_running_query:
|
||||
get_long_running_query_status(res['rows'])
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
|
||||
def get_long_running_query_status(activities):
|
||||
"""
|
||||
This function is used to check the long running query and set the
|
||||
row type to highlight the row color accordingly
|
||||
"""
|
||||
dash_preference = Preferences.module('dashboards')
|
||||
long_running_query_threshold = \
|
||||
dash_preference.preference('long_running_query_threshold').get()
|
||||
|
||||
if long_running_query_threshold is not None:
|
||||
long_running_query_threshold = long_running_query_threshold.split('|')
|
||||
|
||||
warning_value = float(long_running_query_threshold[0]) \
|
||||
if long_running_query_threshold[0] != '' else math.inf
|
||||
alert_value = float(long_running_query_threshold[1]) \
|
||||
if long_running_query_threshold[1] != '' else math.inf
|
||||
|
||||
for row in activities:
|
||||
row['row_type'] = None
|
||||
|
||||
# We care for only those queries which are in active state.
|
||||
if row['state'] != 'active':
|
||||
continue
|
||||
|
||||
active_since = float(row['active_since'])
|
||||
if active_since > warning_value:
|
||||
row['row_type'] = 'warning'
|
||||
if active_since > alert_value:
|
||||
row['row_type'] = 'alert'
|
||||
|
||||
|
||||
@blueprint.route('/dashboard_stats',
|
||||
endpoint='dashboard_stats')
|
||||
@blueprint.route('/dashboard_stats/<int:sid>',
|
||||
@ -376,7 +422,7 @@ def activity(sid=None, did=None):
|
||||
:param sid: server id
|
||||
:return:
|
||||
"""
|
||||
return get_data(sid, did, 'activity.sql')
|
||||
return get_data(sid, did, 'activity.sql', True)
|
||||
|
||||
|
||||
@blueprint.route('/locks/', endpoint='locks')
|
||||
|
@ -437,12 +437,31 @@ define('pgadmin.dashboard', [
|
||||
|
||||
var data = new Data();
|
||||
|
||||
var HighlightedRow = Backgrid.Row.extend({
|
||||
render: function() {
|
||||
Backgrid.Row.prototype.render.call(this);
|
||||
var row_type = this.model.get('row_type');
|
||||
|
||||
if (_.isUndefined(row_type) || _.isNull(row_type)) {
|
||||
this.$el.removeClass('alert');
|
||||
this.$el.removeClass('warning');
|
||||
} else if (row_type === 'warning') {
|
||||
this.$el.addClass('warning');
|
||||
} else if (row_type === 'alert') {
|
||||
this.$el.addClass('alert');
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
// Set up the grid
|
||||
var grid = new Backgrid.Grid({
|
||||
emptyText: gettext('No data found'),
|
||||
columns: columns,
|
||||
collection: data,
|
||||
className: 'backgrid presentation table table-bordered table-noouter-border table-hover',
|
||||
row: HighlightedRow
|
||||
});
|
||||
|
||||
// Render the grid
|
||||
|
@ -12,7 +12,8 @@ SELECT
|
||||
query,
|
||||
pg_catalog.to_char(state_change, 'YYYY-MM-DD HH24:MI:SS TZ') AS state_change,
|
||||
pg_catalog.to_char(query_start, 'YYYY-MM-DD HH24:MI:SS TZ') AS query_start,
|
||||
backend_type
|
||||
backend_type,
|
||||
CASE WHEN state = 'active' THEN ROUND((extract(epoch from now() - query_start) / 60)::numeric, 2) ELSE 0 END AS active_since
|
||||
FROM
|
||||
pg_catalog.pg_stat_activity
|
||||
{% if did %}WHERE
|
||||
|
@ -11,7 +11,8 @@ SELECT
|
||||
pg_catalog.pg_blocking_pids(pid) AS blocking_pids,
|
||||
query,
|
||||
pg_catalog.to_char(state_change, 'YYYY-MM-DD HH24:MI:SS TZ') AS state_change,
|
||||
pg_catalog.to_char(query_start, 'YYYY-MM-DD HH24:MI:SS TZ') AS query_start
|
||||
pg_catalog.to_char(query_start, 'YYYY-MM-DD HH24:MI:SS TZ') AS query_start,
|
||||
CASE WHEN state = 'active' THEN ROUND((extract(epoch from now() - query_start) / 60)::numeric, 2) ELSE 0 END AS active_since
|
||||
FROM
|
||||
pg_catalog.pg_stat_activity
|
||||
{% if did %}WHERE
|
||||
|
@ -10,7 +10,8 @@ SELECT
|
||||
CASE WHEN waiting THEN '{{ _('yes') }}' ELSE '{{ _('no') }}' END AS waiting,
|
||||
query,
|
||||
pg_catalog.to_char(state_change, 'YYYY-MM-DD HH24:MI:SS TZ') AS state_change,
|
||||
pg_catalog.to_char(query_start, 'YYYY-MM-DD HH24:MI:SS TZ') AS query_start
|
||||
pg_catalog.to_char(query_start, 'YYYY-MM-DD HH24:MI:SS TZ') AS query_start,
|
||||
CASE WHEN state = 'active' THEN ROUND((extract(epoch from now() - query_start) / 60)::numeric, 2) ELSE 0 END AS active_since
|
||||
FROM
|
||||
pg_catalog.pg_stat_activity
|
||||
{% if did %}WHERE
|
||||
|
@ -309,6 +309,11 @@ define('pgadmin.preferences', [
|
||||
return 'radioModern';
|
||||
case 'selectFile':
|
||||
return 'binary-paths-grid';
|
||||
case 'threshold':
|
||||
p.warning_label = gettext('Warning');
|
||||
p.alert_label = gettext('Alert');
|
||||
p.unit = gettext('(in minutes)');
|
||||
return 'threshold';
|
||||
default:
|
||||
if (console && console.warn) {
|
||||
// Warning for developer only.
|
||||
|
@ -3584,5 +3584,61 @@ define([
|
||||
},
|
||||
});
|
||||
|
||||
Backform.ThresholdControl = Backform.Control.extend({
|
||||
template: _.template([
|
||||
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
||||
'<div class="<%=Backform.controlContainerClassName%>">',
|
||||
' <span class="control-label pg-el-sm-2 pg-el-12"><%=warning_label%></span>',
|
||||
' <input type="text" id="warning_threshold" class="pg-el-sm-2" value="<%=warning_value%>" />',
|
||||
' <span class="control-label pg-el-sm-1 pg-el-12"><%=alert_label%></span>',
|
||||
' <input type="text" id="alert_threshold" class="pg-el-sm-2" value="<%=alert_value%>" />',
|
||||
' <span class="control-label pg-el-sm-3 pg-el-12"><%=unit%></span>',
|
||||
' <% if (helpMessage && helpMessage.length) { %>',
|
||||
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
||||
' <% } %>',
|
||||
'</div>',
|
||||
].join('\n')),
|
||||
|
||||
events: {
|
||||
'change input#warning_threshold': 'onChange',
|
||||
'change input#alert_threshold': 'onChange',
|
||||
},
|
||||
initialize: function() {
|
||||
Backform.Control.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var field = _.defaults(this.field.toJSON(), this.defaults),
|
||||
attributes = this.model.toJSON(),
|
||||
threshold_val = [];
|
||||
|
||||
if (!_.isUndefined(this.model.get('value')) && !_.isNull(this.model.get('value'))){
|
||||
threshold_val = this.model.get('value').split('|');
|
||||
}
|
||||
|
||||
var data = _.extend(field, {
|
||||
attributes: attributes,
|
||||
'warning_value': threshold_val.length > 0 ? threshold_val[0] : '',
|
||||
'alert_value': threshold_val.length > 1 ? threshold_val[1] : ''
|
||||
});
|
||||
|
||||
this.$el.html(this.template(data));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
onChange: function() {
|
||||
// Get the value from raw jquery and concat it using |
|
||||
// and set the value
|
||||
var warning_threshold = $('input#warning_threshold').val(),
|
||||
alert_threshold = $('input#alert_threshold').val();
|
||||
|
||||
var threshold_val = warning_threshold + '|' + alert_threshold;
|
||||
this.model.set(this.field.get('name'), threshold_val, {
|
||||
silent: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Backform;
|
||||
});
|
||||
|
@ -339,6 +339,14 @@ table.backgrid {
|
||||
border-bottom-color: $color-gray-light !important;
|
||||
}
|
||||
}
|
||||
|
||||
tr.warning {
|
||||
background-color: $color-warning !important;
|
||||
}
|
||||
|
||||
tr.alert {
|
||||
background-color: $color-danger-light !important;
|
||||
}
|
||||
}
|
||||
|
||||
table tr th button {
|
||||
|
@ -437,7 +437,7 @@ class Preferences(object):
|
||||
assert _type in (
|
||||
'boolean', 'integer', 'numeric', 'date', 'datetime',
|
||||
'options', 'multiline', 'switch', 'node', 'text', 'radioModern',
|
||||
'keyboardshortcut', 'select2', 'selectFile',
|
||||
'keyboardshortcut', 'select2', 'selectFile', 'threshold'
|
||||
), "Type cannot be found in the defined list!"
|
||||
|
||||
(cat['preferences'])[name] = res = _Preference(
|
||||
|
Loading…
Reference in New Issue
Block a user