typicaljoe / taffydb

TaffyDB - an open source JavaScript Database for your browser
http://taffydb.com
MIT License
2.21k stars 285 forks source link

onDBChange infinite loop #104

Open vulpineronin opened 9 years ago

vulpineronin commented 9 years ago

I am writing an angularjs app using Taffydb for data manipulation and storage in the front end. Since there is no trigger to update the scope for angular in taffy, and watches will not function reliably with the structure of the taffy objects, I have set up onDBChanges for each taffy table we are using. The issue comes when the function to update a table uses a join on another taffy table. It seems that the join is triggering a change event, even when no data changes. i.e.:

$scope.updaterOne = function(){
       $scope.variabeOne = $scope.taffyTableTwo(filterField:value).order("id2 asec").join($scope.taffyTableOne,['id2','id1']).distinct('id1')
}

$scope.taffyTableOne = TAFFY(dataOne);
$scope.taffyTableTwo = TAFFY(dataTwo);

$scope.taffyTableOne.settings({
       onDBChange:function(){$scope.updaterOne();}
});

$scope.taffyTableOne.insert(newData);

In that example, when taffyTableOne is changed it triggers the updaterOne function to update the variableOne variable for angular. This then updates the scope and everything goes fine, but having that join also quietly re-triggers the onDBChange for taffyTableOne (the joined table specifically, not the initial table for the join), which will then infinitely loop as every trigger ends up re-triggering itself.

This isn't noticed at first in the app since the scope updates but there was no change so there is no change reflected in the data on screen, but very quickly you wind up grinding the browser to a halt with infinite loops running in parallel as you move within the app.

I am currently looking at boolean flags I can feed to the onDBChange function to prevent the loop, but this seems like unexpected behaviour as no changes to the data were made, and onDBChange does not typically trigger for a simple query of the data.

vulpineronin commented 9 years ago

I have verified that the join is the trigger and that the only way to keep it is with multiple nested timeouts to set and reset boolean flags to prevent the re-triggering. It is not a constant number of triggers per join, but somehow seems related to the number of returned results. When we had zero (0) results it would not trigger at all, but 2-3 results would trigger a dozen times and 6-8 results would trigger over 30 times. Each trigger then running its own onDBChange loop again asynchronously. The current workaround we are using is to set each section we are needing into separate local variables and then writing our own joins. Once the Taffydb joins are removed all multiple onDBChange calls are stopped.

Edit for clarity: The multiple nested timeouts would be in the onDBChange. Something like:

flag = true;
...
$scope.taffyTableOne.settings({
       onDBChange:function(){
              $timeout(function(){
                     if(flag){
                            flag = false;
                            $scope.updaterOne();
                     }else{
                           $timeout(function(){
                                  flag = true;
                           },1000); //must be large enough to allow all possible repeats to finish before resetting the flag
                     }
              },100);
       }
});