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

add auto refresh to dags home page #22900

Merged
merged 13 commits into from
May 1, 2022
2 changes: 1 addition & 1 deletion airflow/www/static/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ label[for="timezone-other"],
}

.refresh-actions > .switch-label {
margin: 0 10px 0 20px;
margin: 0 10px;
}

.loading-dots.refresh-loading {
Expand Down
132 changes: 131 additions & 1 deletion airflow/www/static/js/dags.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
* under the License.
*/

/* global document, window, $, d3, STATE_COLOR, isoDateToTimeEl */
/* global document, window, $, d3, STATE_COLOR, isoDateToTimeEl, autoRefreshInterval,
localStorage */

import { getMetaValue } from './utils';
import tiTooltip from './task_instances';
Expand All @@ -38,6 +39,10 @@ const dagStatsUrl = getMetaValue('dag_stats_url');
const taskStatsUrl = getMetaValue('task_stats_url');
const gridUrl = getMetaValue('grid_url');

// auto refresh interval in milliseconds
// (x2 the interval in tree/graph view since this page can take longer to refresh )
const refreshIntervalMs = 2000;
talnagar marked this conversation as resolved.
Show resolved Hide resolved

$('#tags_filter').select2({
placeholder: 'Filter DAGs by tag',
allowClear: true,
Expand Down Expand Up @@ -160,6 +165,7 @@ function lastDagRunsHandler(error, json) {
// Show last run as a link to the graph view
g.selectAll('a')
talnagar marked this conversation as resolved.
Show resolved Hide resolved
.attr('href', `${graphUrl}?dag_id=${encodeURIComponent(dagId)}&execution_date=${encodeURIComponent(executionDate)}`)
.html('')
.insert(isoDateToTimeEl.bind(null, executionDate, { title: false }));

// Only show the tooltip when we have a last run and add the json to a custom data- attribute
Expand Down Expand Up @@ -297,6 +303,7 @@ function drawTaskStatsForDag(dagId, states) {
.duration(300)
.delay((d, i) => i * 50)
.style('opacity', 1);

d3.select('.js-loading-task-stats').remove();

g.append('text')
Expand Down Expand Up @@ -351,7 +358,118 @@ function hideSvgTooltip() {
$('#svg-tooltip').css('display', 'none');
}

function refreshDagRunsAndTasks(selector, dagId, states) {
d3.select(`svg#${selector}-${dagId.replace(/\./g, '__dot__')}`)
.selectAll('circle')
.data(states)
.attr('stroke-width', (d) => {
if (d.count > 0) return strokeWidth;
return 1;
})
.attr('stroke', (d) => {
if (d.count > 0) return STATE_COLOR[d.state];

return 'gainsboro';
})
.attr('fill', '#fff')
.attr('r', diameter / 2)
.attr('title', (d) => d.state)
.on('mouseover', (d) => {
if (d.count > 0) {
d3.select(this).transition().duration(400)
.attr('fill', '#e2e2e2')
.style('stroke-width', strokeWidthHover);
}
});
d3.select(`svg#${selector}-${dagId.replace(/\./g, '__dot__')}`)
.selectAll('text')
.data(states)
.text((d) => {
if (d.count > 0) {
return d.count;
}
return '';
});
}

function refreshTaskStateHandler(error, ts) {
Object.keys(ts).forEach((dagId) => {
const states = ts[dagId];
refreshDagRunsAndTasks('task-run', dagId, states);
});
}

let refreshInterval;

function checkActiveRuns(json) {
// filter latest dag runs and check if there are still running dags
const activeRuns = Object.keys(json).filter((dagId) => {
const dagRuns = json[dagId].filter((s) => s.state === 'running').filter((r) => r.count > 0);
return (dagRuns.length > 0);
});
if (activeRuns.length === 0) {
// in case there are no active runs increase the interval for auto refresh
$('#auto_refresh').prop('checked', false);
clearInterval(refreshInterval);
}
}

function refreshDagRuns(error, json) {
checkActiveRuns(json);
Object.keys(json).forEach((dagId) => {
const states = json[dagId];
drawDagStatsForDag(dagId, states);
refreshDagRunsAndTasks('dag-run', dagId, states);
});
}

function handleRefresh() {
$('#loading-dots').css('display', 'inline-block');
d3.json(lastDagRunsUrl)
.header('X-CSRFToken', csrfToken)
.post(encodedDagIds, lastDagRunsHandler);
d3.json(dagStatsUrl)
.header('X-CSRFToken', csrfToken)
.post(encodedDagIds, refreshDagRuns);
d3.json(taskStatsUrl)
.header('X-CSRFToken', csrfToken)
.post(encodedDagIds, refreshTaskStateHandler);
setTimeout(() => {
$('#loading-dots').css('display', 'none');
}, refreshIntervalMs);
}
talnagar marked this conversation as resolved.
Show resolved Hide resolved

function startOrStopRefresh() {
if ($('#auto_refresh').is(':checked')) {
refreshInterval = setInterval(() => {
handleRefresh();
}, autoRefreshInterval * refreshIntervalMs);
} else {
clearInterval(refreshInterval);
}
}

function initAutoRefresh() {
const isDisabled = localStorage.getItem('dagsDisableAutoRefresh');
$('#auto_refresh').prop('checked', !(isDisabled));
startOrStopRefresh();
d3.select('#refresh_button').on('click', () => handleRefresh());
}

// pause autorefresh when the page is not active
const handleVisibilityChange = () => {
if (document.hidden) {
clearInterval(refreshInterval);
} else {
initAutoRefresh();
}
};

document.addEventListener('visibilitychange', handleVisibilityChange);

$(window).on('load', () => {
initAutoRefresh();

$('body').on('mouseover', '.has-svg-tooltip', (e) => {
const elem = e.target;
const text = elem.getAttribute('title');
Expand Down Expand Up @@ -379,3 +497,15 @@ $('.js-next-run-tooltip').each((i, run) => {
});
});
});

$('#auto_refresh').change(() => {
if ($('#auto_refresh').is(':checked')) {
// Run an initial refresh before starting interval if manually turned on
handleRefresh();
localStorage.removeItem('dagsDisableAutoRefresh');
} else {
localStorage.setItem('dagsDisableAutoRefresh', 'true');
$('#loading-dots').css('display', 'none');
}
startOrStopRefresh();
});
Loading