Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Best practices for updating a clientOnly DataSource

    I'm using SmartClient_v121p_2020-04-14_PowerEdition. I have a ListGrid backed by a "clientOnly" DataSource. The grid is initialized by querying for any existing data in the database and then doing a "setCacheData" on the DataSource. Once initialized, this grid will get updated by data streaming over a websocket. See code below and the COMMENTs as to what seems to be happening in 2 different cases. After reading the documentation, I'm not clear as to which method is best to update a "clientOnly" DataSource - addData/updateData or updateCaches. Which method is considered the best practice in this case? Thank you.

    Code:
    function updateGrid(update) {
        // If the datasource has been defined...
        if( gridDS ) {
    
            // See if the record exists already and if it does, update it,
            // else, add the record
            let data = gridDS.getCacheData();
            let result = null;
            if( data !== undefined ) {
                result = data.find("id", update.id);
            }
    
            try {
    
                // COMMENT:  Case 1:  This does not seem to actually update the cache as I will get
                // "clientOnly add operation failed for DataSource gridDS: Duplicate key in record" errors.
    
                if( result === null ) {
                    gridDS.addData(update, applyFilterCallback);
                } else {
                    gridDS.updateData(update, applyFilterCallback);
                }
    
                // COMMENT: Case 2: This works great as the cache does seem to be getting updated,
                // but I don't see a way to call a callback after the add/update happens.
                // The optional parameter for the updateCaches is a DSRequest,
                // which seems to be for making a call to the server, which is not desired here.
    
                // let operationType = "add";
                // if( result !== null ) {
                //    operationType = "update";
                // }
                // gridDS.updateCaches({
                //    data: [update],
                //    operationType: operationType
                // });
    
            } catch(e) {
                logger.error(e);
            }
        }
    }

    #2
    The former is the better way to do the update if you want a callback. But on an add, if there is an existing record with the same Primary Key value, the add will fail, just as it would for a normal DataSource.

    If the error seems to be erroneous, please let us know and show how the problem can be reproduced.

    Comment


      #3
      The error seems to occur under this condition:
      1. Data is not found in the cached data, so the data is added to the DataSource via the addData method (for example, primary key is id = 1)
      2. An update comes in for id = 1 and even though the data SHOULD be in the cached data, it is not found and attempts to add the data via addData rather than updateData
      Below is some sample code that shows the duplicate error.

      Code:
      // Define clientOnly DataSource
      var gridDS = isc.DataSource.create({
         ID: "gridDS",
         fields: [
            {name: "id", type: "integer", primaryKey: true},
            {name: "name", type: "text"},
            {name: "status", type: "text"}
         ],
         dataFormat: "json",
         clientOnly: true
      });
      
      // Define grid
      var statusGrid = isc.ListGrid.create({
         height: "100%",
         width: "100%",
         ID: "statusGrid",
         autoFetchData: true,
         selectionType: "single",
         cellHeight: 30,
         dataSource: gridDS,
         fields: [
            {name: "id", title: "ID", align: "center"},
            {name: "name", title: "Name", align: "center"},
            {name: "status", title: "Status", align: "center"}
         ],
         sortField: "id",
         sortDirection: "ascending"
      });
      
      // Create some test data to initialize the DataSource with
      var testData = [
         {id: 1, name: "Item 1", status: "Connected"},
         {id: 2, name: "Item 2", status: "Disconnected"},
         {id: 3, name: "Item 3", status: "Unknown"}
      ];
      
      // Initialize the data
      gridDS.setCacheData(testData);
      statusGrid.invalidateCache();
      
      // Create some test statuses to randomly display
      var statuses = ["Connected", "Disconnected", "Error", "Timed Out", "Unknown"];
      
      function updateGrid(update) {
          // If the datasource has been defined...
          if( gridDS ) {
              // See if the record exists already and if it does, update it,
              // else, add the record
              let data = gridDS.getCacheData();
              let result = null;
              if( data !== undefined ) {
                  result = data.find("id", update.id);
              }
              try {
                  if( result === null ) {
                      gridDS.addData(update);
                  } else {
                      gridDS.updateData(update);
                  }
      
              } catch(e) {
                  console.error(e);
              }
          }
      }
      
      function sleep(ms) {
         return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      function sendUpdate() {
         for( var i = 0; i < 10; i++ ) {
            var update = {id: i, name: "Item " + i, status: statuses[Math.floor(Math.random() * 5)]};
            updateGrid(update);
            sleep(1000);
         }
         // Do this same loop again to cause the error; The error happens if another update comes in after you have added data to the
         // datasource via .addData
         for( var i = 0; i < 10; i++ ) {
            var update = {id: i, name: "Item " + i, status: statuses[Math.floor(Math.random() * 5)]};
            updateGrid(update);
            sleep(1000);
         }
      }
      
      setTimeout(function() {
      
         sendUpdate();
      
      }, 2000);

      Comment


        #4
        UPDATE: I do believe it's a timing issue. In the code above, if you change the sendUpdate function to an async function and add an "await sleep(1000);" line between the 2 for loops, then the error doesn't occur. So, I do believe what's happening is that the cache is being updated but the updates are coming in before the cache update is complete.

        Unfortunately, I cannot control when/how often I am receiving updates. I have to process them as they come in. If I am getting this error, I feel like my row is not actually getting updated, but it could be. I'd have to test that.

        In this scenario, would it be better to go with the updateCaches method or is there something else I can do using the addData/updateData method? This is a near real-time application, so I have to get the updates out there ASAP.

        Thanks in advance.

        Comment


          #5
          Hi Dana
          This makes perfect sense.
          Data operations (add, update etc) on any dataSource, including a client-only dataSource are asynchronous.

          For a clientOnly dataSource, since the cache data is all local to the browser, it would be possible to make these operations synchronously update caches and fire their callbacks - but this is not the case by design as they are intended to be able to be swapped with a server-backed dataSource without any change to behavior.

          As such if you're synchronously doing an "addData()" and then looking at the cache, it will not yet have been updated.

          There are basically two options therefore:
          1) You can use the callback from each addData() call to be notified that the add has completed, at which point you could examine the cache data, perform a second update, etc
          2) You could use the updateCaches() API to synchronously update the cache.

          Either approach should be workable for you but now we have a better idea of your use case, updateCaches() is probably a little simpler


          Regards
          Isomorphic Software

          Comment

          Working...
          X