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 => ` `).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 = ` `; paginatedSegments.forEach(segment => { tableHTML += ` `; }); tableHTML += `
Segment ID Collection ID Leader ID Node ID State Rows From
${segment.segment_id} ${segment.collection_id} ${segment.leader_id || 'Not Found'} ${segment.node_id} ${segment.state} ${segment.num_of_rows} ${segment.from}
`; 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 = ` `; paginatedChannels.forEach(channel => { tableHTML += ` `; }); tableHTML += `
Channel Name Collection ID Leader ID Node ID Watch State From
${channel.name || channel.channel_name} ${channel.collection_id} ${channel.leader_id || 'Not Found'} ${channel.node_id} ${channel.watch_state||''} ${channel.from}
`; 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); }); }