My intention is to utilize ng-grid for visualizing high-frequency real-time data, but I am encountering issues with a memory leak. Interestingly, the memory leak does not occur when I opt for a simple HTML table with ng-repeat.
My tech stack includes node+express on the backend and AngularJS on the client side. For streaming real-time data from the server to a client-side table, I make use of socket.io.
To illustrate the memory problem, consider this simplified example:
I am sending 1500 messages per second, each message formatted as follows: {id: 1, name: “name”, time: “[current date/time string]”} After 4 minutes, the browser memory exceeds 400MiB, reaching over 1GiB after 10 minutes.
Testing has been conducted on Chrome and Firefox.
Below is the simplified example. Is there something incorrect in my approach? (Additional details provided at the end).
Server
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
io.of('/test').on('connection', function (socket) {
console.log('socket connection: /test');
for (id=1; id<16; id++) {
streamData(id);
}
function streamData(id) {
setInterval(function () {
socket.emit('updateData', {
id: id,
name: "test"+id,
time: (new Date()).toString()
});
}, 10);
}
});
Service using angular-socket-io
factory('testSocket', function(socketFactory) {
return socketFactory({
ioSocket: io.connect('http://localhost/test')
});
})
Controller
controller('NgGridTestCtrl', function ($scope, testSocket) {
var itemsObj = {};
$scope.items = [];
$scope.gridOptions = {
data: 'items',
columnDefs: [{field:'id', displayName:'ID', width:'15%'},
{field:'name', displayName:'Name', width:'20%'},
{field:'time', displayName:'Date and Time', width:'65%'}],
enableColumnResize: true
};
testSocket.on('updateData', function(data) {
itemsObj[data.id] = data;
var values = [];
angular.forEach(itemsObj, function(value, index) {
this.push(value);
}, values);
// Data for ng-grid
$scope.items = values;
});
});
ngGrid Template
<div>
<h1>ng-grid Table</h1>
<div class="gridStyle" ng-grid="gridOptions"></div>
</div>
Edited to include Plain Table Example
Using a plain table resolves the memory issue - the browser memory remains around 155MiB.
Controller
controller('SimpleTableCtrl', function ($scope, testSocket) {
$scope.items = {};
testSocket.on('updateData', function(data) {
$scope.items[data.id] = data;
});
}).
Plain Table Template
<div>
<h1>Simple Table with ng-repeat</h1>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.time}}</td>
</tr>
</tbody>
</table>
Additional Observations
- The memory issue extends beyond ng-grid, also arising with the "NgGridTestCtrl" controller using the "plain table template" with ng-repeat.
- The problem does not surface (with ng-grid template and NgGridTestCtrl) if the data frequency is lower (500 milliseconds instead of 10 milliseconds interval in the streamData function).
- The memory problem persists (with plain table template and NgGridCtrl), even when lowering the data frequency (500 milliseconds instead of 10 milliseconds interval in the streamData function). The memory growth rate simply slows down, as expected.
- No memory problems are encountered with higher frequency data when utilizing the "SimpleTableCtrl" with "plain table template".
- I have yet to determine if ng-grid can effectively handle high-frequency data. Any insights on ng-grid's performance under such conditions?