从多个JavaScript线程访问IndexedDB

12

概述: 我正在尝试避免从网页和 Web Worker 访问 IndexedDB 时的竞争条件。

设置: 一个网页正在将项目保存到本地的 IndexedDB 中,用户在使用该站点时可以保存数据。每当用户将数据保存到本地数据库中时,记录都会被标记为“未发送”。

另外还有一个 Web Worker 后台线程,它从 IndexedDB 中获取数据并将其发送到服务器。一旦服务器接收到数据,就会将其标记为“已发送”。

问题: 由于访问 IndexedDB 是异步的,我无法保证用户不会在 Web Worker 发送数据到服务器的同时更新记录。时间轴如下所示:

  1. Web Worker 从数据库获取数据并将其发送到服务器
  2. 传输正在进行时,用户更新数据并将其保存到数据库中
  3. Web Worker 接收到来自服务器的响应,然后将数据库更新为“已发送”
  4. 现在数据库中有尚未发送到服务器但被标记为“已发送”的数据

失败的解决方案: 在从服务器接收到响应后,我可以重新检查行以查看是否发生了更改。然而,我仍然面临着一个小窗口的问题,在这个窗口内数据可以被写入数据库并且永远不会被发送到服务器。

例如: 在服务器表示数据已保存后,然后:

IndexedDB.HasDataChanged(
    function(changed) { 
        // Since this is async, this changed boolean could be lying.
        // The data might have been updated after I checked and before I was called.
        if (!changed){ 
          IndexedDB.UpdateToSent() }
    });

其他注意事项: 根据W3规范,有一个同步API,但目前还没有人实现它,因此无法使用(http://www.w3.org/TR/IndexedDB/#sync-database)。 我认为同步API是为Web Workers设计的,以避免出现这种情况。

如有任何想法,请不吝赐教。我已经在处理这个问题上工作了一个星期,但一直没有找到可行的解决方案。

3个回答

3

我觉得暂时找到了一个解决方法。虽然不够完美,但它似乎是线程安全的。

每当我更新数据时,我会先将日期时间存储到LastEdit字段中。然后,从Web Worker发送一条消息给浏览器。

self.postMessage('UpdateDataSent#' + data.ID + '#' + data.LastEdit);

然后在浏览器中,只要最后编辑日期没有更改,我就会更新我的发送标志。

// Get the data from the DB in a transaction
if (data.LastEdit == lastEdit)
{
    data.Sent = true;
    var saveStore = trans.objectStore("Data");
    var saveRequest = saveStore.put(data);
    console.log('Data updated to Sent');
}

由于这一切都在浏览器端的事务中完成,因此似乎工作得很好。一旦浏览器支持Sync API,我就可以抛弃它。


这被称为乐观锁定:https://zh.wikipedia.org/wiki/%E4%B9%90%E8%A7%82%E5%B9%B6%E5%8F%91. - xmedeko

0

我认为你无法在不使用事务的情况下访问IndexedDB。问题是,当我将数据发送到服务器时,事务不会保持打开状态。 - Shawn

0

虽然这是一个旧的线程,但使用事务可以解决“失败的解决方案”方法。即事务只需要跨越检查IndexedDB中的数据是否在“发送后”更改,并在没有更改的情况下将其标记为已发送。如果有更改,则事务结束而不写入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接