api/ui/debug/js/backfill.js (236 lines of code) (raw):
// Copyright (c) 2017-2018 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var upsertbatchTable;
function initSchedulerViewer() {
// Disable null value warning from data table.
$.fn.dataTable.ext.errMode = 'none';
$.ajax({
url: "/dbg/jobs/backfill",
success: function (body) {
var runningJobData = []
var pastRunsData = []
for (var key in body) {
var strs = key.split("|")
var row = body[key]
row['table'] = strs[0]
row['shard'] = strs[1]
row['type'] = strs[2]
if ('lastDuration' in row) {
row['lastDuration'] = row['lastDuration'].toDuration()
}
if ('lockDuration' in row) {
row['lockDuration'] = row['lockDuration'].toDuration()
}
if (row['status'] == 'running') {
runningJobData.push(row)
} else {
// Also get stats like current backfill buffer size and backfilling size, etc.
$.ajax({
url: "/dbg/{0}/{1}".format(row['table'], row['shard']),
success: function (body) {
var backfillMgr = body["liveStore"]["backfillManager"];
if (backfillMgr) {
row["numRecords"] = backfillMgr["numRecords"];
row["currentBufferSize"] = backfillMgr["currentBufferSize"];
row["backfillingBufferSize"] = backfillMgr["backfillingBufferSize"];
row["maxBufferSize"] = backfillMgr["maxBufferSize"];
row["numUpsertBatches"] = backfillMgr["numUpsertBatches"];
}
},
error: function (xhr) {
alert(xhr.responseText);
},
async: false
}
)
pastRunsData.push(row);
}
}
initRunningJobTable(runningJobData)
initPastRunTable(pastRunsData)
},
error: function (xhr) {
alert(xhr.responseText)
}
}
);
// Init table selector.
$('#table-selector').select2({
ajax: {
url: "/schema/tables",
dataType: 'json',
quietMillis: 50,
processResults: function (data) {
return {
results: $.map(data, function (item, idx) {
return {
text: item,
id: idx + 1,
}
})
};
}
},
width: 'resolve'
}).on('change', function () {
refreshBackfillQueue();
});
// Init shard selector.
$('#shard-selector').select2({
data: [
{
"id": 0,
"text": 0,
}
]
}).on('change', function (e) {
refreshBackfillQueue();
});
}
function refreshBackfillQueue() {
var table = $('#table-selector').select2('data')[0].text;
var shard = $('#shard-selector').select2('data')[0].text;
if ($('#upsertbatch-selector').select2()) {
$('#upsertbatch-selector').empty();
$('#upsertbatch-selector').select2("destroy");
}
$('#upsertbatch-selector').select2({
ajax: {
url: "/dbg/{0}/{1}".format(table, shard),
cache: true,
dataType: 'json',
quietMillis: 50,
processResults: function (data) {
var results = [];
var numUpsertBatches = data.liveStore.backfillManager.numUpsertBatches;
for (var i = 0; i < numUpsertBatches; i++) {
results.push({
text: i,
id: i + 1
});
}
return {
results: results
};
}
},
width: '100px',
minimumResultsForSearch: -1
}).on('change', function (e) {
refreshUpsertBatchTable();
});
}
function refreshUpsertBatchTable() {
var table = $("#table-selector").select2('data')[0].text;
var shard = $("#shard-selector").select2('data')[0].text;
var upsertBatch = $("#upsertbatch-selector").select2('data')[0].text;
$.ajax({
url: "/dbg/{0}/{1}/backfill-manager/upsertbatches/{2}".format(table, shard, upsertBatch),
success: function (body) {
var columns = body.columnNames.map(function (name) {
return {"title": name}
}
);
// Need to explicitly destroy old data table.
if (upsertbatchTable) {
upsertbatchTable.destroy();
$('#upsertbatch-table').empty();
}
upsertbatchTable = $('#upsertbatch-table').DataTable({
"serverSide": true,
"processing": true,
"paging": true,
"searching": false,
"pageLength": 20,
"lengthMenu": [[1, 10, 25, 50, 100], [1, 10, 25, 50, 100]],
"columns": columns,
"ajax": {
"type": "GET",
"url": "/dbg/{0}/{1}/backfill-manager/upsertbatches/{2}".format(table, shard, upsertBatch),
"dataType": "json",
"contentType": 'application/json'
}
});
},
error: function (error) {
alert('error: ' + eval(error));
}
});
}
function submitBackfillJob(table, shard) {
var url = "/dbg/{0}/{1}/backfill".format(table, shard)
$.ajax({
url: url,
method: "POST",
dataType: 'json',
success: function (body) {
alert(body);
reloadCurrentTab();
},
error: function (xhr) {
alert(xhr.responseText);
}
}
)
}
function initRunningJobTable(data) {
$('#running-job-table').DataTable({
paging: false,
searching: false,
aoColumns: [
{title: "Table", data: "table"},
{title: "Shard", data: "shard", type: "num"},
{title: "Type", data: "type"},
{title: "Stage", data: "stage"},
{title: "Current", data: "current", type: "num"},
{title: "Total", data: "total", type: "num"},
],
aaData: data,
});
}
function initPastRunTable(data) {
$('#past-runs-table').DataTable({
paging: true,
searching: true,
pageLength: 20,
lengthMenu: [[1, 10, 25, 50, 100], [1, 10, 25, 50, 100]],
aoColumns: [
{title: "Table", data: "table"},
{title: "Shard", data: "shard", type: "num"},
{title: "Type", data: "type"},
{title: "Status", data: "status"},
{title: "Number of Records Backfilled", data: "numRecords", type: "num"},
{title: "Number of Affected Days", data: "numAffectedDays", type: "num"},
{
title: "Action",
mData: null,
bSortable: false,
mRender: function (data, type, row) {
var table = row['table']
var shard = row['shard']
return $("<div />").append($(
"<button class='ui-button' onclick=\"submitBackfillJob('" + table + "'," + shard + ")\">Backfill</button>")).html();
},
},
{
title: "Last Error",
data: "lastError",
type: "string",
render: function (data) {
return JSON.stringify(data)
}
},
{
title: "Last Start Time",
data: "lastStartTime",
type: "date",
render: function (data) {
return new Date(data).toLocaleString()
}
},
{title: "Last Duration", data: "lastDuration", type: "string"},
{title: "Last Lock Wait Duration", data: "lockDuration", type: "string"},
{title: "Redo Log File", data: "redologFile", type: "number"},
{title: "Batch Offset", data: "batchOffset", type: "number"},
{title: "Number of Records in Queue", data: "numRecords", type: "number"},
{title: "Current Buffer Size", data: "currentBufferSize", type: "number"},
{title: "Backfilling Buffer Size", data: "backfillingBufferSize", type: "number"},
{title: "Max Buffer Size", data: "maxBufferSize", type: "number"},
{title: "Num Upsert Batches", data: "numUpsertBatches", type: "number"},
],
aaData: data,
});
}