Mass createRow performance when creating hundreds of rows

  • 5
  • Question
  • Updated 5 months ago
  • Answered
  • (Edited)
When building mass edit UIs we've encountered slow performance of the createRow method when creating hundreds of records. I've attached a basic page that performs increasing volumes of mass row creation, the results I get show 2s for 100 rows and 144s for 1000 rows. Putting aside the wisdom of why we want to create 1000 records, even 100 rows takes too long and he results are not as linear as you'd expect:
Created rows: 1 in 0.023s = 0.023s per row
Created rows: 10 in 0.142s = 0.014s per row
Created rows: 100 in 2.257s = 0.02257s per row
Created rows: 200 in 6.898s = 0.03449s per row
Created rows: 500 in 37.399s = 0.074798s per row
Created rows: 1000 in 144.617s = 0.144617s per row
Our use case is a mass edit table that allows users to "preview" and "configure" a large table (100s of rows), perform some calculations and then save that data to Salesforce for output in an Excel document. We experience sluggish performance when we loop through our data and use createRow to build the transformed data. Are we doing something wrong or is there another way to mass create rows on a model to avoid any bottlenecks? We don't necessarily have to display the mass created rows in a table but we have (reluctantly) started to move some of our code to Apex to get better performance and (to be fair) for other reasons like reducing network roundtrips.
<skuidpage showsidebar="false" showheader="false">
   <models>
      <model id="Contacts" limit="1000" query="false" createrowifnonefound="false" sobject="Contact">
         <fields>
            <field id="Name"/>
            <field id="LastName"/>
         </fields>
         <conditions>
            <condition type="fieldvalue" value="1" enclosevalueinquotes="true" field="Id"/>
         </conditions>
      </model>
   </models>
   <components>
      <pagetitle model="Contacts">
         <maintitle>
            <template>{{Name}}</template>
         </maintitle>
         <subtitle>
            <template>{{Model.label}}</template>
         </subtitle>
         <actions>
            <action type="custom" label="Create Mass Contacts" window="self" snippet="CreateMassContacts"/>
         </actions>
      </pagetitle>
      <skootable showconditions="true" showsavecancel="false" searchmethod="server" searchbox="false" showexportbuttons="false" pagesize="all" createrecords="false" model="Contacts" buttonposition="" mode="readonly" uniqueid="ContactsTable">
         <fields>
            <field id="Name"/>
            <field id="LastName"/>
         </fields>
         <rowactions/>
         <massactions usefirstitemasdefault="true"/>
         <views>
            <view type="standard"/>
         </views>
      </skootable>
   </components>
   <resources>
      <labels/>
      <javascript>
         <jsitem location="inlinesnippet" name="CreateMassContacts" cachelocation="false">var params = arguments[0],
   $ = skuid.$;
var models = skuid.model.map();
var contacts = models.Contacts;
createRows(1);
contacts.cancel();
createRows(10);
contacts.cancel();
createRows(100);
contacts.cancel();
createRows(200);
contacts.cancel();
createRows(500);
contacts.cancel();
createRows(1000);
//contacts.cancel();
function createRows(noOfRows) {
    var start = new Date().getTime();
    for (var i=0; i&lt;noOfRows; i++) {
        var row = contacts.createRow({
            additionalConditions: [
                { field: 'LastName', value: i, operator: '=', nameFieldValue: this.Name }
            ]
        });
    }
    var end = new Date().getTime();
    console.log('Created rows: ' + noOfRows + ' in ' + (end-start)/1000 + 's = ' + ((end-start)/1000)/noOfRows + 's per row');
}</jsitem>
      </javascript>
      <css/>
   </resources>
</skuidpage>
Photo of Stephen Chan

Stephen Chan

  • 956 Points 500 badge 2x thumb

Posted 4 years ago

  • 5
Photo of Ben Hubbard

Ben Hubbard, Employee

  • 12,490 Points 10k badge 2x thumb
Hi Stephen,

The issue you're experiencing with the createRow API being slow is that Skuid runs all of its rendering logic after each createRow.  Each time you add a new row to your model, it takes your browser more and more time to render the table.  In essence, when you call createRow 1000 times, your browser is drawing a progressively larger table 1000 times.

In our common use cases such as updateRow, we've created a separate API method called updateRows that modifies all the rows and then when it's done, handles all of Skuid's rendering logic.

We haven't seen a use case like this before, so we didn't think to make the createRows API method.  I've added this item as a feature we need to add to our API.  This should make your adding of 1000 rows MUCH faster.
Photo of Stephen Chan

Stephen Chan

  • 956 Points 500 badge 2x thumb
Hi Ben,

Thanks for the explanation, it makes sense and we'd really like to see the new mass createRows feature so our more advanced scenarios will work faster.

In the meantime do you have any suggestions for a workaround we can use until the new method? Is there someway of "disabling" the render logic after each row. I vaguely recall something Zach posted somewhere about "disconnecting" a model from its component and then reattaching it? Perhaps I can redesign the page and models to perform the required mass creates on an isolated model and then directly copy or move the data to the real "display" model?

Just to prove you're right and one workaround option that might be available in some scenarios is to not have any component bound to the model so no rendering occurs. You do in fact get the expected linear performance:
Created rows: 1 in 0.011s = 0.011s per row
Created rows: 10 in 0.043s = 0.0043s per row
Created rows: 100 in 0.389s = 0.00389s per row
Created rows: 200 in 0.705s = 0.003525s per row
Created rows: 500 in 1.731s = 0.00346s per row
Created rows: 1000 in 3.58s = 0.00358s per row
Cheers,
Stephen
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Is there an update on this? Is createRows() now available? Thanks!
Photo of Oliver Sinfield

Oliver Sinfield

  • 922 Points 500 badge 2x thumb
Any updates on this? I have a quote building tool that gets really slow once you get past 20 or so row rows...
Photo of Gary

Gary

  • 1,518 Points 1k badge 2x thumb
Another person who'd like an update here. Is there even any work around?

I've found a couple of ways that can help, but not consistently. I was showing a model in a table that was queried when a button was pressed elsewhere. So I put an action on the button that updated a model condition that meant the table would not be rendered, then did the query (also as an action), then the javascript that called createRow based on those results, then updated the condition again afterwards so that the table would show. This works, but unfortunately, sorting by a table column header causes the slow performance again.

It should be feasible in javascript - there's unregisterEditor and registerEditor but I can't quite get it working. After querying the data and creating rows, calling registerEditor doesn't seem to bring the data into the UI. I suspect there may be some sort of refresh call I need to make.

Anyway, any other workarounds people have found? And to Skuid - any update on createRows()?
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Sorry guys.  Its a feature enhancement that we have on our list and have pretty well specced out,  but haven't been able to get completed. 
Photo of Gary

Gary

  • 1,518 Points 1k badge 2x thumb
In lieu of the feature being added, I thought I'd add my workaround in case anybody else reading this in the future could use it.

Currently, we use actions and rendering features to control the display of UI elements that are registered to a model where createRow is being called. Basically - we hide the UI element whilst our snippet runs and then show it again after the snippet has finished.

Our situation: we have a model which is shown in a table of records, and is modified by a snippet using createRow(). We added a render condition on the table that used a checkbox on a model we weren't actually modifying. 

Above the table is a button that runs our snippet as an action. We added two extra actions, one at the start and one at the end. All this did was updated the checkbox used to control the rendering of the table.

This approach felt kind of... amateur? Hacky? I did spend time looking at a number of register and unregister methods, as it seems it should be possible to do this from within the snippet - unregister editors against the models, call createRow as many times as needed, then restore the connection to the editor using a register method. However, try as I might, I couldn't get this to work as I thought it should from the documentation. If anybody better than I is able to achieve it, I'd love to see where I was going wrong!
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Your approach garnerned approving smiles from the developers here at Skuid.  Nothing amateurish or hacky about it.  You are using the conditional rendering and action framework tools in a very effective way.  Unexpected?  Maybe.  But effective...
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
any chance createRows() has made an appearance in Banzai?
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Sorry. Not yet... 
Photo of Jarrod Hinson

Jarrod Hinson

  • 2,310 Points 2k badge 2x thumb
+1 from me as well. We are doing a lot of row creation from APIs now and this is very slow
Photo of Tim Wilson

Tim Wilson

  • 504 Points 500 badge 2x thumb
I stumbled upon this older thread. I'll go ahead and add a +1 from me :D