1

TL;DR: collection.modify() works with a few records, but fails when trying to modify more than 10 records at once. Why?

I have some code that displays and interacts with a database using Javascript, HTML, and a Dexie wrapper over IndexedDB. Here's the problem: I can only modify a few records at a time. If I try more than ~8-10 records, modify() fails with no error.

Here's how I call the database and draw the query tables:

//Every time the page refreshes, the db is open and draws tables
$document.ready(function(){
//Open database
    db=new Dexie("peopleManager");
    db.version(1).stores({
        people: "id++, name, location"
    });
    //Call functions that draw query tables
    showDallas();
    showNewYork();
});

//Draws an HTML table of people who work in Dallas
function showDallas(){
    var output = '';
    db.people
        .where("location")
        .equals("Dallas")
        .each
        (function(people){
            output += "<tr align='center'>";
            output += "<td>"+people.id+"</td>";
            output += "<td>"+people.name+"</td>";
            output += "</tr>";
        $('#dallas').html(output);
        });
}

//Draws an HTML table of people who work in NY
function showNewYork(){
    var output = '';
    db.people
        .where("location")
        .equals("New York")
        .each
        (function(people){
            output += "<tr align='center'>";
        output += "<td>"+people.id+"</td>";
        output += "<td>"+people.name+"</td>";
        output += "</tr>";
        $('#newYork').html(output);
        });
}

Here's the function that keeps failing. It gets fired by a click on an HTML button:

//Move all to New York
function moveToNewYork(){
    db.transaction('rw', db.people, function(){
        db.people.where("location").equals("Dallas").modify({location: "New York"});
    }).then(function(){
        window.location.reload();
    }).catch(Dexie.ModifyError, function(e){
        console.error(e.failures.length + "failed to hire modify");
        throw e;
    });
}

HTML button:

<form role = "form" onSubmit = "moveToNewYork()">
<button type="submit" class="btn btn-primary">Move All</button></form>

I can modify less than 10 records. More than ten and the page refreshes with no change to the db and no error recorded. Following the documentation but haven't seen any examples where modify() would need to change 10+ records. I haven't found anything that shows a modify() transaction fails after a certain number of records.

Anyone see what I'm doing wrong, or how I can troubleshoot this further?

Note that the actual code is long with about 20 other read-only operations going on.

Update: Here's a full JSFiddle showing the error. With these (very small) records, I can get to 12-15 before modify() starts to fail. Weirdly, clicking again a few times randomly gets modify() to work maybe 1 out of 8 times? I'm totally stumped.

  • Have you tried catching all errors instead of just `Dexie.ModifyError`? That could point you to the right direction. `.catch(function(e){ console.error(e); throw e; });` Edit: Ah, nevermind, I didn't read carefully enough to see that the page does reload. – noppa Jul 23 '18 at 19:59
  • Yeah, and in any case adding the ModifyError was a recent change after regular error catching never recorded anything. – Ian Birnbaum Jul 23 '18 at 20:13

1 Answers1

1

I didn't manage to repro this until I filled the DB with a thousand records. After that, the page started reloading prematurely, before the items were stored.

The error I got on the JSFiddle isn't exactly what you described, there the page just hits me with a 404 after trying to reload, but I think I know what's going on.

The page isn't reloading because of the window.location.reload(). Instead, the form's submit event bubbles to its default behaviour because it isn't explicitly prevented anywhere. So there's a race-condition; when there's only a small dataset, Dexie manages to finish its work before form reloads the page. When the dataset grows, changes take longer and the form's submit event wins the race.

To fix this, return false from the onsubmit callback to prevent default submit behaviour.

HTML:

<form role="form" onSubmit="return moveToNewYork()">
                            ^^^^^^ add "return" here

JS:

function moveToNewYork() {
  db.transaction('rw', db.people, function() {
    db.people.where("location").equals("Dallas").modify({
      location: "New York"
    });
  }).then(function() {
    window.location.reload();
  }).catch(Dexie.ModifyError, function(e) {
    console.error(e.failures.length + "failed to hire modify");
    throw e;
  });

  return false;  // add "return false" here
}

And also add the same changes to the other modifying forms & functions.

Related: SO - How to prevent form from being submitted.

noppa
  • 3,947
  • 21
  • 22
  • This did it! In retrospect a race condition seems obvious, but I never would have thought to solve it this way. Thank you thank you! – Ian Birnbaum Jul 24 '18 at 18:42
  • To be fair, spending hours on errors that seem obvious in retrospect is like 83% of programming. Glad I could help :) – noppa Jul 24 '18 at 19:11