IndexedDb:database.close()挂起

时间:2015-01-09 21:08:39

标签: dart indexeddb

我正在编写一些我正在使用IndexedDb的数据存储内容的集成测试。这涉及通过创建数据库,执行某些操作(运行测试),然后通过调用database.close()然后通过调用window.indexedDB.deleteDatabase(DB_NAME)删除数据库来拆除每个测试来设置每个测试。

documentation for IDBDatabase.close()表示“IDBDatabase接口的close()方法立即返回并在单独的线程中关闭连接。 Close不接受一次触发的回调数据库实际上已经关闭,因此无法确定连接是否已关闭。

尝试使用window.indexedDB.deleteDatabase(DB_NAME)删除数据库时,我的初始测试超时。所有测试都在打开数据库,除此之外没有任何操作。我可以通过在调用database.close()后添加一个小超时来解决这个问题。

添加另一个向数据库添加数据的测试后,即使超时,也会再次挂起删除数据库的调用。数据 成功添加并且事务回调完成,因此我不确定调用database.close()的原因是什么。任何见解将不胜感激。

修改

我创建了一个项目来说明这个问题。代码可以在这里找到:https://github.com/bgourlie/idb-hang-repro

有几点要注意 - 复制品是用飞镖写的,因为那是我看到这个问题的地方。该行为在Chrome和Dartium(嵌入了dart VM的Chromium的特殊版本)中得到了再现。对于那些没有使用Dart但仍想解决此问题的人,请按照以下步骤操作:

  • Download and install Dart。它也可以通过homebrew安装。
  • 提取dart安装后,将{extracted_dir}/dart/dart-sdk/bin添加到路径中。
  • git clone https://github.com/bgourlie/idb-hang-repro.git
  • cd idb-hang-repro
  • pub get
  • pub serve

这将启动pub开发服务器,最有可能是http://localhost:8080。我在测试运行器中重现了这个问题,可以在http://localhost:8080/tests.html访问。测试超时并显示任何输出需要很短的时间。还有一些重要的打印消息将显示在开发者控制台上。

2 个答案:

答案 0 :(得分:4)

由于我已经为我的idb_shim项目做了大量的索引数据库实验,我可以分享我能够在新数据库上编写单元测试,只要我确保

  • 关闭数据库之前完成的最后一笔交易
  • 数据库在删除之前已关闭

根据我能够修复你的测试项目(感谢分享它),并进行了以下更改:

  • 在第一次测试中,确保从tx.completed返回未来,以便在事务完成之前不调用tearDown(无需添加完成者,人们往往会忘记您可以安全地在测试中返回未来/ setUp / tearDown以及单元测试框架将等待他们):
return tx.completed.then((_) {
    print('transaction complete.');
},...
  • 在你的tearDown中,在删除它之前调用db.close。

作为旁注,我通常更喜欢删除setUp功能中的数据库,以便在先前的测试失败并且未清理数据库时它可以正常工作。因此,现有代码的解决方案是:

setUp(() {
  return dom.window.indexedDB.deleteDatabase(DB_NAME, onBlocked: (e) {
    print('delete db blocked, but completing future anyway');
  }).then((_) {
    print('db successfully deleted!');
    return dom.window.indexedDB.open(DB_NAME, version: 1, onUpgradeNeeded: (VersionChangeEvent e) {
      print('db upgrade called (${e.oldVersion} -> ${e.newVersion})');
      final db = (e.target as Request).result;
      db.createObjectStore('foo', autoIncrement: true);
    }, onBlocked: (e) => print('open blocked.')).then((_db_) {
      print('db opened.');
      db = _db_;
    });
  });
});

tearDown(() {
  // note the 'close' here
  db.close();
});

group('indexed DB delete hang repro', () {
  test('second test which will add data', () {
    print('adding data in second test...');
    final tx = db.transaction('foo', 'readwrite');
    final objectStore = tx.objectStore('foo');
    objectStore.add({
      'bar': 1,
      'baz': 2
    }).then((addedKey) {
      print('object added to store with key=$addedKey');
    }, onError: (e) => print('error adding object!'));

    // note the 'return' here
    return tx.completed.then((_) {
      print('transaction complete.');
    }, onError: (e) => print('transaction errored!'));
  });

  test('call setup and teardown', () {
    print('just setup and teardown being called in first test.');
  });
});

答案 1 :(得分:3)

正如您所注意到的,无法知道database.close()何时结束,window.indexedDB.deleteDatabase在尝试删除数据库时仍然有点奇怪。但这些并非难以逾越的问题。看看what actually happens when you try to delete a database。我对此的解读表明,如果您尝试删除仍处于打开状态的数据库,它将在每个打开的数据库连接上触发versionchange事件。然后,如果数据库仍处于打开状态,它将为您的blocked触发success事件而不是deleteDatabase,但它仍会继续并以任何方式删除数据库。

因此,我这样做:

var request = indexedDB.deleteDatabase("whatever");
request.onsuccess = function () {
    success();
};
request.onfailure = function (event) {
    fail(event);
};
request.onblocked = function () {
    success();
};

更好的解决方案可能是以某种方式监听前面提到的versionchange事件并在那里关闭数据库,但我无法弄清楚如何做到这一点。只有当你删除它时,如果你关心数据库是否打开,那就更重要了,你可能不会这样做。