IndexedDB删除的对象从未从Chrome上的磁盘中删除

时间:2016-04-04 19:56:00

标签: google-chrome typescript indexeddb

我正在尝试创建一个应用程序,每秒都会收到产品,并使用LRU策略更新固定大小的数据库(~300 MB)。虽然我没有例外添加新产品并从数据库中删除它们,但Chrome似乎从未删除.ldb和.bak文件。因此,我花了几千兆字节的硬盘,我总是达到配额限制。相同的代码在Firefox上运行完美。谁能解释一下我做错了什么?您可以在下面找到代码。

startExperiment(300 * 1024 * 1024);

function startExperiment(lrusize:number) {

var j = 0;

var productsToInsert = new HashMap<number, Product>();

window.indexedDB.deleteDatabase("ExampleDatabase");
var versionNumber = 1;
var stats = new Stats();
var sizeOfDatabase = 0;
var db = new ProductDatabase('ExampleDatabase', versionNumber, () => {
    db.getSizeOfDatabase((result) => {
        if(result == null) {
            sizeOfDatabase = 0;
        } else {
            sizeOfDatabase = result;
        }
    });
});





function randomizeArray() {
    var numOfMBS = Math.floor((Math.random() * (10 - 2) + 2) * 1024 * 1024);
    var bytearray = new Uint8Array(numOfMBS.valueOf());
    for (var i = 0; i < bytearray.length; i++) {
        bytearray[i] = Math.random() * (100 - 1) + 1;
    }
    return bytearray;
}


setInterval(function () {
    var readAverage = stats.getReadTimesAverage();
    var writeAverage = stats.getWriteTimesAverage();
    var deleteAverage = stats.getDeleteTimesAverage();

    console.log("Num of insertions : " + j + " | Read average : " + readAverage + " | Write average : " + writeAverage + " | Delete average : " + deleteAverage);
}, 5000);

setInterval(function () {
    var bytearray = randomizeArray();
    var identifier = j++;
    var timestamp = Date.now();
    db.getProduct(identifier, (product) => {
        if (product == null) {
            var newProduct = new Product(identifier, timestamp, 0, bytearray);
            var size = memorySizeOf(newProduct);
            newProduct.sizeInBytes = size;
            productsToInsert.set(identifier, newProduct);
        }
    });
}, 1000);


function updateLRU() {
    var tmpList:Product[] = [];
    var keys = productsToInsert.keys();
    var currentBytesToBeInserted = 0;
    for (var i = 0; i < keys.length; i++) {
        var product = productsToInsert.get(keys[i]);
        tmpList.push(product);
        currentBytesToBeInserted += product.sizeInBytes;
    }

    var currentSize = sizeOfDatabase + currentBytesToBeInserted;
    if (currentSize > lrusize) {
        var bytesToRemove = currentSize - lrusize;
        db.deleteProducts(bytesToRemove, stats, () => {
            sizeOfDatabase -= bytesToRemove;
            addFragments(tmpList);
        });
    } else {
        addProducts(tmpList);
    }
}


function addProducts(tmpList:Product[]) {

    var product = tmpList[0];
    var startAddProductTs = Date.now();
    db.addProduct(product, () => {
        var stopAddProductTs = Date.now();
        stats.addWriteTimes(stopAddProductTs - startAddProductTs);
        sizeOfDatabase += product.sizeInBytes;
        tmpList.shift();
        productsToInsert.delete(product.productId);
        if(tmpList.length > 0) {
            addProducts(tmpList);
        } else {
            db.addDBSize(sizeOfDatabase, () => {

            });
        }
    });
}

setInterval(function () {

    updateLRU();

}, 20000);


}





class ProductDatabase {

private db;

constructor(private name:string, private version:number, callback:() => void) {
    this.openDatabase(callback);

}


openDatabase(callback:() => void) {
    var openDatabaseRequest = window.indexedDB.open(this.name, this.version);
    openDatabaseRequest.onupgradeneeded = this.upgrade;
    openDatabaseRequest.onsuccess = () => {
        this.db = openDatabaseRequest.result;
        callback();
    }

}

upgrade(event:any) {
    var store = event.target.result.createObjectStore("products", {keyPath: 'productId'});
    store.createIndex('by_timestamp', "timestamp", {unique: true});
    event.target.result.createObjectStore("dbsize", {keyPath: 'sizeId'});
}


getProduct(productId:number, callback:(result:Product) => void) {


    var productStore = this.db.transaction(["products"], "readonly").objectStore('products');

    var query = productStore.get(productId);

    query.onsuccess = () => {
        var product = query.result;
        callback(product);
    }

    query.onerror = () => {
        console.error("Read product error : " + query.error);
    }


}


addDBSize(dbSize:number, callback:() => void) {


    var transaction = this.db.transaction('dbsize', 'readwrite');
    var productStore = transaction.objectStore('dbsize');
    var newSize = {'sizeId': 1, 'bytelength': dbSize};
    var request = productStore.put(newSize);


    request.onerror = () => {
        console.log("Unsuccessful request with error : " + request.error);

    }


    transaction.oncomplete = () => {
        callback();
    }

    transaction.onerror = () => {
        console.error("fucking error : " + transaction.error);

    }


    transaction.onabort = () => {
        console.error("Shit. transaction is aborted with error : " + transaction.error);

    }


}

addCachedProducts(productList:Array<Product>, callback:() => void) {


    var transaction = this.db.transaction('products', 'readwrite');
    var productStore = transaction.objectStore('products');
    for (var i = 0; i < productList.length; i++) {
        productStore.add(productList[i]);
    }

    transaction.oncomplete = () => {
        callback();
    }

    transaction.onabort = () => {
        console.error("Shit. transaction is aborted with error : " + transaction.error);
    }

}

getNumberOfProducts(callback:(result:number) => void) {

    var productStore = this.db.transaction('products', 'readonly').objectStore('products');
    var query = productStore.count();
    query.onsuccess = () => {
        var result = query.result;
        callback(result);
    }

    query.onerror = () => {
        console.error("Read number of products error : " + query.error);
    }

}

getSizeOfDatabase(callback:(result:number) => void) {

    var productStore = this.db.transaction('dbsize', "readonly").objectStore('dbsize');

    var query = productStore.get(1);

    query.onsuccess = () => {
        var product = query.result;
        callback(product);
    }

    query.onerror = () => {
        console.error("Read databasesize error : " + query.error);
    }

}

deleteProducts(numOfBytes:number, stats:Stats, callback:() => void) {


    var transaction = this.db.transaction('products', 'readwrite');
    var productStore = transaction.objectStore('products');
    var index = productStore.index('by_timestamp');

    var request = index.openCursor();
    request.onsuccess = function () {
        var cursor = request.result;
        if (cursor) {
            var cursorBytes = cursor.value.sizeInBytes;
            var startDeleteTs = Date.now();
            var deleteRequest = cursor.delete();
            deleteRequest.onsuccess = () => {
                var stopDeleteTs = Date.now();
                stats.addDeleteTimes(stopDeleteTs - startDeleteTs);
                numOfBytes -= cursorBytes;
                if (numOfBytes > 0) {
                    cursor.continue();
                }
            }

            deleteRequest.onerror = () => {
                console.error("Delete product error : " + deleteRequest.error);
            }
        }

    }


    transaction.oncomplete = () => {
        callback();
    }

    transaction.onabort = () => {
        console.log("Delete transaction aborted with error : " + transaction.error);
    }

}


addProduct(product:Product, callback:() => void) {


        var transaction = this.db.transaction('products', 'readwrite');
        var productStore = transaction.objectStore('products');
        var request = productStore.put(product);


        request.onerror = () => {
            console.log("Unsuccessful request with error : " + request.error);
        }


        transaction.oncomplete = () => {
            callback();
        }

        transaction.onerror = () => {
            console.error("fucking error : " + transaction.error);
        }


        transaction.onabort = () => {
            console.error("Shit. transaction is aborted with error : " + transaction.error);
        }



}

}

2 个答案:

答案 0 :(得分:2)

在Chrome中,通过IndexedDB API删除数据并将其从磁盘中删除之间存在延迟。通常那很好。但根据我的经验,sometimes it never gets deleted from disk, which is really bad when the user has exceeded their quota because then you can never store any more data even if you delete everything

答案 1 :(得分:0)

谢谢dumbmatter。不幸的是,我的经验也告诉我,Chrome永远不会删除不必要的文件。问题是Chrome中的IndexedDB使用LevelDB作为实现,它很少调用compact。但是,我找到了一个使用PouchDB的解决方案,它利用了IndexedDB和LevelDB api,我可以显式调用compact并删除我不必要的文件。