const startPage = 0;
const paginationSize = 5;
function renderNodesMetrics(data) {
if (!data || !data.nodes_info || data.nodes_info.length === 0) {
document.getElementById("components").innerHTML = "No Found Components information"
return
}
let tableHTML = '' +
'Node Name ' +
'CPU Usage ' +
'Usage/Memory(GB) ' +
'Usage/Disk(GB) '+
'IO Wait ' +
'RPC Ops/s ' +
'Network Throughput(MB/s) ' +
'Disk Throughput(MB/s) ' +
' ';
tableHTML += '
';
data.nodes_info.forEach(node => {
tableHTML += '';
tableHTML += `${node.infos['name']} `;
let hardwareInfo = node.infos['hardware_infos']
let cpuUsage = parseFloat(`${hardwareInfo['cpu_core_usage']}`).toFixed(2)
tableHTML += `${cpuUsage}% `;
let memoryUsage = (parseInt(`${hardwareInfo['memory_usage']}`) / 1024 / 1024 / 1024).toFixed(2)
let memory = (parseInt(`${hardwareInfo['memory']}`) / 1024 / 1024 / 1024).toFixed(2)
tableHTML += `${memoryUsage}/${memory} `;
let diskUsage = (parseInt(`${hardwareInfo['disk_usage']}`) / 1024 / 1024 / 1024).toFixed(2)
let disk = (parseInt(`${hardwareInfo['disk']}`) / 1024 / 1024 / 1024).toFixed(2)
tableHTML += `${diskUsage}/${disk} `;
tableHTML += `0.00 `;
tableHTML += `100 `;
tableHTML += `5 `;
tableHTML += `20 `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('nodeMetrics').innerHTML = tableHTML;
}
function renderComponentInfo(data) {
if (!data || !data.nodes_info || data.nodes_info.length === 0) {
document.getElementById("components").innerHTML = "No Found Components information"
return
}
let tableHTML = `
Node Name
Node IP
Start Time
Node Status
`;
// Process each node's information
data.nodes_info.forEach(node => {
const hardwareInfo = node.infos['hardware_infos'];
const tr = `
${node.infos['name']}
${hardwareInfo['ip']}
${node.infos['created_time']}
${node.infos['has_error']? node.infos['error_reason'] : 'Healthy'}
`;
tableHTML += tr
});
tableHTML += ' '
document.getElementById("components").innerHTML = tableHTML;
}
function renderNodeRequests(data) {
let tableHTML = '' +
'Node Name ' +
'QPS ' +
'Read Request Count ' +
'Write Request Count ' +
'Delete Request Count ' +
' ';
tableHTML += '';
data.forEach(node => {
tableHTML += '';
tableHTML += `${node.node_name} `;
tableHTML += `${node.QPS} `;
tableHTML += `${node.read_request_count} `;
tableHTML += `${node.write_request_count} `;
tableHTML += `${node.delete_request_count} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('nodeRequests').innerHTML = tableHTML;
}
let databaseData = null; // Global variable to store fetched data
function renderDatabases(currentPage, rowsPerPage) {
if (!databaseData) {
console.error('No database data available');
return;
}
// Generate the HTML for the table with Bootstrap classes
let tableHTML = '' +
'Name ' +
'ID ' +
'Created Timestamp ' +
' ';
tableHTML += '';
// Calculate start and end indices for pagination
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const totalCount = databaseData.db_names.length;
// Loop over page data to render only rows for the current page
for (let i = start; i < end && i < totalCount; i++) {
tableHTML += '';
tableHTML += `${databaseData.db_names[i]} `;
tableHTML += `${databaseData.db_ids? databaseData.db_ids[i] : 0} `;
tableHTML += `${databaseData.created_timestamps[i]} `;
tableHTML += ' ';
// Hidden row for displaying collection details as JSON
tableHTML += `
Loading...
`;
}
tableHTML += ' ';
// Insert table HTML into the DOM
document.getElementById('database').innerHTML = tableHTML;
// Display the total count
document.getElementById('db_totalCount').innerText = `Total Databases: ${totalCount}`;
// Create pagination controls
const totalPages = Math.ceil(totalCount / rowsPerPage);
let paginationHTML = ' ';
// Insert pagination HTML into the DOM
document.getElementById('dbPaginationControls').innerHTML = paginationHTML;
}
function describeDatabase(databaseName, rowIndex, type) {
fetchData(`${MILVUS_URI}/_db/desc?db_name=${databaseName}`, describeDatabaseData)
.then(data => {
// Format data as JSON and insert into the designated row
const jsonFormattedData = JSON.stringify(data, null, 2);
document.getElementById(`${type}-json-details-${rowIndex}`).textContent = jsonFormattedData;
// Toggle the visibility of the details row
const detailsRow = document.getElementById(`${type}-details-row-${rowIndex}`);
detailsRow.style.display = detailsRow.style.display === 'none' ? 'table-row' : 'none';
})
.catch(error => {
console.error('Error fetching collection details:', error);
document.getElementById(`${type}-json-details-${rowIndex}`).textContent = 'Failed to load collection details.';
});
}
function describeCollection(databaseName, collectionName, rowIndex, type) {
fetchData(`${MILVUS_URI}/_collection/desc?db_name=${databaseName}&&collection_name=${collectionName}`, describeCollectionData)
.then(data => {
// Format data as JSON and insert into the designated row
const jsonFormattedData = JSON.stringify(data, null, 2);
document.getElementById(`${type}-json-details-${rowIndex}`).textContent = jsonFormattedData;
// Toggle the visibility of the details row
const detailsRow = document.getElementById(`${type}-details-row-${rowIndex}`);
detailsRow.style.display = detailsRow.style.display === 'none' ? 'table-row' : 'none';
})
.catch(error => {
console.error('Error fetching collection details:', error);
document.getElementById(`${type}-json-details-${rowIndex}`).textContent = 'Failed to load collection details.';
});
}
function fetchCollections(databaseName) {
fetchData(MILVUS_URI + `/_collection/list?db_name=${databaseName}`, listCollectionData )
.then(data => {
collectionsData = data;
renderCollections(databaseName, startPage, paginationSize)
})
.catch(error => {
handleError(error);
});
}
let collectionsData = null; // Global variable to store fetched data
function renderCollections(databaseName, currentPage, rowsPerPage) {
let data = collectionsData;
if (!data || !data.collection_names) {
console.error('No collections data available');
return;
}
let tableHTML = '' +
'Name ' +
'Collection ID ' +
'Created Timestamp ' +
'Loaded Percentages ' +
'Queryable ' +
' ';
tableHTML += '';
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const totalCount = data.collection_names.length;
for (let i = start; i < end && i < totalCount; i++) {
tableHTML += '';
tableHTML += `${data.collection_names[i]} `;
tableHTML += `${data.collection_ids[i]} `;
tableHTML += `${data.created_utc_timestamps[i]} `;
tableHTML += `${data.inMemory_percentages? data.inMemory_percentages[i]: 'unknown'} `;
tableHTML += `${data.query_service_available? data.query_service_available[i] ? 'Yes' : 'No' : 'No'} `;
tableHTML += ' ';
// Hidden row for displaying collection details as JSON
tableHTML += `
Loading...
`;
}
tableHTML += ' ';
document.getElementById('collectionList').innerHTML = tableHTML;
document.getElementById('collection_totalCount').innerText = `Total Collections: ${totalCount}`;
const totalPages = Math.ceil(totalCount / rowsPerPage);
let paginationHTML = ' ';
document.getElementById('collectionPaginationControls').innerHTML = paginationHTML;
}
let collectionRequestsData = null; // Global variable to store fetched data
function renderCollectionRequests(database, currentRequestPage, requestRowsPerPage) {
if (!collectionRequestsData) {
console.error('No collection requests data available');
return;
}
const data = collectionRequestsData;
let tableHTML = '' +
'Collection Name ' +
'Search QPS ' +
'Query QPS ' +
'Insert Throughput(MB/s) ' +
'Delete QPS ' +
' ';
tableHTML += '';
const start = currentRequestPage * requestRowsPerPage;
const end = start + requestRowsPerPage;
const totalCount = data.length;
for (let i = start; i < end && i < totalCount; i++) {
tableHTML += '';
tableHTML += `${data[i].collection_name} `;
tableHTML += `${data[i].search_QPS} `;
tableHTML += `${data[i].query_QPS} `;
tableHTML += `${data[i].write_throughput} `;
tableHTML += `${data[i].delete_QPS} `;
tableHTML += ' ';
// Hidden row for displaying collection details as JSON
tableHTML += `
Loading...
`;
}
tableHTML += ' ';
document.getElementById('collectionRequests').innerHTML = tableHTML;
document.getElementById('collection_totalCount').innerText = `Total Collections: ${totalCount}`;
const totalPages = Math.ceil(totalCount / requestRowsPerPage);
let paginationHTML = ' ';
document.getElementById('collectionPaginationControls').innerHTML = paginationHTML;
}
function renderSysInfo(data) {
if (!data || data.length === 0 ) {
document.getElementById("sysInfo").innerHTML = "No Found Sys information"
return
}
let tableHTML = '' +
' Attribute ' +
' Value ' +
' Description ' +
' ';
tableHTML += readSysInfo(data.nodes_info[0].infos['system_info'])
tableHTML += '';
tableHTML += `Started Time `;
tableHTML += `${data.nodes_info[0].infos['created_time']} `;
tableHTML += `The time when the system was started `;
tableHTML += '';
// Display table in the div
document.getElementById('sysInfo').innerHTML = tableHTML;
}
function renderClientsInfo(data) {
if (!data || data.length === 0 ) {
document.getElementById("clients").innerHTML = "No clients connected"
return
}
let tableHTML = '' +
' Host ' +
' User ' +
' SdkType ' +
' SdkVersion ' +
' LocalTime ' +
' LastActiveTime ' +
' ';
data.forEach(client => {
const tr = `
${client['host']}
${client['user'] || "default"}
${client['sdk_type']}
${client['sdk_version']}
${client['local_time']}
${client['reserved'] ? client['reserved']['last_active_time']: ""}
`;
tableHTML += tr
});
tableHTML += ''
document.getElementById("clients").innerHTML = tableHTML;
}
function readSysInfo(systemInfo) {
let row = ''
row += '';
row += `GitCommit `;
row += `${systemInfo.system_version} `;
row += `Git commit SHA that the current build of the system is based on `;
row += ' ';
row += '';
row += `Deploy Mode `;
row += `${systemInfo.deploy_mode} `;
row += `the mode in which the system is deployed `;
row += ' ';
row += '';
row += `Build Version `;
row += `${systemInfo.build_version} `;
row += `the version of the system that was built `;
row += ' ';
row += '';
row += `Build Time `;
row += `${systemInfo.build_time} `;
row += `the exact time when the system was built `;
row += ' ';
row += '';
row += `Go Version `;
row += `${systemInfo.used_go_version} `;
row += `the version of the Golang that was used to build the system `;
row += ' ';
return row
}
let configData = null; // Global variable to store the config object
let currentPage = 0;
const rowsPerPage = 10;
function renderConfigs(obj, searchTerm = '') {
configData = obj;
// Filter the data based on the search term
const filteredData = Object.keys(obj).filter(key =>
key.toLowerCase().includes(searchTerm.toLowerCase()) ||
String(obj[key]).toLowerCase().includes(searchTerm.toLowerCase())
).reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {});
// Calculate pagination variables
const totalCount = Object.keys(filteredData).length;
const totalPages = Math.ceil(totalCount / rowsPerPage);
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
// Generate table header
let tableHTML = '' +
' Attribute ' +
' Value ' +
' ';
// Generate table rows based on current page and filtered data
tableHTML += '';
const entries = Object.entries(filteredData).slice(start, end);
entries.forEach(([prop, value]) => {
tableHTML += '';
tableHTML += `${prop} `;
tableHTML += `${value} `;
tableHTML += ' ';
});
tableHTML += ' ';
// Display the table
document.getElementById('mConfig').innerHTML = tableHTML;
// Display total count and pagination controls
let paginationHTML = '';
// Total count display
paginationHTML += `Total Configs: ${totalCount} `;
// Pagination controls
paginationHTML += '
';
document.getElementById('paginationControls').innerHTML = paginationHTML;
}
function changePage(page, searchTerm = '') {
currentPage = page;
renderConfigs(configData, searchTerm);
}
function renderDependencies(data) {
if (!data || data.length === 0 ) {
document.getElementById("3rdDependency").innerHTML = "No Found "
return
}
let tableHTML = '' +
' Sys Name ' +
' Cluster Status ' +
' Members Status ' +
' ';
Object.keys(data).forEach(key => {
row = data[key]
const tr = `
${key === 'metastore'? 'metastore [' + row['meta_type'] + ']' : 'mq [' + row['mq_type'] + ']'}
${row['health_status']? 'Healthy' : row['unhealthy_reason']}
${row['members_health']? row['members_health'].map(member => `
Endpoint: ${member.endpoint}, Health: ${member.health ? "Healthy" : "Unhealthy"}
`).join('') :
``}
`;
tableHTML += tr
});
tableHTML += ''
document.getElementById("3rdDependency").innerHTML = tableHTML;
}
function renderQCTasks(data) {
if (!data || data.length === 0 ) {
document.getElementById("qcTasks").innerHTML = "No Found QC Tasks"
return
}
let tableHTML = '' +
'Task Name ' +
'Collection ID ' +
'Task Type ' +
'Task Status ' +
'Actions ' +
' ';
tableHTML += '';
data.forEach(task => {
let taskStatus = task.task_status;
if (taskStatus === 'failed') {
taskStatus = task.reason;
}
tableHTML += '';
tableHTML += `${task.task_name} `;
tableHTML += `${task.collection_id} `;
tableHTML += `${task.task_type} `;
tableHTML += `${taskStatus} `;
tableHTML += `${task.actions.join(', ')} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('qcTasks').innerHTML = tableHTML;
}
function renderBuildIndexTasks(data) {
if (!data || data.length === 0 ) {
document.getElementById("buildIndexTasks").innerHTML = "No Found Build Index Tasks"
return
}
let tableHTML = '' +
'Index ID ' +
'Collection ID ' +
'Segment ID ' +
'Build ID ' +
'Index State ' +
'Index Size ' +
'Index Version ' +
'Create Time ' +
' ';
tableHTML += '';
data.forEach(task => {
let indexState = task.index_state;
if (indexState === 'Failed') {
indexState = task.fail_reason;
}
tableHTML += '';
tableHTML += `${task.index_id} `;
tableHTML += `${task.collection_id} `;
tableHTML += `${task.segment_id} `;
tableHTML += `${task.build_id} `;
tableHTML += `${indexState} `;
tableHTML += `${task.index_size} `;
tableHTML += `${task.index_version} `;
tableHTML += `${task.create_time} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('buildIndexTasks').innerHTML = tableHTML;
}
function renderCompactionTasks(data) {
if (!data || data.length === 0 ) {
document.getElementById("compactionTasks").innerHTML = "No Found Compaction Tasks"
return
}
let tableHTML = '' +
'Plan ID ' +
'Collection ID ' +
'Type ' +
'State ' +
'Start Time ' +
'End Time ' +
'Total Rows ' +
'Input Segments ' +
'Result Segments ' +
' ';
tableHTML += '';
data.forEach(task => {
let state = task.state;
if (state === 'Failed') {
state = task.fail_reason;
}
tableHTML += '';
tableHTML += `${task.plan_id} `;
tableHTML += `${task.collection_id} `;
tableHTML += `${task.type} `;
tableHTML += `${state} `;
tableHTML += `${new Date(task.start_time * 1000).toLocaleString()} `;
tableHTML += `${new Date(task.end_time * 1000).toLocaleString()} `;
tableHTML += `${task.total_rows} `;
tableHTML += `${task.input_segments? task.input_segments.join(', '): ''} `;
tableHTML += `${task.result_segments? task.result_segments.join(', '): ''} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('compactionTasks').innerHTML = tableHTML;
}
function renderImportTasks(data) {
if (!data || data.length === 0 ) {
document.getElementById("importTasks").innerHTML = "No Found Import Tasks"
return
}
let tableHTML = '' +
'Job ID ' +
'Task ID ' +
'Collection ID ' +
'Node ID ' +
'State ' +
'Task Type ' +
'Created Time ' +
'Complete Time ' +
' ';
tableHTML += '';
data.forEach(task => {
let state = task.state;
if (state === 'Failed') {
state = task.reason;
}
tableHTML += '';
tableHTML += `${task.job_id} `;
tableHTML += `${task.task_id} `;
tableHTML += `${task.collection_id} `;
tableHTML += `${task.node_id} `;
tableHTML += `${state} `;
tableHTML += `${task.task_type} `;
tableHTML += `${task.created_time} `;
tableHTML += `${task.complete_time} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('importTasks').innerHTML = tableHTML;
}
function renderSyncTasks(data) {
if (!data || data.length === 0 ) {
document.getElementById("syncTasks").innerHTML = "No Found Sync Tasks"
return
}
let tableHTML = '' +
'Segment ID ' +
'Batch Rows ' +
'Segment Level ' +
'Timestamp From ' +
'Timestamp To ' +
'Delta Row Count ' +
'Flush Size ' +
'Running Time ' +
'Node ID ' +
' ';
tableHTML += '';
data.forEach(task => {
tableHTML += '';
tableHTML += `${task.segment_id} `;
tableHTML += `${task.batch_rows} `;
tableHTML += `${task.segment_level} `;
tableHTML += `${task.ts_from} `;
tableHTML += `${task.ts_to} `;
tableHTML += `${task.delta_row_count} `;
tableHTML += `${task.flush_size} `;
tableHTML += `${task.running_time} `;
tableHTML += `${task.node_id} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('syncTasks').innerHTML = tableHTML;
}
let dataChannels = null; // Store fetched merged dn_channels data and dc_dist data
function fetchAndRenderDataChannels() {
let dcDist = fetchData(MILVUS_URI + "/_dc/dist", dc_dist);
let dnChannels = fetchData(MILVUS_URI + "/_dn/channels", dn_channels);
Promise.all([dnChannels, dcDist])
.then(([dnChannelsData, dcDistData]) => {
if (dnChannelsData && dcDistData) {
const { mergedChannels, notifications } = mergeChannels(dnChannelsData, dcDistData);
dataChannels = mergedChannels
renderChannels(mergedChannels, currentPage, rowsPerPage);
renderNotifications(notifications, 'notificationsDChannels');
}
})
.catch(error => {
handleError(error);
});;
}
// Merge channels by matching names from dn_channels and dc_dist
function mergeChannels(dnChannels, dcDist) {
const merged = {};
const notifications = []; // Store notifications for unmatched channels
// const dnChannelNames = new Set(dnChannels.map(channel => channel.name));
const dcChannelNames = new Set(dcDist.dm_channels.map(dcChannel => dcChannel.channel_name));
// Add dn_channels to merged and mark channels missing in dc_dist
dnChannels.forEach(channel => {
channel.node_id = "datanode-" + channel.node_id;
const channelData = { ...channel };
if (!dcChannelNames.has(channel.name)) {
channelData.notification = 'Not found in DataCoord';
notifications.push(channelData); // Add to notifications
}
merged[channel.name] = channelData;
});
// Merge dc_dist channels or add new ones with notification if missing in dn_channels
dcDist.dm_channels.forEach(dcChannel => {
dcChannel.node_id = "datacoord-" + dcChannel.node_id;
const name = dcChannel.channel_name;
if (merged[name]) {
if (merged[name].watch_state !== "Healthy") {
dcChannel.watch_state = merged[name].watch_state
}
merged[name] = { ...merged[name], ...dcChannel };
} else {
const channelData = { ...dcChannel, notification: 'Not found in DataNode' };
notifications.push(channelData); // Add to notifications
merged[name] = channelData;
}
});
return { mergedChannels: Object.values(merged), notifications };
}
// Render the merged channels in a paginated table
function renderChannels(channels, currentPage, rowsPerPage) {
const table = document.getElementById("dataChannelsTableBody");
table.innerHTML = ""; // Clear previous rows
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const paginatedData = channels.slice(start, end);
paginatedData.forEach(channel => {
const row = document.createElement("tr");
row.innerHTML = `
${channel.name || channel.channel_name}
${channel.watch_state || "N/A"}
${channel.node_id}
${channel.latest_time_tick || "N/A"}
${channel.start_watch_ts || "N/A"}
${channel.check_point_ts || "N/A"}
`;
table.appendChild(row);
});
// Update pagination info
const totalPages = Math.ceil(channels.length / rowsPerPage);
let paginationHTML = ' ';
document.getElementById('dchannelPaginationControls').innerHTML = paginationHTML;
document.getElementById('dchannel_totalCount').innerText = `Total Channels: ${channels.length}`;
}
// Render notifications below the table
function renderNotifications(notifications, id) {
if (!notifications || notifications.length === 0) {
return
}
const notificationsContainer = document.getElementById(id);
notificationsContainer.innerHTML = ""; // Clear previous notifications
notifications.forEach(channel => {
const notificationAlert = document.createElement("div");
notificationAlert.className = "alert alert-warning";
notificationAlert.role = "alert";
// Generate detailed information for each channel
const details = `
Channel: ${channel.name || channel.channel_name} ${channel.notification}
`;
notificationAlert.innerHTML = details;
notificationsContainer.appendChild(notificationAlert);
});
}
let dataSegments = null; // Store fetched merged dn_segments data and dc_dist data
const dataSegmentsStartPage = 0;
const dataSegmentsPaginationSize = 5;
function fetchAndRenderDataSegments() {
let dcDist = fetchData(MILVUS_URI + "/_dc/dist", dc_dist);
let dnSegments = fetchData(MILVUS_URI + "/_dn/segments", dn_segments);
Promise.all([dnSegments, dcDist])
.then(([dnSegmentsData, dcDistData]) => {
if (dnSegmentsData && dcDistData) {
const { mergedSegments, notifications } = mergeSegments(dnSegmentsData, dcDistData);
dataSegments = mergedSegments;
renderSegments(mergedSegments, currentPage, rowsPerPage);
renderNotifications(notifications, 'notificationsDSegments');
}
})
.catch(error => {
handleError(error);
});
}
// Merge segments by matching segment IDs from dn_segments and dc_dist
function mergeSegments(dnSegments, dcDist) {
const merged = {};
const notifications = []; // Store notifications for unmatched segments
const dcSegmentIds = new Set(dcDist.segments.map(dcSegment => dcSegment.segment_id));
// Add dn_segments to merged and mark segments missing in dc_dist
dnSegments.forEach(segment => {
segment.node_id = "datanode-" + segment.node_id;
const segmentData = { ...segment };
if (!dcSegmentIds.has(segment.segment_id)) {
segmentData.notification = 'Not found in DataCoord';
notifications.push(segmentData); // Add to notifications
}
merged[segment.segment_id] = segmentData;
});
// Merge dc_dist segments or add new ones with notification if missing in dn_segments
dcDist.segments.forEach(dcSegment => {
dcSegment.node_id = "datacoord-" + dcSegment.node_id;
const id = dcSegment.segment_id;
if (merged[id]) {
merged[id] = { ...merged[id], ...dcSegment };
} else {
const segmentData = { ...dcSegment, notification: 'Not found in DataNode' };
if (dcSegment.state !== 'Dropped' && dcSegment.state !== 'Flushed'){
notifications.push(segmentData); // Add to notifications
}
merged[id] = segmentData;
}
});
return { mergedSegments: Object.values(merged), notifications };
}
// Render the merged segments in a paginated table
function renderSegments(segments, currentPage, rowsPerPage) {
const table = document.getElementById("dataSegmentsTableBody");
table.innerHTML = ""; // Clear previous rows
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const paginatedData = segments.slice(start, end);
paginatedData.forEach(segment => {
const row = document.createElement("tr");
const numRows = segment.state === "Growing" ? segment.flushed_rows : segment.num_of_rows;
row.innerHTML = `
${segment.segment_id}
${segment.collection_id}
${segment.partition_id}
${segment.channel}
${numRows || 'unknown'}
${segment.state}
${segment.level}
`;
table.appendChild(row);
});
// Update pagination info
const totalPages = Math.ceil(segments.length / rowsPerPage);
let paginationHTML = ' ';
document.getElementById('dsegmentPaginationControls').innerHTML = paginationHTML;
document.getElementById('dsegment_totalCount').innerText = `Total Segments: ${segments.length}`;
}
function fetchAndRenderTargets(currentPage = startPage, rowsPerPage = rowsPerPage) {
let currentTargets = fetchData(MILVUS_URI + "/qc/target", qcCurrentTargets);
let nextTargets = fetchData(MILVUS_URI + "/_qc/target?target_scope=2", qcNextTargets);
Promise.all([currentTargets, nextTargets])
.then(([currentTargetsData, nextTargetsData]) => {
if (currentTargetsData && nextTargetsData) {
const mergedSegments = mergeTargetSegments(currentTargetsData, nextTargetsData);
const mergedChannels = mergeTargetChannels(currentTargetsData, nextTargetsData);
renderTargetSegments(mergedSegments, currentPage, rowsPerPage);
renderTargetChannels(mergedChannels, currentPage, rowsPerPage);
}
})
.catch(error => {
handleError(error);
});
}
function mergeTargetSegments(currentTargets, nextTargets) {
const merged = [];
currentTargets.forEach(target => {
target.segments.forEach(segment => {
segment.targetScope = 'current';
merged.push(segment);
});
});
nextTargets.forEach(target => {
target.segments.forEach(segment => {
segment.targetScope = 'next';
merged.push(segment);
});
});
return merged;
}
function mergeTargetChannels(currentTargets, nextTargets) {
const merged = [];
currentTargets.forEach(target => {
if (target.dm_channels) {
target.dm_channels.forEach(channel => {
channel.targetScope = 'current';
merged.push(channel);
});
}
});
nextTargets.forEach(target => {
if (target.dm_channels) {
target.dm_channels.forEach(channel => {
channel.targetScope = 'next';
merged.push(channel);
});
}
});
return merged;
}
function renderTargetSegments(segments, currentPage, rowsPerPage) {
const table = document.getElementById("dataSegmentsTableBody");
table.innerHTML = ""; // Clear previous rows
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const paginatedData = segments.slice(start, end);
paginatedData.forEach(segment => {
const row = document.createElement("tr");
row.innerHTML = `
${segment.segment_id}
${segment.collection_id}
${segment.partition_id}
${segment.channel}
${segment.num_of_rows}
${segment.state}
${segment.targetScope}
`;
table.appendChild(row);
});
// Update pagination info
const totalPages = Math.ceil(segments.length / rowsPerPage);
let paginationHTML = ' ';
document.getElementById('segmentPaginationControls').innerHTML = paginationHTML;
document.getElementById('segment_totalCount').innerText = `Total Segments: ${segments.length}`;
}
function renderTargetChannels(channels, currentPage, rowsPerPage) {
const table = document.getElementById("dataChannelsTableBody");
table.innerHTML = ""; // Clear previous rows
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const paginatedData = channels.slice(start, end);
paginatedData.forEach(channel => {
const row = document.createElement("tr");
row.innerHTML = `
${channel.channel_name}
${channel.collection_id}
${channel.node_id}
${channel.version}
${channel.unflushed_segment_ids.join(', ')}
${channel.flushed_segment_ids.join(', ')}
${channel.dropped_segment_ids.join(', ')}
${channel.targetScope}
`;
table.appendChild(row);
});
// Update pagination info
const totalPages = Math.ceil(channels.length / rowsPerPage);
let paginationHTML = ' ';
document.getElementById('channelPaginationControls').innerHTML = paginationHTML;
document.getElementById('channel_totalCount').innerText = `Total Channels: ${channels.length}`;
}
function renderSlowQueries(data) {
let tableHTML = '' +
'Time ' +
'Trace ID ' +
'Request ' +
'User ' +
'Database ' +
'Collection ' +
'Parameters ' +
'Duration ' +
' ';
tableHTML += '';
data.forEach(query => {
tableHTML += '';
tableHTML += `${query.time} `;
tableHTML += `${query.trace_id} `;
tableHTML += `${query.type} `;
tableHTML += `${query.user || 'unknown'} `;
tableHTML += `${query.database} `;
tableHTML += `${query.collection} `;
tableHTML += `${JSON.stringify(query.query_params)} `;
tableHTML += `${query.duration} `;
tableHTML += ' ';
});
tableHTML += ' ';
document.getElementById('slowQueries').innerHTML = tableHTML;
document.getElementById('slowQueriesTotalCount').innerText = `Total Slow Requests: ${data.length}`;
}
const querySegmentsStartPage = 0;
const querySegmentsPaginationSize = 5;
var querySegments = null;
var queryChannels = null;
function fetchAndRenderQuerySegmentsAndChannels(currentPage = querySegmentsStartPage, rowsPerPage = querySegmentsPaginationSize) {
let qcDistData = fetchData(MILVUS_URI + "/_qc/dist", qcDist);
let qcCurrentTargetsData = fetchData(MILVUS_URI + "/_qc/target", qcCurrentTargets);
let qcNextTargetsData = fetchData(MILVUS_URI + "/_qc/target?target_scope=2", qcNextTargets);
let qnSegmentsData = fetchData(MILVUS_URI + "/_qn/segments", qnSegments);
let qnChannelsData = fetchData(MILVUS_URI + "/_qn/channels", qnChannels);
Promise.all([qcDistData, qcCurrentTargetsData, qcNextTargetsData, qnSegmentsData,qnChannelsData])
.then(([qcDistData, qcCurrentTargetsData, qcNextTargetsData, qnSegmentsData,qnChannelsData]) => {
const mergedSegments = mergeQuerySegments(qcDistData, qcCurrentTargetsData, qcNextTargetsData, qnSegmentsData);
querySegments = mergedSegments
renderQuerySegments(mergedSegments, currentPage, rowsPerPage);
document.getElementById('querySegmentsTotalCount').innerText = `Total Segments: ${mergedSegments.length}`;
renderQuerySegmentsPaginationControls(currentPage, Math.ceil(mergedSegments.length / rowsPerPage));
const mergedChannels = mergeQueryChannels(qcDistData, qcCurrentTargetsData, qcNextTargetsData, qnChannelsData);
queryChannels = mergedChannels
renderQueryChannels(mergedChannels, currentPage, rowsPerPage);
document.getElementById('queryChannelsTotalCount').innerText = `Total Channels: ${mergedChannels.length}`;
renderQueryChannelsPaginationControls(currentPage, Math.ceil(mergedChannels.length / rowsPerPage));
})
.catch(error => {
handleError(error);
});
}
function mergeQuerySegments(qcDist, qcCurrentTargets, qcNextTargets, qnSegments) {
const segments = [];
const addSegment = (segment, from, leaderId = null) => {
const existingSegment = segments.find(s => s.segment_id === segment.segment_id);
if (existingSegment) {
if (from) {
existingSegment.from += ` | ${from}`;
}
if (leaderId) {
existingSegment.leader_id = leaderId;
}
if (segment.state && segment.state !== 'SegmentStateNone') {
existingSegment.state = segment.state;
}
} else {
segments.push({ ...segment, from, leader_id: leaderId });
}
};
if (qcDist && qcDist.segments && qcDist.segments.length > 0) {
qcDist.segments.forEach(segment => {
addSegment(segment, 'DIST');
});
}
if (qcCurrentTargets && qcCurrentTargets.length >0 ) {
qcCurrentTargets.forEach(target => {
target.segments.forEach(segment => {
addSegment(segment, 'CT ');
});
});
}
if (qcCurrentTargets && qcCurrentTargets.length >0 ) {
qcCurrentTargets.forEach(target => {
target.segments.forEach(segment => {
addSegment(segment, 'NT');
});
});
}
if (qnSegments && qnSegments.length > 0) {
qnSegments.forEach(segment => {
addSegment(segment, 'QN');
});
}
if (qcDist && qcDist.leader_views && qcDist.leader_views.length > 0) {
qcDist.leader_views.forEach(view => {
if (view.sealed_segments && view.sealed_segments.length > 0) {
view.sealed_segments.forEach(segment => {
addSegment(segment, null, view.leader_id);
});
}
if (view.growing_segments && view.growing_segments.length > 0) {
view.growing_segments.forEach(segment => {
addSegment(segment, null, view.leader_id);
});
}
});
}
return segments;
}
function renderQuerySegments(segments, currentPage, rowsPerPage) {
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const paginatedSegments = segments.slice(start, end);
let tableHTML = `
Segment ID
Collection ID
Leader ID
Node ID
State
Rows
From
`;
paginatedSegments.forEach(segment => {
tableHTML += `
${segment.segment_id}
${segment.collection_id}
${segment.leader_id || 'Not Found'}
${segment.node_id}
${segment.state}
${segment.num_of_rows}
${segment.from}
`;
});
tableHTML += `
`;
document.getElementById('querySegmentsTable').innerHTML = tableHTML;
}
function renderQuerySegmentsPaginationControls(currentPage, totalPages) {
let paginationHTML = ' ';
document.getElementById('querySegmentsPagination').innerHTML = paginationHTML;
}
const queryChannelsStartPage = 0;
const queryChannelsPaginationSize = 5;
function renderQueryChannelsPaginationControls(currentPage, totalPages) {
let paginationHTML = ' ';
document.getElementById('queryChannelsPagination').innerHTML = paginationHTML;
}
function mergeQueryChannels(qcDist, qcCurrentTargets, qcNextTargets, qnChannels) {
const channels = [];
const addChannel = (channel, from, leaderId = null) => {
const existingChannel = channels.find(c => c.name === channel.name || c.name === channel.channel_name);
if (existingChannel) {
if (from) {
existingChannel.from += ` | ${from}`;
}
if (leaderId) {
existingChannel.leader_id = leaderId;
}
if (channel.watch_state && channel.watch_state !== 'Healthy' ) {
existingChannel.watch_state = channel.watch_state;
}
} else {
channels.push({ ...channel, from, leader_id: leaderId });
}
};
if (qcDist && qcDist.dm_channels && qcDist.dm_channels.length > 0) {
qcDist.dm_channels.forEach(channel => {
addChannel(channel, 'DIST');
});
}
if (qcCurrentTargets && qcCurrentTargets.length > 0) {
qcCurrentTargets.forEach(target => {
if (target.dm_channels) {
target.dm_channels.forEach(channel => {
addChannel(channel, 'CT ');
});
}
});
}
if (qcNextTargets && qcNextTargets.length > 0) {
qcNextTargets.forEach(target => {
if (target.dm_channels) {
target.dm_channels.forEach(channel => {
addChannel(channel, 'NT');
});
}
});
}
if (qnChannels && qnChannels.length > 0) {
qnChannels.forEach(channel => {
addChannel(channel, 'QN');
});
}
if (qcDist && qcDist.leader_views && qcDist.leader_views.length > 0) {
qcDist.leader_views.forEach(view => {
if (view.channel) {
addChannel({name: view.channel}, null, view.leader_id);
}
});
}
return channels;
}
function renderQueryChannels(channels, currentPage, rowsPerPage) {
const start = currentPage * rowsPerPage;
const end = start + rowsPerPage;
const paginatedChannels = channels.slice(start, end);
let tableHTML = `
Channel Name
Collection ID
Leader ID
Node ID
Watch State
From
`;
paginatedChannels.forEach(channel => {
tableHTML += `
${channel.name || channel.channel_name}
${channel.collection_id}
${channel.leader_id || 'Not Found'}
${channel.node_id}
${channel.watch_state||''}
${channel.from}
`;
});
tableHTML += `
`;
document.getElementById('queryChannelsTable').innerHTML = tableHTML;
}
function fetchAndRenderReplicas() {
fetchData(MILVUS_URI + "/_qc/replica", qcReplica)
.then(data => {
renderReplicas(data);
})
.catch(error => {
handleError(error);
});
}
function renderReplicas(replicas) {
if (!replicas || replicas.length === 0) {
return
}
const table = document.getElementById('replicasTable');
table.innerHTML = ''; // Clear the table
// Render table headers
const headers = ['ID', 'Collection ID', 'RW Nodes', 'RO Nodes', 'Resource Group'];
const headerRow = document.createElement('tr');
headers.forEach(header => {
const th = document.createElement('th');
th.textContent = header;
headerRow.appendChild(th);
});
table.appendChild(headerRow);
// Render table rows
replicas.forEach(replica => {
const row = document.createElement('tr');
row.innerHTML = `
${replica.ID}
${replica.collectionID}
${replica.rw_nodes? replica.rw_nodes.join(', '): ''}
${replica.ro_nodes? replica.ro_nodes.join(', '): ''}
${replica.resource_group}
`;
table.appendChild(row);
});
}
function fetchAndRenderResourceGroup() {
fetchData(MILVUS_URI + "/_qc/resource_group", qcResourceGroup)
.then(data => {
renderResourceGroup(data);
})
.catch(error => {
console.error('Error fetching resource group data:', error);
});
}
function renderResourceGroup(data) {
if (!data || data.length === 0) {
return
}
const table = document.getElementById('resourceGroupTable');
table.innerHTML = ''; // Clear existing table content
// Create table headers
const headerRow = document.createElement('tr');
const headers = ['Name', 'Nodes', 'Cfg'];
headers.forEach(headerText => {
const header = document.createElement('th');
header.textContent = headerText;
headerRow.appendChild(header);
});
table.appendChild(headerRow);
// Populate table rows with data
data.forEach(resourceGroup => {
const row = document.createElement('tr');
const nameCell = document.createElement('td');
nameCell.textContent = resourceGroup.name;
row.appendChild(nameCell);
const nodesCell = document.createElement('td');
nodesCell.textContent = resourceGroup.nodes.join(', ');
row.appendChild(nodesCell);
const cfgCell = document.createElement('td');
cfgCell.textContent = resourceGroup.cfg? JSON.stringify(resourceGroup.cfg) : '';
row.appendChild(cfgCell)
table.appendChild(row);
});
}