IndexedDb:database.close() 挂起

w.b*_*ian 4 indexeddb dart

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

IDBDatabase.close()文档指出“IDBDatabase 接口的 close() 方法立即返回并在单独的线程中 关闭连接。一旦数据库实际关闭,Close 不接受要触发的回调,因此无法确定连接是否已关闭。

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

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

编辑

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

需要注意的几件事——repro 是用 dart 编写的,因为我在那里看到了这个问题。该行为在 Chrome 和 Dartium(嵌入了 dart VM 的特殊版本的 Chromium)中都可以重现。对于那些没有使用过 Dart 但仍然想解决这个问题的人,请按照以下步骤操作:

  • 下载并安装 Dart。也可以通过homebrew安装。
  • 解压缩 dart 安装后,添加{extracted_dir}/dart/dart-sdk/bin到 PATH。
  • 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. 测试超时并显示任何输出需要很短的时间。还有一些重要的打印消息将显示在开发人员控制台上。

ale*_*xtk 5

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

  • 最后一个事务在关闭数据库之前完成
  • 数据库在删除之前已关闭

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

  • 在你的第一次测试中,确保你从 tx.completed 返回未来,以便在交易完成之前不会调用tearDown(不需要添加完成者,人们往往会忘记你可以安全地在测试/设置/ tearDown 并且单元测试框架将“等待”它们):
return tx.completed.then((_) {
    print('transaction complete.');
},...
Run Code Online (Sandbox Code Playgroud)
  • 在您的拆卸中,在删除之前调用 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.');
  });
});
Run Code Online (Sandbox Code Playgroud)