indexeddbshim / IndexedDBShim

A polyfill for IndexedDB using WebSql
Other
968 stars 191 forks source link

Safari - "14 unable to open database file" #233

Closed Abrissirba closed 7 years ago

Abrissirba commented 8 years ago

I get the following error after I have done around 400 calls to indexedDB.open() in safari: (14 unable to open database file). After some googling I found this blogpost, http://zacster.blogspot.se/2015/10/safari-web-sql-14-unable-to-open.html, which says that safari doesn't garbage collect db handles from openDatabas calls.

The solution I'm currently using is to cache open databases in an object and reuse that connection. This is how the openDB(oldVersion) method at line 366 looks for me now.

function openDB(oldVersion) {
    openDatabases[name + oldVersion] = openDatabases[name + oldVersion] || window.openDatabase(name, 1, name, DEFAULT_DB_SIZE);
    var db = openDatabases[name + oldVersion];

Do you think this is a solid solution? If so I'm happy to make a PR.

Abrissirba commented 8 years ago

After some testing I discovered a new problem I think is related to my suggested solution. When my app has been running for a while, new transactions will fail with the error message: "database has been closed". I haven't found any really good way to see if the database has been closed or not without creating a transaction which will call the error callback.

My very ugly solution to this at the moment is to reuase the connections but before each transaction make a dummy transaction that checks that the database is open. If not I reopen the database again

(function (idbModules) {
    idbModules.DatabasePool = {};

    idbModules.DatabasePool.openConnections = {}

    var DEFAULT_DB_SIZE = 49 * 1024 * 1024;

    idbModules.DatabasePool.getConnection = function (name, version, size, callback) {
        size = size || DEFAULT_DB_SIZE;
        var cachedDB = idbModules.DatabasePool.openConnections[name + version]

        if (!cachedDB) {
            cachedDB = idbModules.DatabasePool.openConnections[name + version] = window.openDatabase(name, 1, name, size);
            callback(cachedDB);
        }
        else {
            cachedDB.transaction(function (tx) {
                callback(cachedDB)
            }, function () {
                cachedDB = idbModules.DatabasePool.openConnections[name + version] = window.openDatabase(name, 1, name, size);
                callback(cachedDB);
            })
        }        
    }
}(idbModules));

and in IDBTransaction.prototype.__executeRequests I wrap the transaction:

idbModules.DatabasePool.getConnection(me.db.name, me.db.version, null, function (db) {
    db.transaction(function executeRequests(tx) {
brettz9 commented 7 years ago

Recent code (including the latest 3.0.0 RCs) included such caching. Feel free to give it a shot. Closing, but feel free to report if you experience any difficulties.