Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-45230: Qserv Web Dashboard crashes when trying to render the very long queries #867

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/www/qserv/css/QservCzarMySQLQueries.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ table#fwk-czar-mysql-queries > thead > tr > th.sticky {
}
table#fwk-czar-mysql-queries tbody th,
table#fwk-czar-mysql-queries tbody td {
vertical-align:middle;
vertical-align:top;
}
table#fwk-czar-mysql-queries pre {
padding: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/www/qserv/css/QservWorkerMySQLQueries.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ table#fwk-worker-mysql-queries > thead > tr > th.sticky {
}
table#fwk-worker-mysql-queries tbody th,
table#fwk-worker-mysql-queries tbody td {
vertical-align:middle;
vertical-align:top;
}
table#fwk-worker-mysql-queries pre {
padding: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/www/qserv/css/QservWorkerQueries.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ table#fwk-qserv-queries caption {
}
table#fwk-qserv-queries tbody th,
table#fwk-qserv-queries tbody td {
vertical-align:middle;
vertical-align:top;
}
table#fwk-qserv-queries pre {
padding: 0;
Expand Down
4 changes: 4 additions & 0 deletions src/www/qserv/css/StatusQueryInspector.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
height:20px;
margin-top: 7px;
}
#fwk-status-query-info tbody > tr > td > a.download-query {
height:20px;
margin-top: 7px;
}
#fwk-status-query-info tbody > tr > td > pre.query_toggler {
color:#4d4dff;
}
Expand Down
14 changes: 13 additions & 1 deletion src/www/qserv/js/Common.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,18 @@ function(sqlFormatter,
static RestAPIVersion = 35;
static query2text(query, expanded) {
if (expanded) {
return sqlFormatter.format(query, Common._sqlFormatterConfig);
if (query.length > Common._max_expanded_length) {
return sqlFormatter.format(
"************* ATTENTION **************;" +
"*Query has been truncated at " + Common._max_expanded_length + " bytes since it is too long*;" +
"*Click the download button to see the full text of the query*;" +
"********************************;" +
";" +
query.substring(0, Common._max_expanded_length) + "...",
Common._sqlFormatterConfig);
} else {
return sqlFormatter.format(query, Common._sqlFormatterConfig);
}
} else if (query.length > Common._max_compact_length) {
return query.substring(0, Common._max_compact_length) + "...";
} else {
Expand All @@ -18,6 +29,7 @@ function(sqlFormatter,
}
static _sqlFormatterConfig = {"language":"mysql", "uppercase:":true, "indent":" "};
static _max_compact_length = 120;
static _max_expanded_length = 4096;
static _ivals = [
{value: 2, name: '2 sec'},
{value: 5, name: '5 sec'},
Expand Down
10 changes: 10 additions & 0 deletions src/www/qserv/js/QservCzarMySQLQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function(CSSLoader,
// queries between updates.
this._id2query = {}; // Store query text for each identifier. The dictionary gets
// updated at each refresh of the page.
this._id2url = {}; // Store URL to the query blob for each identifier
}
fwk_app_on_show() {
console.log('show: ' + this.fwk_app_name);
Expand Down Expand Up @@ -70,6 +71,7 @@ function(CSSLoader,
<th class="sticky" style="text-align:right;">Time</th>
<th class="sticky" style="text-align:right;">State</th>
<th class="sticky" style="text-align:center;"><i class="bi bi-clipboard-fill"></i></th>
<th class="sticky" style="text-align:center;"><i class="bi bi-download"></i></th>
<th class="sticky">Query</th>
</tr>
</thead>
Expand Down Expand Up @@ -132,20 +134,25 @@ function(CSSLoader,
}
_display(queries) {
const queryCopyTitle = "Click to copy the query text to the clipboard.";
const queryDownloadTitle = "Click to download the query text to your computer.";
const COL_Id = 0, COL_Command = 4, COL_Time = 5, COL_State = 6, COL_Info = 7;
let tbody = this._table().children('tbody');
if (_.isEmpty(queries.columns)) {
tbody.html('');
return;
}
this._id2query = {};
for (let id in this._id2url) {
URL.revokeObjectURL(this._id2url[id]);
}
let html = '';
for (let i in queries.rows) {
let row = queries.rows[i];
if (row[COL_Command] !== 'Query') continue;
let queryId = row[COL_Id];
let query = row[COL_Info];
this._id2query[queryId] = query;
this._id2url[queryId] = URL.createObjectURL(new Blob([query], {type: "text/plain"}));
const expanded = (queryId in this._queryId2Expanded) && this._queryId2Expanded[queryId];
const queryToggleTitle = "Click to toggle query formatting.";
const queryStyle = "color:#4d4dff;";
Expand All @@ -157,6 +164,9 @@ function(CSSLoader,
<td style="text-align:center; padding-top:0; padding-bottom:0">
<button class="btn btn-outline-dark btn-sm copy-query" style="height:20px; margin:0px;" title="${queryCopyTitle}"></button>
</td>
<td style="text-align:center; padding-top:0; padding-bottom:0">
<a class="btn btn-outline-dark btn-sm" style="height:20px; margin:0px;" title="${queryDownloadTitle}" href="${this._id2url[queryId]}" download></a>
</td>
<td class="query_toggler" title="${queryToggleTitle}"><pre class="query" style="${queryStyle}">` + this._query2text(queryId, expanded) + `<pre></td>
</tr>`;
}
Expand Down
49 changes: 30 additions & 19 deletions src/www/qserv/js/QservWorkerMySQLQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ function(CSSLoader,

constructor(name) {
super(name);
this._mySqlThreadId2Expanded = {}; // Store 'true' to allow persistent state for the expanded
this._mysqlThreadId2Expanded = {}; // Store 'true' to allow persistent state for the expanded
// queries between updates.
this._mySqlThreaId2query = {}; // Store query text for each identifier. The dictionary gets
this._mysqlThreadId2query = {}; // Store query text for each identifier. The dictionary gets
// updated at each refresh of the page.
this._mysqlThreadId2url = {}; // Store URL to the query blob for each identifier
}
fwk_app_on_show() {
console.log('show: ' + this.fwk_app_name);
Expand Down Expand Up @@ -101,6 +102,7 @@ function(CSSLoader,
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</tr>
<tr>
<th class="sticky" style="text-align:right;">QID</th>
Expand All @@ -115,6 +117,7 @@ function(CSSLoader,
<th class="sticky" style="text-align:right;">Command</th>
<th class="sticky" style="text-align:right;">State</th>
<th class="sticky" style="text-align:center;"><i class="bi bi-clipboard-fill"></i></th>
<th class="sticky" style="text-align:center;"><i class="bi bi-download"></i></th>
<th class="sticky">Query</th>
</tr>
</thead>
Expand Down Expand Up @@ -217,14 +220,18 @@ function(CSSLoader,
_display(status) {
const queryInspectTitle = "Click to see detailed info (progress, messages, etc.) on the query.";
const queryCopyTitle = "Click to copy the query text to the clipboard.";
const queryDownloadTitle = "Click to download the query text to your computer.";
const COL_Id = 0, COL_Command = 4, COL_Time = 5, COL_State = 6, COL_Info = 7;
const desiredQueryCommand = this._query_command();
let tbody = this._table().children('tbody');
if (_.isEmpty(status.queries.columns)) {
tbody.html('');
return;
}
this._mySqlThreaId2query = {};
this._mysqlThreadId2query = {};
for (let id in this._mysqlThreadId2url) {
URL.revokeObjectURL(this._mysqlThreadId2url[id]);
}
let numQueriesTotal = 0;
let numQueriesDisplayed = 0;
let html = '';
Expand All @@ -234,10 +241,11 @@ function(CSSLoader,
let row = status.queries.rows[i];
const thisQueryCommand = row[COL_Command];
if ((desiredQueryCommand !== '') && (thisQueryCommand !== desiredQueryCommand)) continue;
let mySqlThreadId = row[COL_Id];
let mysqlThreadId = row[COL_Id];
let query = row[COL_Info];
this._mySqlThreaId2query[mySqlThreadId] = query;
const expanded = (mySqlThreadId in this._mySqlThreadId2Expanded) && this._mySqlThreadId2Expanded[mySqlThreadId];
this._mysqlThreadId2query[mysqlThreadId] = query;
this._mysqlThreadId2url[mysqlThreadId] = URL.createObjectURL(new Blob([query], {type: "text/plain"}));
const expanded = (mysqlThreadId in this._mysqlThreadId2Expanded) && this._mysqlThreadId2Expanded[mysqlThreadId];
const queryToggleTitle = "Click to toggle query formatting.";
const queryStyle = "color:#4d4dff;";
// Task context (if any)
Expand All @@ -247,8 +255,8 @@ function(CSSLoader,
let subChunkId = '';
let templateId = '';
let state = '';
if (_.has(status.mysql_thread_to_task, mySqlThreadId)) {
let task = status.mysql_thread_to_task[mySqlThreadId];
if (_.has(status.mysql_thread_to_task, mysqlThreadId)) {
let task = status.mysql_thread_to_task[mysqlThreadId];
queryId = task['query_id'];
jobId = task['job_id'];
chunkId = task['chunk_id'];
Expand All @@ -258,7 +266,7 @@ function(CSSLoader,
}
const rowClass = QservWorkerMySQLQueries._state2css(state);
html += `
<tr mysql_thread_id="${mySqlThreadId}" query_id="${queryId}">
<tr mysql_thread_id="${mysqlThreadId}" query_id="${queryId}">
<th style="text-align:right;"><pre>${queryId}</pre></th>`;
if (queryId === '') {
html += `
Expand All @@ -275,7 +283,7 @@ function(CSSLoader,
<td style="text-align:right;" class="${rowClass}"><pre>${subChunkId}</pre></td>
<td style="text-align:right;" class="${rowClass}"><pre>${templateId}</pre></td>
<td style="text-align:right;" class="${rowClass}"><pre>${state}</pre></td>
<th style="text-align:right;"><pre>${mySqlThreadId}</pre></th>
<th style="text-align:right;"><pre>${mysqlThreadId}</pre></th>
<td style="text-align:right;"><pre>${row[COL_Time]}</pre></td>
<td style="text-align:right;"><pre>${row[COL_Command]}</pre></td>
<td style="text-align:right;"><pre>${row[COL_State]}</pre></td>`;
Expand All @@ -289,7 +297,10 @@ function(CSSLoader,
<td style="text-align:center; padding-top:0; padding-bottom:0">
<button class="btn btn-outline-dark btn-sm copy-query" style="height:20px; margin:0px;" title="${queryCopyTitle}"></button>
</td>
<td class="query_toggler" title="${queryToggleTitle}"><pre class="query" style="${queryStyle}">` + this._query2text(mySqlThreadId, expanded) + `<pre></td>`;
<td style="text-align:center; padding-top:0; padding-bottom:0">
<a class="btn btn-outline-dark btn-sm" style="height:20px; margin:0px;" title="${queryDownloadTitle}" href="${this._mysqlThreadId2url[mysqlThreadId]}" download></a>
</td>
<td class="query_toggler" title="${queryToggleTitle}"><pre class="query" style="${queryStyle}">` + this._query2text(mysqlThreadId, expanded) + `<pre></td>`;
}
html += `
</tr>`;
Expand All @@ -305,8 +316,8 @@ function(CSSLoader,
};
let copyQueryToClipboard = function(e) {
let button = $(e.currentTarget);
let mySqlThreadId = button.parent().parent().attr("mysql_thread_id");
let query = that._mySqlThreaId2query[mySqlThreadId];
let mysqlThreadId = button.parent().parent().attr("mysql_thread_id");
let query = that._mysqlThreadId2query[mysqlThreadId];
navigator.clipboard.writeText(query,
() => {},
() => { alert("Failed to write the query to the clipboard. Please copy the text manually: " + query); }
Expand All @@ -315,18 +326,18 @@ function(CSSLoader,
let toggleQueryDisplay = function(e) {
let td = $(e.currentTarget);
let pre = td.find("pre.query");
const mySqlThreadId = td.parent().attr("mysql_thread_id");
const expanded = !((mySqlThreadId in that._mySqlThreadId2Expanded) && that._mySqlThreadId2Expanded[mySqlThreadId]);
pre.text(that._query2text(mySqlThreadId, expanded));
that._mySqlThreadId2Expanded[mySqlThreadId] = expanded;
const mysqlThreadId = td.parent().attr("mysql_thread_id");
const expanded = !((mysqlThreadId in that._mysqlThreadId2Expanded) && that._mysqlThreadId2Expanded[mysqlThreadId]);
pre.text(that._query2text(mysqlThreadId, expanded));
that._mysqlThreadId2Expanded[mysqlThreadId] = expanded;
};
tbody.find("button.inspect-query").click(displayQuery);
tbody.find("button.copy-query").click(copyQueryToClipboard);
tbody.find("td.query_toggler").click(toggleQueryDisplay);
this._set_num_queries(numQueriesTotal, numQueriesDisplayed);
}
_query2text(mySqlThreadId, expanded) {
return Common.query2text(this._mySqlThreaId2query[mySqlThreadId], expanded);
_query2text(mysqlThreadId, expanded) {
return Common.query2text(this._mysqlThreadId2query[mysqlThreadId], expanded);
}
static _state2css(state) {
switch (state) {
Expand Down
14 changes: 12 additions & 2 deletions src/www/qserv/js/QservWorkerQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function(CSSLoader,
// queries between updates.
this._id2query = {}; // Store query text for each identifier. The dictionary gets
// updated at each refresh of the page.
this._id2url = {}; // Store URL to the query blob for each identifier
}
fwk_app_on_show() {
console.log('show: ' + this.fwk_app_name);
Expand Down Expand Up @@ -71,6 +72,7 @@ function(CSSLoader,
<th class="sticky">#tasks</th>
<th class="sticky">qid</th>
<th class="sticky" style="text-align:center;"><i class="bi bi-clipboard-fill"></i></th>
<th class="sticky" style="text-align:center;"><i class="bi bi-download"></i></th>
<th class="sticky" style="text-align:center;"><i class="bi bi-info-circle-fill"></i></th>
<th class="sticky">query</th>
</tr>
Expand Down Expand Up @@ -142,11 +144,15 @@ function(CSSLoader,
*/
_display(data) {
const queryCopyTitle = "Click to copy the query text to the clipboard.";
const queryDownloadTitle = "Click to download the query text to your computer.";
const queryInspectTitle = "Click to see detailed info (progress, messages, etc.) on the query.";
const queryToggleTitle = "Click to toggle query formatting.";
const queryStyle = "color:#4d4dff;";
let html = '';
this._id2query = {};
for (let id in this._id2url) {
URL.revokeObjectURL(this._id2url[id]);
}
for (let worker in data) {
if (!data[worker].success) {
html += `
Expand All @@ -173,15 +179,19 @@ function(CSSLoader,
const queryId = scheduler.query_id_to_count[j][0];
const numTasks = scheduler.query_id_to_count[j][1];
this._id2query[queryId] = queries[queryId].query;
this._id2url[queryId] = URL.createObjectURL(new Blob([queries[queryId].query], {type: "text/plain"}));
const expanded = (queryId in this._queryId2Expanded) && this._queryId2Expanded[queryId];
htmlSchedulerQueries += `
<tr id="${queryId}">
<td><pre>${numTasks}</pre></td>
<td><pre>${queryId}</pre></td>
<th><pre>${queryId}</pre></th>
<td style="text-align:center; padding-top:0; padding-bottom:0">
<button class="btn btn-outline-dark btn-sm copy-query" style="height:20px; margin:0px;" title="${queryCopyTitle}"></button>
</td>
<td style="text-align:right; padding-top:0; padding-bottom:0">
<td style="text-align:center; padding-top:0; padding-bottom:0">
<a class="btn btn-outline-dark btn-sm" style="height:20px; margin:0px;" title="${queryDownloadTitle}" href="${this._id2url[queryId]}" download></a>
</td>
<td style="text-align:center; padding-top:0; padding-bottom:0">
<button class="btn btn-outline-info btn-sm inspect-query" style="height:20px; margin:0px;" title="${queryInspectTitle}"></button>
</td>
<td class="query_toggler" title="${queryToggleTitle}"><pre class="query" style="${queryStyle}">` + this._query2text(queryId, expanded) + `<pre></td>
Expand Down
10 changes: 10 additions & 0 deletions src/www/qserv/js/StatusActiveQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function(CSSLoader,
this._queryId2Expanded = {}; // Store 'true' to allow persistent state for the expanded
// queries between updates.
this._id2query = {}; // Store query text for each identifier
this._id2url = {}; // Store URL to the query blob for each identifier
}

/**
Expand Down Expand Up @@ -108,6 +109,7 @@ function(CSSLoader,
<th class="sticky" style="text-align:right;">Czar</th>
<th class="sticky" style="text-align:right;">QID</th>
<th class="sticky" style="text-align:center;"><i class="bi bi-clipboard-fill"></i></th>
<th class="sticky" style="text-align:center;"><i class="bi bi-download"></i></th>
<th class="sticky" class="sticky" style="text-align:center;"><i class="bi bi-info-circle-fill"></i></th>
<th class="sticky" class="sticky" style="text-align:center;"><i class="bi bi-bar-chart-steps"></i></th>
<th class="sticky">Query</th>
Expand Down Expand Up @@ -184,15 +186,20 @@ function(CSSLoader,
}
_display(data) {
this._id2query = {};
for (let id in this._id2url) {
URL.revokeObjectURL(this._id2url[id]);
}
const queryToggleTitle = "Click to toggle query formatting.";
const queryCopyTitle = "Click to copy the query text to the clipboard.";
const queryDownloadTitle = "Click to download the query text to your computer.";
const queryInspectTitle = "Click to see detailed info (progress, messages, etc.) on the query.";
const queryProgressTitle = "Click to see query progression plot.";
const queryStyle = "color:#4d4dff;";
let html = '';
for (let i in data.queries) {
let query = data.queries[i];
this._id2query[query.queryId] = query.query;
this._id2url[query.queryId] = URL.createObjectURL(new Blob([query.query], {type: "text/plain"}));
const progress = Math.floor(100. * query.completedChunks / query.totalChunks);
const scheduler = _.isUndefined(query.scheduler) ? 'Loading...' : query.scheduler.substring('Sched'.length);
const scheduler_color = _.has(this._scheduler2color, scheduler) ?
Expand Down Expand Up @@ -231,6 +238,9 @@ function(CSSLoader,
<td style="text-align:center; padding-top:0; padding-bottom:0">
<button class="btn btn-outline-dark btn-sm copy-query" style="height:20px; margin:0px;" title="${queryCopyTitle}"></button>
</td>
<td style="text-align:center; padding-top:0; padding-bottom:0">
<a class="btn btn-outline-dark btn-sm" style="height:20px; margin:0px;" title="${queryDownloadTitle}" href="${this._id2url[query.queryId]}" download></a>
</td>
<td style="text-align:center; padding-top:0; padding-bottom:0">
<button class="btn btn-outline-info btn-sm inspect-query" style="height:20px; margin:0px;" title="${queryInspectTitle}"></button>
</td>
Expand Down
Loading
Loading