DataTable initialization gets slower after each destroy.

DataTable initialization gets slower after each destroy.

bms270bms270 Posts: 8Questions: 1Answers: 0

I am initializing the DataTables on an existing HTML table that is generated via another framework. The data in the table is controlled by that framework so when the data changes, I need to destroy the DataTables (to get the original table back) then get the rows updated and then initialize the DataTables again. This process works fine but everytime the data is updated, the initialization takes longer and longer. The initial load takes 2 seconds then 4, then 6, then 8.. I profiled the browser and seems like CPU is being utilized at 100% during the initialization. I am posting the screenshot of the browser profile here. Only thing I can see is that the "Listeners" accumulate and never cleared. Not sure if that is the culprit but looks like the other memory items (JS Heap, Documents and Nodes grow but they get cleared and after they are cleared, the processing time does not improve.

Answers

  • kthorngrenkthorngren Posts: 21,303Questions: 26Answers: 4,947

    Why do you need to destroy the Datatables? Does the Datatable config change or just the DOM table data?

    If just the table data changes maybe you can try rows().invalidate() with the dom parameter to refresh the Datatable data cache.

    Kevin

  • bms270bms270 Posts: 8Questions: 1Answers: 0
    edited August 2019

    Thanks Kevin for the answer. The invalidate works too but the issue is that the table loses the sorting capability after data is refreshed. Calling the draw() method after records are updated does not help either. Not sure if there are any other tricks to get the sorting back. The only way we can get the sorting back is to destroy and recreate which is causing the issue I described.

  • glimpsed_chaosglimpsed_chaos Posts: 140Questions: 30Answers: 4

    I do something similar. I use signalR to receive data on a periodic basis and update content for all clients. In my case, like yours, I need to update an existing table.

    First in my JS file I have my table created with a "data": null setting. Basically just creating a table with an empty dataset, although you could have data as well.

        var table = $('#mytable').DataTable({
            "data": null,
            order: [[1, 'asc', 2, 'asc', 3, 'asc', 4, 'asc']],
            ordering: true,
            select: false,
            autoWidth: true,
           etc....
    

    Second, when my data is received/updated I do the following:

    var data = JSON.parse(myData); 
    var updatemytable = $('#mytable').DataTable().clear().rows.add(data).draw();
    
    

    I don't have to destroy and recreate over and over and have not seen any degraded performance. I don't lose any of my initial sorting settings or capability.

    Only issue that I have found, is that if a user is looking through the data it gets reset back to the first row view.

  • bms270bms270 Posts: 8Questions: 1Answers: 0

    @glimpsed_chaos Thanks for the answer but in my case, I get the updates through variable binding in the basic HTML table in the DOM which gets parsed by DataTable on initialization. So not able to use the direct Data API of the DataTables to clear and add rows.

  • kthorngrenkthorngren Posts: 21,303Questions: 26Answers: 4,947

    The invalidate works too but the issue is that the table loses the sorting capability after data is refreshed.

    Then is sounds like invalidate isn't working because it should read the table and update the Datatables cache so that sorting and searching work.

    Are you recreating the whole HTML table or just clearing and adding new data? If you are recreating the whole table then I can see that invalidate doesn't work as Datatables is not associated to the table anymore.

    I guess we need to understand more about your process of replacing the data. Are you using destroy() before you update the HTML table? If not you might be leaving behind events, etc that aren't destroyed if you remove the HTML table before destroying.

    Please provide more info or better a link to your page to take a look.

    Kevin

  • bms270bms270 Posts: 8Questions: 1Answers: 0
    edited August 2019

    @kthorngren You are correct. Even though the data in the data tables is updated (I can see new rows) but the rows().data() returns the original set of data and does not get updated. invalidate seems to have no effect on getting the stored data refreshed.

    Here's the step by step what I used to have (with the increasing initialization CPU time issue):

    1. An event is captured that the data needs to be refreshed
    2. I check to see if the data table is initialized, if so, I destroy the table. This brings back the old basic HTML table in the DOM.
    3. I update the variables that are responsible to generate the HTML table. (similar to angular scope variable).
    4. Once the HTML table is rendered, I re-initialize the DataTable (which parses the new table data)

    This works fine but step 4 is where I see increase in initialization every time the refresh happens. The first time its 2 seconds, then 4 then 6 then 8, ... (See the profile image above).

    Per your recommendation, I modified the process to below:

    1. An event is captured that the data needs to be refreshed
    2. I check to see if the data table is initialized, if so, I call the invalidate().draw().
    3. I update the variables that are responsible to generate the HTML table. (similar to angular scope variable).
    4. This updates the Data Tables with pagination and search but the sorting disappears. (No need to re-initialize the table.)

    I also tried to do invalidate().draw() after step 3 but no changes.

    It's very strange.

  • kthorngrenkthorngren Posts: 21,303Questions: 26Answers: 4,947

    For invalidate to work it would need to run after you updated the DOM based table. Specifically did you use table.rows().invalidate('dom').draw();?

    Where table is a variable containing the Datatable API.

    Your first set of steps seem like the correct order. Are you able to provide a link to your page or a test case replicating the issue?
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • bms270bms270 Posts: 8Questions: 1Answers: 0

    I spent more time drilling into processes in the profiler in chrome and finally realized the issue. I have a listener setup on 'draw.dt' which takes care of some styling. This listener sticks around even after the table is destroyed which seems to be a bug. What happens is that the draw event gets called internally during the initialization so every time the table is initialized, the handler is called from all previous initializations in addition to the current initialization. This leads to significant increase in the CPU time. The majority of the initialization is apparently was just the internal call to draw method which was causing my handler to be fired. Once I removed the handler, all initializations are equally in a matter of milliseconds. I think this is a bug but for now, I am going to find a better solution to apply my styling after the datatable is initialized or alternatively use the off to turn off the handler before destroying the table.

  • bms270bms270 Posts: 8Questions: 1Answers: 0

    Thank you Kevin for your help and keeping me motivated to dig in :smile:

  • kthorngrenkthorngren Posts: 21,303Questions: 26Answers: 4,947

    Good, glad you found it. @allan or @colin can take a closer look to see if they can replicate the issue to fix it.

    Kevin

  • bms270bms270 Posts: 8Questions: 1Answers: 0

    Looks like this is not a bug. It is documented:

    "It is important to remove events from objects which no longer exist (before they are destroyed) to allow the Javascript engine's garbage collector to release the memory allocated for the events and the objects it has attached to."

  • bms270bms270 Posts: 8Questions: 1Answers: 0

    However, @allan I still think once you destroy the DataTable, the listeners should be removed automatically to avoid getting into my scenario.

  • kthorngrenkthorngren Posts: 21,303Questions: 26Answers: 4,947

    Nice find. I hadn't noticed that before. Lots of docs to read :smile:

    Kevin

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Hi @bms270 (and Kevin),

    I'm not sure it is a bug. As you say, that quoted text states that if you add an event, it's up to you to remove it. The call to destroy() will remove all internal events, but the user may wish that events they created remain, especially if they're going to recreate the table as you are. One for @allan methinks :)

    Cheers,

    Colin

  • allanallan Posts: 63,457Questions: 1Answers: 10,465 Site admin

    Yes, as Colin says, if you add event handlers, you are also responsible for removing them in order to prevent memory leaks, just as it works in the DOM.

    Its possible we should have a namespace for events to automatically be removed on destroy() (we actually do already internally in the code, but it isn't publicly documented anywhere - I was worried about confusing things).

    Allan

This discussion has been closed.