summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js')
-rw-r--r--dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js204
1 files changed, 204 insertions, 0 deletions
diff --git a/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js b/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js
new file mode 100644
index 0000000000..33dc69b210
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js
@@ -0,0 +1,204 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* exported testSteps */
+async function testSteps() {
+ // A constant used to deal with small decrease in usage when transactions are
+ // transferred from the WAL file back into the original database, also called
+ // as checkpointing.
+ const cosmologicalConstant = 32768;
+
+ // The maximum number of threads that can be used for database activity at a
+ // single time.
+ const maxConnectionThreadCount = 20;
+
+ // The length of time that database connections will be held open after all
+ // transactions have completed before doing idle maintenance.
+ const connectionIdleMaintenanceMS = 2 * 1000;
+
+ const name = "test_connection_idle_maintenance_stop";
+ const abc = "abcdefghijklmnopqrstuvwxyz";
+
+ // IndexedDB on Android does `PRAGMA auto_vacuum = FULL`, so the freelist
+ // pages are moved to the end of the database file and the database file is
+ // truncated to remove the freelist pages at every transaction commit.
+ if (mozinfo.os == "android") {
+ info("Test disabled on Android for now");
+ return;
+ }
+
+ info("Setting pref");
+
+ Services.prefs.setIntPref(
+ "dom.indexedDB.connectionIdleMaintenance.pauseOnConnectionThreadMs",
+ 2 * connectionIdleMaintenanceMS
+ );
+
+ info("Forcing only one connection thread to be available");
+
+ let done = false;
+
+ // Create databases which will continuously keep their connection threads
+ // busy.
+ const completePromises = await (async function () {
+ let promises = [];
+
+ for (let index = 0; index < maxConnectionThreadCount - 1; index++) {
+ const request = indexedDB.open(name + "-" + index, 1);
+
+ {
+ const event = await expectingUpgrade(request);
+
+ const database = event.target.result;
+
+ const objectStore = database.createObjectStore(name);
+
+ objectStore.add("foo", 42);
+ }
+
+ const event = await expectingSuccess(request);
+
+ const database = event.target.result;
+
+ const transaction = database.transaction(name);
+
+ const objectStore = transaction.objectStore(name);
+
+ function doWork() {
+ const request = objectStore.get(42);
+ request.onsuccess = function () {
+ if (!done) {
+ doWork();
+ }
+ };
+ }
+
+ doWork();
+
+ const promise = new Promise(function (resolve) {
+ transaction.oncomplete = resolve;
+ });
+
+ promises.push(promise);
+ }
+
+ return promises;
+ })();
+
+ info("Creating database A");
+
+ // Create a database which will be used for stopping of the connection idle
+ // maintenance.
+ const databaseA = await (async function () {
+ const request = indexedDB.open(name + "-a", 1);
+
+ {
+ const event = await expectingUpgrade(request);
+
+ const database = event.target.result;
+
+ database.createObjectStore(name);
+ }
+
+ const event = await expectingSuccess(request);
+
+ const database = event.target.result;
+
+ return database;
+ })();
+
+ info("Creating database B");
+
+ // Create a database for checking of the connection idle maintenance.
+ {
+ const request = indexedDB.open(name + "-b", 1);
+
+ const event = await expectingUpgrade(request);
+
+ const database = event.target.result;
+
+ const objectStore = database.createObjectStore(name);
+
+ // Add lots of data...
+ for (let index = 0; index < 10000; index++) {
+ objectStore.add(abc, index);
+ }
+
+ // And then clear it so that maintenance has some space to reclaim.
+ objectStore.clear();
+
+ await expectingSuccess(request);
+ }
+
+ info("Getting database usage before maintenance");
+
+ const databaseUsageBeforeMaintenance = await new Promise(function (resolve) {
+ getCurrentUsage(function (request) {
+ resolve(request.result.databaseUsage);
+ });
+ });
+
+ info("Waiting for maintenance to start");
+
+ // This time is a double of connectionIdleMaintenanceMS which should be
+ // pessimistic enough to work with randomly slowed down threads in the
+ // chaos mode.
+ await new Promise(function (resolve) {
+ do_timeout(2 * connectionIdleMaintenanceMS, resolve);
+ });
+
+ info("Activating database A");
+
+ // Activate an open database which should trigger stopping of the connection
+ // idle maintenance of the database which had a lot of data.
+ {
+ const transaction = databaseA.transaction(name);
+
+ const objectStore = transaction.objectStore(name);
+
+ const request = objectStore.get(42);
+
+ await requestSucceeded(request);
+ }
+
+ info("Waiting for maintenance to finish");
+
+ // This time is a double of connectionIdleMaintenanceMS which should be
+ // pessimistic enough to work with randomly slowed down threads in the
+ // chaos mode.
+ await new Promise(function (resolve) {
+ do_timeout(2 * connectionIdleMaintenanceMS, resolve);
+ });
+
+ info("Getting database usage after maintenance");
+
+ const databaseUsageAfterMaintenance = await new Promise(function (resolve) {
+ getCurrentUsage(function (request) {
+ resolve(request.result.databaseUsage);
+ });
+ });
+
+ info(
+ "Database usage before: " +
+ databaseUsageBeforeMaintenance +
+ ". " +
+ "Database usage after: " +
+ databaseUsageAfterMaintenance
+ );
+
+ // Checkpointing done immediately after the maintenance can slightly decrease
+ // the usage even when the maintenance was stopped very early.
+ ok(
+ databaseUsageBeforeMaintenance - databaseUsageAfterMaintenance <
+ cosmologicalConstant,
+ "Maintenance did not significantly decrease database usage"
+ );
+
+ done = true;
+
+ info("Waiting for transactions to complete");
+
+ await Promise.all(completePromises);
+}