Skip to content

Commit

Permalink
Fix issues with PerformanceReport Page (commit data, hiding missing d…
Browse files Browse the repository at this point in the history
…ata) and increase Axios timeout. Additional null handling and performance improvements
  • Loading branch information
Travis Stark committed Jan 3, 2025
1 parent 55ee5f4 commit 3217aae
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 98 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"react": "^18.2.0",
"react-apexcharts": "^1.4.0",
"react-dom": "^18.2.0",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.8.1",
"react-scripts": "5.0.1",
"recoil": "^0.7.6",
Expand Down
3 changes: 2 additions & 1 deletion src/common/Axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import axios from 'axios';

export const AxiosConfig = axios.create({
baseURL: process.env.REACT_APP_LAMBDA_URL,
timeout: 3000,
// timeout in milliseconds; increased from 3000ms due to large number of commit data requests
timeout: 4000,
headers: {
'Content-Type': 'application/json',
},
Expand Down
4 changes: 1 addition & 3 deletions src/common/Constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

export const USE_CASE: string[] = ['statsd', 'logs', 'disk'];
export const REPORTED_METRICS: string[] = [
'cpu_usage',
'procstat_cpu_usage',
'procstat_memory_rss',
'procstat_memory_swap',
Expand All @@ -18,8 +17,7 @@ export const TRANSACTION_PER_MINUTE: number[] = [100, 1000, 5000];
export const OWNER_REPOSITORY: string = 'aws';
export const SERVICE_NAME: string = 'AmazonCloudWatchAgent';
export const CONVERT_REPORTED_METRICS_NAME: { [metric_name: string]: string } = {
cpu_usage: 'CPU Usage',
procstat_cpu_usage: 'Procstat CPU Usage',
procstat_cpu_usage: 'CPU Usage',
procstat_memory_rss: 'Memory Resource',
procstat_memory_swap: 'Memory Swap',
procstat_memory_vms: 'Virtual Memory',
Expand Down
6 changes: 4 additions & 2 deletions src/containers/PerformanceReport/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ export interface PerformanceMetricReport {
};

UseCase: { S: string };
Service: { S: string };
UniqueID: { S: string };
}

// PerformanceMetric shows all collected metrics when running performance metrics
export interface PerformanceMetric {
cpu_usage?: { M: PerformanceMetricStatistic };
procstat_cpu_usage?: { M: PerformanceMetricStatistic };
procstat_memory_rss?: { M: PerformanceMetricStatistic };
procstat_memory_swap?: { M: PerformanceMetricStatistic };
Expand All @@ -72,13 +73,15 @@ export interface PerformanceMetricStatistic {
export interface ServiceLatestVersion {
// Release version for the service
tag_name: string;
body: string;
}

export interface ServicePRInformation {
// Release version for the service
title: string;
html_url: string;
number: number;
sha: string;
}

export interface UseCaseData {
Expand All @@ -87,7 +90,6 @@ export interface UseCaseData {
instance_type?: string;
data: {
[data_rate: string]: {
cpu_usage?: string;
procstat_cpu_usage?: string;
procstat_memory_rss?: string;
procstat_memory_swap?: string;
Expand Down
102 changes: 71 additions & 31 deletions src/containers/PerformanceReport/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT
import { CircularProgress, Container, Link, MenuItem, Paper, Select, Table, TableBody, TableCell, TableContainer, TableRow, Typography } from '@mui/material';
import { Button, CircularProgress, Container, Link, MenuItem, Paper, Select, Table, TableBody, TableCell, TableContainer, TableRow, Typography } from '@mui/material';
import moment from 'moment';
import * as React from 'react';
import { TRANSACTION_PER_MINUTE } from '../../common/Constant';
import { usePageEffect } from '../../core/page';
import { PerformanceTable } from '../../core/table';
import { UseCaseData } from './data';
import { GetLatestPerformanceReports, GetServiceLatestVersion } from './service';
import { ServicePRInformation, UseCaseData } from './data';
import { createDefaultServicePRInformation, GetLatestPerformanceReports, GetServiceLatestVersion, GetServicePRInformation } from './service';
import { PasswordDialog } from '../../common/Dialog';
import { SelectChangeEvent } from '@mui/material/Select';
import ReactMarkdown from 'react-markdown';

export default function PerformanceReport(props: { password: string; password_is_set: boolean; set_password_state: any }): JSX.Element {
usePageEffect({ title: 'Amazon CloudWatch Agent' });
const { password, password_is_set, set_password_state } = props;
const [{ version, commit_date, commit_title, commit_hash, commit_url, use_cases, ami_id, collection_period }] = useStatePerformanceReport(password);
const [{ version, commit_date, commit_title, commit_hash, commit_url, use_cases, ami_id, collection_period, body }] = useStatePerformanceReport(password);
const [{ data_type }, setDataTypeState] = useStateDataType();
const [isHidden, setIsHidden] = React.useState(true);

const handleDataTypeChange = (event: SelectChangeEvent) => {
setDataTypeState({ data_type: event.target.value });
};

const toggleContent = () => {
setIsHidden(!isHidden);
};
const selectedUseCaseData: UseCaseData[] = use_cases.filter((useCase: UseCaseData) => useCase?.data_type?.toLowerCase() === data_type.toLowerCase());

return (
<Container>
Expand Down Expand Up @@ -56,7 +69,7 @@ export default function PerformanceReport(props: { password: string; password_is
aria-label="a dense table"
>
<TableBody>
{['Version', 'Architectural', 'Collection Period', 'Testing AMI', 'Commit Hash', 'Commit Name', 'Commit Date', 'Data Type']?.map((name) => (
{['Version', 'Architectural', 'Collection Period', 'Testing AMI', 'Commit Hash', 'Commit Name', 'Commit Date', 'Data Type', 'Release Notes']?.map((name) => (
<TableRow key={name}>
<TableCell
sx={{
Expand All @@ -69,7 +82,6 @@ export default function PerformanceReport(props: { password: string; password_is
<TableCell
sx={{
border: '1px solid #000',
textAlign: 'center',
}}
>
{name === 'Version' ? (
Expand All @@ -88,24 +100,33 @@ export default function PerformanceReport(props: { password: string; password_is
</Link>
) : name === 'Commit Date' ? (
<Typography variant="h4">{commit_date}</Typography>
) : (
<Select
sx={{ height: '41px' }}
value={data_type}
onChange={(e: {
target: {
value: string;
};
}) =>
setDataTypeState({
data_type: e.target.value,
})
}
>
) : name === 'Data Type' ? (
<Select sx={{ height: '41px' }} value={data_type} onChange={handleDataTypeChange}>
<MenuItem value={'Metrics'}>Metric</MenuItem>
<MenuItem value={'Traces'}>Trace</MenuItem>
<MenuItem value={'Logs'}>Logs</MenuItem>
</Select>
) : (
<div>
<Button
onClick={toggleContent}
className="toggle-button"
variant="outlined"
sx={{
marginBottom: 0,
backgroundColor: '#ffffff',
borderColor: '#333333',
color: '#333333',
'&:hover': {
backgroundColor: '#f5f5f5',
borderColor: '#333333',
},
}}
>
{isHidden ? 'Show release notes' : 'Hide release notes'}
</Button>
{!isHidden && <ReactMarkdown className="markdown-content">{body}</ReactMarkdown>}
</div>
)}
</TableCell>
</TableRow>
Expand All @@ -120,7 +141,7 @@ export default function PerformanceReport(props: { password: string; password_is
<Typography sx={{ mb: 2, fontWeight: 'bold' }} variant="h3">
{data_type} (TPM: {tpm}){' '}
</Typography>
<PerformanceTable data_rate={String(tpm)} use_cases={use_cases.filter((use_case: UseCaseData) => use_case?.data_type === data_type.toLowerCase())} />
<PerformanceTable key={data_type} data_rate={String(tpm)} use_cases={selectedUseCaseData} />
</Container>
))}
</Container>
Expand All @@ -140,6 +161,7 @@ function useStatePerformanceReport(password: string) {
ami_id: undefined as string | undefined,
collection_period: undefined as string | undefined,
error: undefined as string | undefined,
body: undefined as string | undefined,
});

React.useEffect(() => {
Expand All @@ -155,8 +177,9 @@ function useStatePerformanceReport(password: string) {

const use_cases: UseCaseData[] = [];
// We only get the latest commit ID; therefore, only use case are different; however, general metadata
// information (e.g Commit_Hash, Commit_Date of the PR) would be the same for all datas.
// information (e.g Commit_Hash, Commit_Date of the PR) would be the same for all data.
const commit_hash = performance_reports.at(0)?.CommitHash.S || '';
const commitHashes = performance_reports.map((report) => report.CommitHash?.S);
const commit_date = performance_reports.at(0)?.CommitDate.N;
const collection_period = performance_reports.at(0)?.CollectionPeriod.S;
const ami_id = performance_reports.at(0)?.InstanceAMI.S;
Expand All @@ -166,11 +189,10 @@ function useStatePerformanceReport(password: string) {
name: pReport?.UseCase.S,
data_type: pReport?.DataType.S,
instance_type: pReport?.InstanceType.S,
data: TRANSACTION_PER_MINUTE.reduce(
data: Object.keys(pReport?.Results.M).reduce(
(accu, tpm) => ({
...accu,
[tpm]: {
cpu_usage: pReport?.Results.M[tpm]?.M?.cpu_usage?.M?.Average?.N,
procstat_cpu_usage: pReport?.Results.M[tpm]?.M?.procstat_cpu_usage?.M?.Average?.N,
procstat_memory_rss: pReport?.Results.M[tpm]?.M?.procstat_memory_rss?.M?.Average?.N,
procstat_memory_swap: pReport?.Results.M[tpm]?.M?.procstat_memory_swap?.M?.Average?.N,
Expand All @@ -187,26 +209,44 @@ function useStatePerformanceReport(password: string) {
),
});
}
// const commit_info = await GetServicePRInformation(password, commit_hash);
const commit_info: ServicePRInformation[] = await GetServicePRInformation(password, commitHashes);
const commit_info_finalized = commit_info.find((value) => value !== undefined) ?? createDefaultServicePRInformation();

setState((prev: any) => ({
...prev,
version: service_info.tag_name,
ami_id: ami_id,
collection_period: collection_period,
use_cases: use_cases,
// commit_title: `${commit_info?.title} (#${commit_info?.number})`,
// commit_url: commit_info?.html_url,
commit_hash: commit_hash,
commit_title: `PlaceHolder`,
commit_url: `www.github.com/aws/amazon-cloudwatch-agent`,
commit_date: moment.unix(Number(commit_date)).format('dddd, MMMM Do, YYYY h:mm:ss A'),
commit_title: `${commit_info_finalized?.title} (#${commit_info_finalized?.number})`,
commit_url: commit_info_finalized?.html_url,
commit_hash: commit_info_finalized?.sha ?? commit_hash,
commit_date: formatUnixTimestamp(commit_date ?? 0),
body: service_info.body ?? 'Release notes unavailable',
}));
})();
}, [password, setState]);
return [state, setState] as const;
}

export const formatUnixTimestamp = (timestamp: string | number, format: string = 'dddd, MMMM Do, YYYY h:mm:ss A'): string => {
try {
// Handle string input
const unixTime = typeof timestamp === 'string' ? Number(timestamp) : timestamp;

// Validate timestamp
if (!Number.isFinite(unixTime) || unixTime < 0) {
console.log('invalid unix timestamp:');
return moment.unix(0).format(format);
}

return moment.unix(unixTime).format(format);
} catch (error) {
console.error('Error formatting unix timestamp:', error);
return moment.unix(0).format(format);
}
};

function useStateDataType() {
const [state, setState] = React.useState({
data_type: 'Metrics' as 'Metrics' | 'Traces' | 'Logs' | string,
Expand Down
49 changes: 33 additions & 16 deletions src/containers/PerformanceReport/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,38 @@ export async function GetServiceLatestVersion(password: string): Promise<Service
});
}

export async function GetServicePRInformation(password: string, commit_sha: string): Promise<ServicePRInformation> {
AxiosConfig.defaults.headers['x-api-key'] = password;
return AxiosConfig.post('/', {
Action: 'Github',
URL: 'GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls',
Params: {
owner: OWNER_REPOSITORY,
repo: process.env.REACT_APP_GITHUB_REPOSITORY,
commit_sha: commit_sha,
},
})
.then(function (body: { data: any[] }) {
return Promise.resolve(body.data.at(0));
})
.catch(function (error: unknown) {
return Promise.reject(error);
export async function GetServicePRInformation(password: string, commitHashes: string[]): Promise<ServicePRInformation[]> {
try {
AxiosConfig.defaults.headers['x-api-key'] = password;
const prInformation = commitHashes.map(async (commitHash) => {
const result = await AxiosConfig.post('/', {
Action: 'Github',
URL: 'GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls',
Params: {
owner: OWNER_REPOSITORY,
repo: process.env.REACT_APP_GITHUB_REPOSITORY,
commit_sha: commitHash,
},
});
if (result.data?.data?.length === undefined) {
console.log('PR Info not found for: ' + commitHash);
return undefined;
}

return result.data?.data?.at(0);
});

return Promise.all(prInformation);
} catch (error) {
return Promise.reject(error);
}
}

export function createDefaultServicePRInformation(): ServicePRInformation {
return {
title: 'PR data unavailable',
html_url: 'https://github.com/aws/amazon-cloudwatch-agent/pulls',
number: 0,
sha: 'default-sha',
};
}
8 changes: 6 additions & 2 deletions src/containers/PerformanceTrend/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,13 @@ export interface ServiceCommitInformation {
// Release version for the service
author: {
login: string;
date: string;
};
commit: { message: string };
commit: {
message: string;
author: {
date: string;
};
};
sha: string;
}

Expand Down
Loading

0 comments on commit 3217aae

Please sign in to comment.