tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }