IndexedDB事务的冲突目的

时间:2015-01-06 02:08:34

标签: javascript transactions indexeddb

据我了解,将多个IndexedDB操作放在单个事务中而不是为每个操作使用唯一事务有三个不同的原因:

  1. 性能。如果你正在对一个对象存储进行大量写操作,那么如果它们在一个事务中发生就会快得多。
  2. 确保在继续之前写入数据。 Waiting for the “oncomplete” event is the only way to be sure that a subsequent IndexedDB query won’t return stale data.
  3. 执行数组操作的原子集。基本上,“做所有这些事情,但如果其中一个失败,请将其全部推回”。
  4. #1很好,大多数数据库具有相同的特征。

    #2更加独特,与#3一起考虑会导致问题。假设我有一些简单的函数可以将数据写入数据库,并在结束时运行回调:

    function putWhatever(obj, cb) {
        var tx = db.transaction("whatever", "readwrite");
        tx.objectStore("whatever").put(obj);
        tx.oncomplete = function () { cb(); };
    }
    

    工作正常。但是现在如果你想把这个函数作为你想要自动提交或失败的一组操作的一部分来调用,那是不可能的。你必须做这样的事情:

    function putWhatever(tx, obj, cb) {
        tx.objectStore("whatever").put(obj).onsuccess = function () { cb(); };
    }
    

    该函数的第二个版本与第一个版本非常不同,因为回调在数据保证写入数据库之前运行。如果您尝试回读刚才写的对象,则可能会得到陈旧的值。

    基本上,问题是你只能利用#2或#3中的一个。有时选择是明确的,但有时不是。这导致我编写了可怕的代码,如:

    function putWhatever(tx, obj, cb) {
        if (tx === undefined) {
            tx = db.transaction("whatever", "readwrite");
            tx.objectStore("whatever").put(obj);
            tx.oncomplete = function () { cb(); };
        } else {
            tx.objectStore("whatever").put(obj).onsuccess = function () { cb(); };
        }
    }
    

    然而,即使这仍然不是一般解决方案,在某些情况下可能会失败。

    还有其他人遇到过这个问题吗?你如何解决?或者我只是以某种方式误解了事情?

1 个答案:

答案 0 :(得分:1)

以下只是意见,因为这似乎不是一个“正确答案”的问题。

首先,表现是一个无关紧要的考虑因素。完全避免这个因素,除非后来的分析表明存在重大问题。性能问题的可能性非常低。

其次,我更愿意将请求组织到事务中以保持完整性。诚信是至关重要的。我在此定义的完整性只是意味着任何一个时间点的数据库都不包含冲突或不稳定的数据。本质上,数据库永远无法进入“坏”状态。例如,强加一个规则,即跨存储对象引用指向其他存储中的有效和现有对象(例如参照完整性),或者防止重复请求,例如双重添加/放置/删除。显然,如果应用程序类似于银行应用程序,可以记入/借记帐户,或者是心脏病发作监控应用程序,那么事情可能会出现严重错误。

我自己的经验让我相信涉及indexedDB的代码不容易出现传统的外观模式。我发现在将请求组织到不同的包装函数方面最有效的方法是围绕事务设计函数。我发现很少有DRY violations,因为每个请求几乎总是唯一的事务上下文。换句话说,虽然类似的“put对象”请求可能出现在多个事务中,但它的行为是如此明显,因为它的单独上下文值得违反DRY。

如果你按照请求路由运行函数,我不确定你为什么要检查事务参数是否未定义。让调用者创建函数,然后依次将其传递给请求。期望tx始终被定义,并且不要过度热心地防范它。如果没有定义,则indexedDB或您的调用函数中存在严重错误。

明确地说,像:

function doTransaction1(db, onComplete) {
  var tx = db.transaction(...);
  tx.onComplete = onComplete;
  doRequest1(tx);
  doRequest2(tx);
  doRequest3(tx);
}
function doRequest1(tx) {
  var store = tx.objectStore(...);
  // ...
}
// ...

如果请求不应该并行执行,并且必须以一系列方式运行,那么这表明设计问题更大,更困难。