I am trying to make 7 api calls right away when the component mounts and get data quickly, then keep on making an API call for each of the 3 methods to getTemp, getGpu, getMemory. Although it's taking about 20 seconds to get 1 API call.
I am looking at my backend and everything work fine, the calls are received and responded correctly
I tried to remove asyc calls but that didn't work
Explained in details: Right now:
Refresh page -> backend API gets hit with 3 calls -> nothing is shown in the console or browser
20 seconds later -> all my state arrays are empty
Circa 25 seconds later -> arrays have 1 entry
1 second later -> arrays start to have multiple entries as they should and works fine
What I want to happen:
- Hit the refresh button -> Server API needs to be hit 7 times
- Right after that make a call every 1 second all the time
- Don't want to wait 20 or 25 seconds for first array to populate
From Network tabs I keep getting all the API calls pending:
Eventually it clears up but it takes between 2 and 3 mins to start clearing up
Can you help with making the 7 API calls once only and making 1 api call every second?
import { Card, Metric, CategoryBar, Flex, Text, AreaChart, ProgressBar, Title, LineChart } from "@tremor/react";
import card_1 from '../assets/cards/card_1.json'
import card_2 from '../assets/cards/card_2.json'
import card_3 from '../assets/cards/card_3.json'
import card_4 from '../assets/cards/card_4.json'
import React, { useState, useEffect } from 'react';
import axios from 'axios';
var all_cards_array = [card_1, card_2, card_3, card_4]
var array_of_temperatures = []
var array_of_gpu_usage = []
var array_of_gpu_memory = []
const array_of_colors = [
"slate",
"gray",
"zinc",
"neutral",
"stone",
"red",
"orange",
"amber",
"yellow",
"lime",
"green",
"emerald",
"teal",
"cyan",
"sky",
"blue",
"indigo",
"violet",
"purple",
"fuchsia",
"pink",
"rose"
];
var startTime, endTime;
var optionCelsius = {
style: 'unit',
unit: 'celsius'
};
var optionPercent = {
style: 'percent',
minimumFractionDigits: 0,
maximumFractionDigits: 0
};
axios.defaults.baseURL = 'http://url';
const valueFormatterPercentage = (number) => `${new Intl.NumberFormat("en-US", optionPercent).format(number / 100).toString()}`;
const valueFormatterCelcius = (number) => `${new Intl.NumberFormat("en-US", optionCelsius).format(number).toString()}`;
const Dashboard = () => {
const [temperatures, setTemperatures] = useState(null);
const [gpuUsage, setGpuUsage] = useState(null);
const [gpuMemory, setGpuMemory] = useState(null);
const [count, setCount] = useState(10);
const getTemps = async () => {
try {
const { data } = await axios.get('/temperature_info');
await setTemperatures(data);
converTemperatureJsonToArray()
console.log('Temps')
console.log(array_of_temperatures)
console.log(temperatures)
return data
} catch (err) {
console.error(err);
}
};
const getGpuUsage = async () => {
try {
const { data } = await axios.get('/gpu_usage_info');
await setGpuUsage(data);
converGpuJsonToArray()
console.log('Gpu Usage')
console.log(array_of_gpu_usage)
console.log(gpuUsage)
return data
} catch (err) {
console.error(err);
}
};
const getGpuMemory = async () => {
try {
const { data } = await axios.get('/memory_usage_info');
await setGpuMemory(data);
converMemoryJsonToArray()
console.log('Memory')
console.log(array_of_gpu_memory)
console.log(gpuMemory)
return data
} catch (err) {
console.error(err);
}
};
const countDown = () => {
const interval = setInterval(function () {
setCount((prev) => prev - 1);
}, 1000);
};
useEffect(() => {
getTemps()
getGpuUsage()
getGpuMemory()
gpuUsageKeys()
gpuMemoryKeys()
temperaturesKeys()
// countDown()
}, [temperatures, gpuUsage, gpuMemory]);
function start() {
startTime = new Date();
}
function end() {
endTime = new Date();
var timeDiff = endTime - startTime; //in ms
// strip the ms
timeDiff /= 1000;
// get seconds
var seconds = Math.round(timeDiff);
console.log(seconds + " seconds");
}
function select_random_color(array_lenght) {
const list_colors = []
for (i in array_lenght) {
list_colors.push(array_of_colors[Math.floor(Math.random() * array_lenght)])
}
return list_colors
}
function temperaturesKeys() {
if (temperatures) {
console.log('In keys temperatures')
var list_keys = []
for (const key of Object.keys(temperatures)) {
if (key !== 'time_stamp') {
list_keys.push(key)
}
}
return list_keys
}
return null
}
function gpuUsageKeys() {
if (gpuUsage) {
console.log('In keys gpu')
var list_keys = []
for (const key of Object.keys(gpuUsage)) {
if (key !== 'time_stamp') {
list_keys.push(key)
}
}
return list_keys
}
return null
}
function gpuMemoryKeys() {
if (gpuMemory) {
console.log('In keys memory')
var list_keys = []
for (const key of Object.keys(gpuMemory)) {
if (key !== 'time_stamp') {
list_keys.push(key)
}
}
return list_keys
}
return null
}
function getTemperatureFormattedJson() {
const currentDate = new Date().toLocaleTimeString()
const dict = {}
for (const key of Object.keys(temperatures)) {
if (key === 'time_stamp') {
dict[key] = currentDate
} else {
dict[key] = temperatures[key]
}
}
return dict
}
function getGpuFormattedJson() {
const currentDate = new Date().toLocaleTimeString()
const dict = {}
for (const key of Object.keys(gpuUsage)) {
if (key === 'time_stamp') {
dict[key] = currentDate
} else {
dict[key] = gpuUsage[key]
}
}
return dict
}
function getMemoryFormattedJson() {
const currentDate = new Date().toLocaleTimeString()
const dict = {}
for (const key of Object.keys(gpuMemory)) {
if (key === 'time_stamp') {
dict[key] = currentDate
} else {
dict[key] = gpuMemory[key]
}
}
return dict
}
function converTemperatureJsonToArray() {
if (temperatures) {
if (array_of_temperatures.length > 7) {
array_of_temperatures.splice(0, 1)
}
array_of_temperatures.push(getTemperatureFormattedJson())
return array_of_temperatures
}
return [""]
}
function converGpuJsonToArray() {
if (gpuUsage) {
if (array_of_gpu_usage.length > 7) {
array_of_gpu_usage.splice(0, 1)
}
array_of_gpu_usage.push(getGpuFormattedJson())
return array_of_gpu_usage
}
return [""]
}
function converMemoryJsonToArray() {
if (gpuMemory) {
if (array_of_gpu_memory.length > 7) {
array_of_gpu_memory.splice(0, 1)
}
array_of_gpu_memory.push(getMemoryFormattedJson())
return array_of_gpu_memory
}
return [""]
}
function renderBox() {
return (
<div className={color}>
</div>
)
}
function uuidv4() {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
function renderGPUName(cardName) {
return (
<div key={uuidv4()}>
<h2 className='text-2xl font-bold'>
Gpu Name
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{cardName}</Metric>
</Card>
</div>
)
}
function renderCards() {
const cards = []
var div_top_black = 'black'
var div_top_blue = 'grid grid-cols-5 gap-14 blue'
const list_colors = [div_top_black, div_top_blue]
var count = 0
for (const card of all_cards_array) {
var color = ''
if (count == 0) {
color = list_colors[0]
count += 1
} else {
color = list_colors[1]
count = 0
}
cards.push(
<div key={uuidv4()}>
<br />
<br />
<br />
<br />
</div>
)
cards.push(
<div className={color} key={uuidv4()}>
</div>
)
cards.push(renderGPUName(card.name))
cards.push(
<div key={uuidv4()}>
<br />
<br />
<br />
<br />
</div>
)
cards.push(
<div className='grid grid-cols-5 gap-14' key={uuidv4()}>
<div>
<h2 className='text-2xl font-bold'>
Driver Version
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.driver_version}</Metric>
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
P State
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.pstate}</Metric>
</Card>
</div>
<div>
<h2 className='text-lg font-bold'>
Pcie Link Gen Max
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.pcie_link_gen_max}</Metric>
</Card>
</div>
<div>
<h2 className='text-lg font-bold'>
Pcie Link Gen Current
</h2>
<br />
<Card className="max-w-xs mx-auto" decoration="top" decorationColor="indigo">
<Metric>{card.pcie_link_gen_current}</Metric>
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Temperature
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card.temperature_gpu}C</Text>
<Text>200</Text>
</Flex>
<ProgressBar value={card.temperature_gpu} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Utilization Gpu
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["utilization_gpu [%]"]} %</Text>
<Text>100</Text>
</Flex>
<ProgressBar value={card["utilization_gpu [%]"]} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-xl font-bold'>
Utilization Memory
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["utilization_memory [%]"]}%</Text>
<Text>100</Text>
</Flex>
<ProgressBar value={card["utilization_memory [%]"]} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Memory Total
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["memory_total [MiB]"]} MiB</Text>
<Text>{card["memory_total [MiB]"]}</Text>
</Flex>
<ProgressBar value={card["memory_total [MiB]"]} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Memory Free
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["memory_free [MiB]"]} MiB</Text>
<Text>{card["memory_total [MiB]"]}</Text>
</Flex>
<ProgressBar value={card["memory_free [MiB]"] / card["memory_total [MiB]"] * 100} color="red" className="mt-3" />
</Card>
</div>
<div>
<h2 className='text-2xl font-bold'>
Memory Used
</h2>
<br />
<Card className="max-w-sm mx-auto">
<Flex>
<Text>0 • {card["memory_used [MiB]"]} MiB</Text>
<Text>{card["memory_total [MiB]"]}</Text>
</Flex>
<ProgressBar value={card["memory_used [MiB]"] / card["memory_total [MiB]"] * 100} color="green" className="mt-3" />
</Card>
<br />
<br />
</div>
</div>
)
}
return cards
}
return (
<>
<div className='grid grid-cols-1 gap-14'>
{array_of_temperatures.length > 7 ? 'Fetched' : 'Loading... ' + count + ' seconds left'}
<br />
<br />
<br />
<br />
<Card>
<Title>Cards Temperature</Title>
<LineChart
className="mt-6"
data={array_of_temperatures.length > 7 ? array_of_temperatures : null}
index="time_stamp"
categories={temperatures ? temperaturesKeys() : []}
colors={array_of_temperatures > 0 ? select_random_color(temperaturesKeys().length - 1) : ["neutral", "indigo", "orange", "green"]}
valueFormatter={valueFormatterCelcius}
yAxisWidth={100}
/>
</Card>
</div>
<br />
<br />
<div className='grid grid-cols-1 gap-14'>
<Card>
<Title>Cards Gpu Usage</Title>
<LineChart
className="mt-6"
data={array_of_gpu_usage.length > 7 ? array_of_gpu_usage : null}
index="time_stamp"
categories={gpuUsage ? gpuUsageKeys() : []}
colors={array_of_gpu_usage > 0 ? select_random_color(gpuUsageKeys().length - 1) : ["neutral", "indigo", "orange", "green"]}
valueFormatter={valueFormatterPercentage}
yAxisWidth={100}
/>
</Card>
</div>
<br />
<br />
<div className='grid grid-cols-1 gap-14'>
<Card>
<Title>Cards Memory Usage</Title>
<LineChart
className="mt-6"
data={array_of_gpu_memory.length > 7 ? array_of_gpu_memory : null}
index="time_stamp"
categories={gpuMemory ? gpuMemoryKeys() : []}
colors={array_of_gpu_memory > 0 ? select_random_color(gpuMemoryKeys().length - 1) : ["neutral", "indigo", "orange", "green"]}
valueFormatter={valueFormatterPercentage}
yAxisWidth={100}
/>
</Card>
</div>
<br />
<br />
{renderCards()}
</>
)
};
export default Dashboard;
I think I had to step back and see a different approach to this. I am adding below some references to justify my response.
First, we need to break the "If the only tool you have is a hammer, you tend to see every problem as a nail"
So when to use api calls and webhooks?
This is a really good article that set me on the right path: Webhooks, Api Calls, Push Sub, and Websockets differences
Second, let's implement the websocket:
That's it! all it needed it's a websocket, you can use a setTimeout to set a delay if you want and you can use multiple useEffect() with [] to run once multiple times if you want
To @Leroy you are using polling, which in this case is the wrong approach, I know I was thinking the same way, then it hit me. Polling will always be slow, webhooks will not work either as it will be slow too since there is a call every 1 second. A websocket is opened and stays open until you close it, continuously gives you data and you can either establish a timeout server or client side. In this case it works perfectly.
Also:
In react 18 using the dom directly is deprecated