IndexedDB может сделать базу данных недоступной (заблокировать), как разблокировать?

ОБНОВЛЕНИЕ Я обнаружил, что проблема в том, что он заблокирован. Несмотря на то, что база данных всегда создается и обновляется одним и тем же расширением, она не закрывается. Итак, теперь я вызываю функцию «заблокировано».

Как «разблокировать» заблокированные в настоящее время базы данных? И как предотвратить это в будущем? Это приложение, поэтому никакие вкладки его не используют. И поскольку я не могу открыть эти базы данных, чтобы даже удалить их (это также блокируется), как мне их закрыть?

(Для тех, кто интересуется, чтобы избежать этой проблемы с самого начала, вы ДОЛЖНЫ сделать следующее:)

mydb.onversionchange = function(event) {
    mydb.close();
};

Исходное сообщение

IndexedDB умирает и становится недоступным для открытия, если я (случайно) пытаюсь открыть и обновить неправильную версию. Насколько я могу судить, нет возможности запросить у indexedDB последнюю версию БД. Поэтому, если я попытаюсь запустить следующий код дважды, он уничтожит базу данных и ее невозможно будет открыть:

И он никогда не выдает ошибку и не вызывает onerror. Он просто сидит молча

var db = null;

//Note, no version passed in, so the second time I do this, it seems to cause an error
var req = indexedDB.open( "test" );
req.onsuccess = function(event) { console.log( "suc: " + event.target.result.version ); db = event.target.result; };
req.onerror = function(event) { console.log( "err: " + event ); };
req.onupgradeneeded = function(event) { console.log( "upg: " + event.target.result.version ); };

//We're doing in interval since waiting for callback
var intv = setInterval(
    function()
    {
        if ( db === null ) return;

        clearInterval( intv );

        var req2 = indexedDB.open( "test", db.version + 1 );
        req2.onsuccess = function(event) { console.log( "suc: " + event.target.result.version ); };
        req2.onerror = function(event) { console.log( "err: " + event ); };
        req2.onupgradeneeded = function(event) { console.log( "upg: " + event.target.result.version ); };
    },
    50
);

Весь этот код находится в моем файле chrome.runtime.onInstalled.addListener. Поэтому, когда я обновляю свое приложение, оно снова вызывает его. Если я вызову indexedDB.open( "test" ) без передачи новой версии, а затем снова запущу функцию setInterval, это приведет к тому, что все станет непригодным для использования, и я никогда не смогу снова открыть «тест». Это было бы решено, если бы я мог запросить у indexedDB версию базы данных, прежде чем пытаться ее открыть. Это существует?


person Don Rhummy    schedule 11.06.2013    source источник


Ответы (2)


Может быть, это помогает?

function getVersion(callback) {
  var r = indexedDB.open('asdf');
  r.onblocked = r.onerror = console.error;
  r.onsuccess = function(event) {
    event.target.result.close();
    callback(event.target.result.version);      
  };
}

getVersion(function(version) {
  console.log('The version is: %s', version);
});

Хорошо, судя по разговору, эта небольшая полезная функция может указать вам путь:

var DATABASE_NAME_CONSTANT = 'whatever';

// Basic indexedDB connection helper
// @param callback the action to perform with the open connection
// @param version the version of the database to open or upgrade to
// @param upgradeNeeded the callback if the db should be upgraded
function connect(callback, version, upgradeNeeded) {
   var r = indexedDB.open(DATABASE_NAME_CONSTANT, version);
   if(upgradeNeeded) r.onupgradeneeded = updateNeeded;
   r.onblocked = r.onerror = console.error;
   r.onsuccess = function(event) {
     console.log('Connected to %s version %s', 
       DATABASE_NAME_CONSTANT, version);
     callback(event.target.result);
   };
}

// Now let us say you needed to connect
// and need to have the version be upgraded
// and need to send in custom upgrades based on some ajax call

function fetch() {
  var xhr = new XMLHttpRequest();
  // ... setup the request and what not
  xhr.onload = function(event) {
    // if response is 200 etc
    // store the json in some variable
    var responseJSON = ...;

    console.log('Fetched the json file successfully');
    // Let's suppose you send in version and updgradeNeeded
    // as properties of your fetched JSON object
    var targetVersion = responseJSON.idb.targetVersion;
    var upgradeNeeded = responseJSON.idb.upgradeNeeded;

    // Now connect and do whatever
    connect(function(db) {
      // Do stuff with the locally scoped db variable
      // For example, grab a prop from the fetched object
      db.objectStore('asdf').put(responseJSON.recordToInsert);

      // If you feel the need, but should not, close the db
      db.close();
      console.log('Finished doing idb stuff');
    }, targetVersion, upgradeNeeded);
  }
}
person Josh    schedule 11.06.2013
comment
Он никогда не вызывает onsuccess. Он вызывает только onblocked, и это приводит к ошибке: DOM IDBDatabase Exception 11 - person Don Rhummy; 11.06.2013
comment
Пытаясь тогда уточнить, вышеприведенное выводит на консоль «заблокированную» ошибку, даже если вы не передаете параметр версии в indexedDB.open? - person Josh; 11.06.2013
comment
Кроме того, просмотрите stackoverflow.com /вопросы/11898375/ . Это может помочь вам. Избегайте использования глобальной переменной db. Доступ только из обратного вызова. Скорее всего, это решит вашу проблему с блокировкой. Разработчики indexedDB используют шаблон обратного вызова, чтобы разрешить асинхронные вызовы функций, поэтому попытка использовать глобальную переменную базы данных вызовет у вас головную боль. - person Josh; 11.06.2013
comment
на ваш первый вопрос: да, даже без параметра версии выдает ошибку. - person Don Rhummy; 11.06.2013
comment
К вашей второй идее, это мысль. Все, что я читал (все учебники и вступления) в IndexedDB, рекомендует сохранять ссылку на БД. Ссылка, которую вы разместили, показывает, что ошибка связана с тем, что глобальная БД равна нулю, но в моем случае этого не произойдет, поскольку я проверяю ее в интервале перед вызовом функции обновления. Я обновляю только в том случае, если у меня есть действительный db. Причина, по которой сохранение db в глобальной переменной может быть полезным, заключается в том, что иногда вызов indexedDB.open(..) может занять более 1 секунды для вызова обратного вызова onsuccess. - person Don Rhummy; 11.06.2013
comment
Я могу ошибаться, но пока кажется, что пока я проверяю, что мой глобальный db не равен нулю, и я вызываю mydb.onversionchange = function(event) { mydb.close(); };, все в порядке. Я буду продолжать следить, но это, похоже, решает проблему. - person Don Rhummy; 11.06.2013
comment
Вы должны попробовать это без глобального. Это решит проблему блокировки, и вам не нужно проверять значение null/closed/etc или использовать трюки setTimeout/setInterval. Кроме того, onversionchange устарела. - person Josh; 11.06.2013
comment
означает ли это, что каждый раз, когда я хочу использовать базу данных, мне нужно будет снова вызывать асинхронный open()? - person Don Rhummy; 11.06.2013
comment
Как правило, да. Как правило, вас за это не наказывают. Вам просто нужно использовать шаблон обратного вызова. Если вы так сильно ненавидите шаблон обратного вызова, я бы порекомендовал изучить промисы. Например, посмотрите на исходный код Google Closure и его использование объекта dojo promises в связи с indexedDB. Или что-то вроде github.com/axemclion/jquery-indexeddb. - person Josh; 12.06.2013

Я думаю, что лучше всегда указывать номер версии. Если нет, то как вы собираетесь управлять обновлениями структуры БД? Если вы этого не сделаете, это хороший шанс, что вы попадете в ситуацию, когда те же версии базы данных на клиенте будут иметь другую структуру базы данных, и я не думаю, что это то, что вам нужно. Поэтому я бы предложил сохранить номер версии в переменной.

Также при работе с indexeddb вам необходимо будет предоставить план обновления со всех предыдущих версий до текущей. Это означает, что версия 4 имеет определенную структуру, но вам нужно будет получить ту же структуру с нуля, что и в версиях 1, 2 и 3.

person Kristof Degrave    schedule 11.06.2013
comment
как вы предлагаете сохранить этот номер версии при перезапуске Chrome? - person Don Rhummy; 11.06.2013
comment
Да, но хром не сохранит это при перезапуске. Представьте, что мое приложение может подключиться к серверу для проверки новых данных и изменений в схеме данных (через ajax). Если он это обнаружит, он изменит схему и обновит версию. Сохранение этой информации в переменной в javascript не будет (я думаю) существовать при перезапуске Chrome. Будет ли он? - person Don Rhummy; 11.06.2013
comment
Если это так, я бы посоветовал вам отслеживать все открытые соединения, чтобы вы могли их закрыть. Или закрывайте соединение каждый раз, когда выполняется операция. Именно так я создаю свою библиотеку linq2indexeddb. - person Kristof Degrave; 11.06.2013
comment
если вы вызываете db.close(), требуется ли еще раз вызывать асинхронный db.open() для запуска каких-либо переливаний? (Я предполагаю, что это так, но хотел быть уверенным) - person Don Rhummy; 11.06.2013
comment
Нет, вы можете вызвать его в событии завершения вашей транзакции. txn.oncomplete = функция (e){ e.target.close();} - person Kristof Degrave; 11.06.2013
comment
нет, я имел в виду, для следующей транзакции мне нужно снова вызывать асинхронное открытие? - person Don Rhummy; 11.06.2013
comment
Да, вам придется. Другой вариант — отслеживать все существующие подключения и закрывать их, когда вы хотите использовать базу данных с более высокой версией. Но во всех случаях после обновления вам придется установить новое соединение или повторно использовать соединение, которое вы установили для более высокой версии. - person Kristof Degrave; 12.06.2013
comment
Спасибо. Есть ли способ найти все открытые соединения, или мне нужно отслеживать их вручную? Кроме того, будет ли простой вызов db.close() закрывать все открытые соединения (мое приложение является одноэлементным) или это нужно делать для каждой транзакции? - person Don Rhummy; 12.06.2013
comment
Вам придется сделать это самостоятельно, также я бы разрешил открывать соединения, если вы не отслеживаете их. Это приведет к утечке памяти, потому что соединения останутся открытыми и больше не будут использоваться. - person Kristof Degrave; 12.06.2013
comment
Как вы отслеживаете открытые соединения? Разве я не кэшировал только объект db? Разве закрытие не будет всем, что мне нужно закрыть? Или мне нужно отслеживать каждый возврат при успешном выполнении транзакции или открытого вызова (т. е. каждый из них представляет собой отдельное открытое соединение)? - person Don Rhummy; 12.06.2013
comment
Вы можете сделать это, сохранив результат открытия dbconnection в переменной. например вар БД; indexeddb.open().onsuccess = функция (e) { var db = e.target.result; }. Если у вас их несколько, вы можете добавить их в массив. - person Kristof Degrave; 12.06.2013
comment
Зачем открывать его снова и снова вместо того, чтобы повторно использовать первый сохраненный db? Разве это не было бы более эффективным и использовало бы меньше памяти? - person Don Rhummy; 12.06.2013
comment
Да, но, как я уже сказал, в этом случае вам нужно следить за этим и закрыть все вызовы, прежде чем вы сможете выполнить обновление. Поверьте мне, стоимость открытия сейчас соединения не очень высока. - person Kristof Degrave; 13.06.2013