How did it start?
Back in 2015, I was working on a product, the tech stack I used was React & Flux.
The product requirement was to create a dashboard having multiple charts to plot different sets of data.
The chart has to show thousand of data points depending upon the time range.
Drawing thousands of the data points on multiple charts meaning,
- Larger DOM size
- Increasing paint, and
- Blocking the main thread.
Altogether it’s quite expensive and if you have a computer with the low configuration your system will start freezing or the browser will crash.
The same problem I faced while doing development, I tried to render the charts, my browser crashed after a few seconds of page load.
I started debugging the issue, opened developer tools, and found out the reason for the crash.
There were thousands of SVG elements in the DOM.
I was using the C3 library which uses SVG to draw charts and if you have thousands of data points to paint, your DOM size will increase, so as the CPU usage, causing browser crash.
Make use of Canvas.
For any problem, we always take help from google and found that I can use canvas to draw charts as it does not increase DOM size.
Discovered this library called chart.js, it paints the chart on canvas and does not increase the DOM size and elements.
I started using chart.js, but our requirement was a little different I wanted to update our data points in the chart every 5 minutes like live streaming.
Unfortunately, the version of the library doesn’t support updating the live charts and I stopped using chart.js.
HighCharts — The savior
I found highCharts, this library had all the features I wanted in my product also it can support live data streaming on charts.
This library could plot a million data points on a graph.
I started using it and the results were great, I was excited as half of my job was done, but, it was not the end of my problems.
To draw thousands of data points, I had to make thousands of requests.
A dashboard with date picker, where you select a start date as 1st of Jan 2020 and end date as 31 Jan 2020, and you want to draw a chart with every 5 minutes interval in a month.
This means there will be 43800(approx) data points needs to be drawn on a chart
If you make a backend API call from the client (UI) with the start and end-date mentioned above, the API response time will be too high since the backend will process the data for every 5 minutes for each day and then sending back the response to UI, which means your end-user will have to wait for minutes or hours to see the real data on charts.
How did I fix it?
To reduce API response time, I thought to make the ajax request from the client (UI) for every 5 minutes interval.
It helped me to reduce the API response time, so now we can draw the chart faster.
But my problems didn’t end here,
I need to run the for loop, do some computation on the client, process the data, and pass it to high-chart, which was causing my main thread to block for a longer period causing browser crash.
How Web Worker helped us?
I read online about how to reduce main thread time and got I can make use of Web Workers
If you want to know how a web worker works please read here.
The pool of Web Workers
You can start a web worker from the main thread, but as shared above I wanted to make multiple requests for every 5-minute interval to reduce API load time.
So I needed a pool of web workers, where each worker will make different ajax (API) requests, example
Say, I created one web worker A, this worker will make an API call to get the data for the first 5 minutes of the start date and soon as API response is available, the A worker will post the data back to the main thread
Check the implementation
As you can check we are running a while-loop up to 60, which will create 60 web workers from the main thread, and we are increasing our start and end time by every 5 minutes. In the worker file, I am making the ajax call for every 5 minutes interval.
With the above approach, I figured out how to create multiple requests from the worker thread.
But there was one more problem, the data from the backend API needs to be restructured as required by highcharts, and the processing of the data was still blocking my main thread.
So I defined the worker type
- One of the types of workers responsible for API calls
- Another one will be responsible for processing data
Now the system had multiple workers making multiple requests, Worker A gets the data from API, it posts the data to the main thread, main thread receiving the message from worker A, it creates a new worker thread say A1 which will perform computation and process the data for my charts.
This is how I created a pool of web workers, doing different operations.
The problem was not yet resolved since the API takes 55–58 seconds to get the complete data for all intervals and we wanted to get it done in less than 10 seconds.
Increase the hostname reduces your load time.
As of now, I was available with 60 web workers working together as shown in the image above.
But I was using http1.1 and the browser can only allow 6 requests per host.
I thought to increased my hostname up to 10 and changed my approach a little bit,
The system was capable of using 60 web workers, and I thought to make use of one web worker to make the request on one hostname and the second web worker to make the request on the second.
Worker A will make an ajax call on
Worker B will make an ajax call on
Worker C will make an ajax call on
We have an array of
serverIp which has multiple URL’s starting from
Each worker can make a request to a different subdomain and get the data.
Each of these domains resolves to the same server, and the Web browser will open 6 connections to each.
With this approach, we almost reduced the load time and the complete API request and response operation was done within 10–15 seconds.
Also, we cache the API response for every interval which helped in reducing API response time.
This problem took me almost a month to figure out which libraries to use, how to reduce main thread work, DOM size, etc.
Here is the complete workflow, how each worker connected and communicated with the main thread.
I hope you enjoyed reading this article.
P.S. 👋 Hi, I’m Aatif! If you liked this, be sure to follow me on twitter!