test_connection_idle_maintenance_stop.js (5651B)
1 /** 2 * Any copyright is dedicated to the Public Domain. 3 * http://creativecommons.org/publicdomain/zero/1.0/ 4 */ 5 6 /* exported testSteps */ 7 async function testSteps() { 8 // A constant used to deal with small decrease in usage when transactions are 9 // transferred from the WAL file back into the original database, also called 10 // as checkpointing. 11 const cosmologicalConstant = 35000; 12 13 // The maximum number of threads that can be used for database activity at a 14 // single time. 15 const maxConnectionThreadCount = 20; 16 17 // The length of time that database connections will be held open after all 18 // transactions have completed before doing idle maintenance. 19 const connectionIdleMaintenanceMS = 2 * 1000; 20 21 const name = "test_connection_idle_maintenance_stop"; 22 const abc = "abcdefghijklmnopqrstuvwxyz"; 23 24 // IndexedDB on Android does `PRAGMA auto_vacuum = FULL`, so the freelist 25 // pages are moved to the end of the database file and the database file is 26 // truncated to remove the freelist pages at every transaction commit. 27 if (mozinfo.os == "android") { 28 info("Test disabled on Android for now"); 29 return; 30 } 31 32 info("Setting pref"); 33 34 Services.prefs.setIntPref( 35 "dom.indexedDB.connectionIdleMaintenance.pauseOnConnectionThreadMs", 36 2 * connectionIdleMaintenanceMS 37 ); 38 39 info("Forcing only one connection thread to be available"); 40 41 let done = false; 42 43 // Create databases which will continuously keep their connection threads 44 // busy. 45 const completePromises = await (async function () { 46 let promises = []; 47 48 for (let index = 0; index < maxConnectionThreadCount - 1; index++) { 49 const request = indexedDB.open(name + "-" + index, 1); 50 51 { 52 const event = await expectingUpgrade(request); 53 54 const database = event.target.result; 55 56 const objectStore = database.createObjectStore(name); 57 58 objectStore.add("foo", 42); 59 } 60 61 const event = await expectingSuccess(request); 62 63 const database = event.target.result; 64 65 const transaction = database.transaction(name); 66 67 const objectStore = transaction.objectStore(name); 68 69 function doWork() { 70 const request = objectStore.get(42); 71 request.onsuccess = function () { 72 if (!done) { 73 doWork(); 74 } 75 }; 76 } 77 78 doWork(); 79 80 const promise = new Promise(function (resolve) { 81 transaction.oncomplete = resolve; 82 }); 83 84 promises.push(promise); 85 } 86 87 return promises; 88 })(); 89 90 info("Creating database A"); 91 92 // Create a database which will be used for stopping of the connection idle 93 // maintenance. 94 const databaseA = await (async function () { 95 const request = indexedDB.open(name + "-a", 1); 96 97 { 98 const event = await expectingUpgrade(request); 99 100 const database = event.target.result; 101 102 database.createObjectStore(name); 103 } 104 105 const event = await expectingSuccess(request); 106 107 const database = event.target.result; 108 109 return database; 110 })(); 111 112 info("Creating database B"); 113 114 // Create a database for checking of the connection idle maintenance. 115 { 116 const request = indexedDB.open(name + "-b", 1); 117 118 const event = await expectingUpgrade(request); 119 120 const database = event.target.result; 121 122 const objectStore = database.createObjectStore(name); 123 124 // Add lots of data... 125 for (let index = 0; index < 10000; index++) { 126 objectStore.add(abc, index); 127 } 128 129 // And then clear it so that maintenance has some space to reclaim. 130 objectStore.clear(); 131 132 await expectingSuccess(request); 133 } 134 135 info("Getting database usage before maintenance"); 136 137 const databaseUsageBeforeMaintenance = await new Promise(function (resolve) { 138 getCurrentUsage(function (request) { 139 resolve(request.result.databaseUsage); 140 }); 141 }); 142 143 info("Waiting for maintenance to start"); 144 145 // This time is a double of connectionIdleMaintenanceMS which should be 146 // pessimistic enough to work with randomly slowed down threads in the 147 // chaos mode. 148 await new Promise(function (resolve) { 149 do_timeout(2 * connectionIdleMaintenanceMS, resolve); 150 }); 151 152 info("Activating database A"); 153 154 // Activate an open database which should trigger stopping of the connection 155 // idle maintenance of the database which had a lot of data. 156 { 157 const transaction = databaseA.transaction(name); 158 159 const objectStore = transaction.objectStore(name); 160 161 const request = objectStore.get(42); 162 163 await requestSucceeded(request); 164 } 165 166 info("Waiting for maintenance to finish"); 167 168 // This time is a double of connectionIdleMaintenanceMS which should be 169 // pessimistic enough to work with randomly slowed down threads in the 170 // chaos mode. 171 await new Promise(function (resolve) { 172 do_timeout(2 * connectionIdleMaintenanceMS, resolve); 173 }); 174 175 info("Getting database usage after maintenance"); 176 177 const databaseUsageAfterMaintenance = await new Promise(function (resolve) { 178 getCurrentUsage(function (request) { 179 resolve(request.result.databaseUsage); 180 }); 181 }); 182 183 info( 184 "Database usage before: " + 185 databaseUsageBeforeMaintenance + 186 ". " + 187 "Database usage after: " + 188 databaseUsageAfterMaintenance 189 ); 190 191 // Checkpointing done immediately after the maintenance can slightly decrease 192 // the usage even when the maintenance was stopped very early. 193 ok( 194 databaseUsageBeforeMaintenance - databaseUsageAfterMaintenance < 195 cosmologicalConstant, 196 "Maintenance did not significantly decrease database usage" 197 ); 198 199 done = true; 200 201 info("Waiting for transactions to complete"); 202 203 await Promise.all(completePromises); 204 }