- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / test / data / indexeddb / perf_test.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 var overallTestStartTime = Date.now();
6 var kUseIndex = true;
7 var kDontUseIndex = false;
8 var kReadKeysOnly = true;
9 var kReadDataToo = false;
10 var kWriteToo = true;
11 var kDontWrite = false;
12 var kWriteSameStore = true;
13 var kWriteDifferentStore = false;
14 var kPlaceholderArg = false;
15 var kDontRead = false;
16 var kAlternateWithReads = true;
17
18 var tests = [
19 // Create a single small item in a single object store, then delete everything.
20   [testCreateAndDeleteDatabase,  1,    1,    1],
21 // Create many small items in a single object store, then delete everything.
22   [testCreateAndDeleteDatabase,  1000,  1,    1],
23 // Create a single small item in many object stores, then delete everything.
24   [testCreateAndDeleteDatabase,  1,    1000,  1],
25 // Create many large items in a single object store, then delete everything.
26   [testCreateAndDeleteDatabase,  1000,    1,  10000],
27 // Create a single small item in a single object store.
28   [testCreateKeysInStores, 1,     1,    1],
29 // Create many small items in a single object store.
30   [testCreateKeysInStores, 1000,   1,    1],
31 // Create a single small item in many object stores.
32   [testCreateKeysInStores, 1,     1000,  1],
33 // Create many large items in a single object store.
34   [testCreateKeysInStores, 1000,   1,    10000],
35 // Read one item per transaction.
36   [testRandomReadsAndWrites, 1000, 1, 0, 1000, kDontUseIndex],
37 // Read a few random items in each of many transactions.
38   [testRandomReadsAndWrites, 1000,  5,    0,  100, kDontUseIndex],
39 // Read many random items in each of a few transactions.
40   [testRandomReadsAndWrites, 1000,  500,   0,  5,  kDontUseIndex],
41 // Read many random items in each of a few transactions, in a large store.
42   [testRandomReadsAndWrites, 10000,  500,   0,  5,  kDontUseIndex],
43 // Read a few random items from an index, in each of many transactions.
44   [testRandomReadsAndWrites, 1000,  5,    0,  100, kUseIndex],
45 // Read many random items from an index, in each of a few transactions.
46   [testRandomReadsAndWrites, 1000,  500,   0,  5,  kUseIndex],
47 // Read many random items from an index, in each of a few transactions, in a
48 // large store.
49   [testRandomReadsAndWrites, 10000,  500,   0,  5,  kUseIndex],
50 // Read and write a few random items in each of many transactions.
51   [testRandomReadsAndWrites, 1000,  5,    5,  50, kDontUseIndex],
52 // Read and write a few random items, reading from an index, in each of many
53 // transactions.
54   [testRandomReadsAndWrites, 1000,  5,    5,  50, kUseIndex],
55 // Read a long, contiguous sequence of an object store via a cursor.
56   [testCursorReadsAndRandomWrites, kReadDataToo, kDontUseIndex, kDontWrite,
57    kPlaceholderArg],
58 // Read a sequence of an object store via a cursor, writing
59 // transformed values into another.
60   [testCursorReadsAndRandomWrites, kReadDataToo, kDontUseIndex, kWriteToo,
61    kWriteDifferentStore],
62 // Read a sequence of an object store via a cursor, writing
63 // transformed values into another.
64   [testCursorReadsAndRandomWrites, kReadDataToo, kDontUseIndex, kWriteToo,
65    kWriteSameStore],
66 // Read a sequence of an index into an object store via a cursor.
67   [testCursorReadsAndRandomWrites, kReadDataToo, kUseIndex, kDontWrite,
68    kPlaceholderArg],
69 // Read a sequence of an index into an object store via a key cursor.
70   [testCursorReadsAndRandomWrites, kReadKeysOnly, kUseIndex, kDontWrite,
71    kPlaceholderArg],
72 // Make batches of random writes into a store, triggered by periodic setTimeout
73 // calls.
74   [testSporadicWrites, 5, 0, kDontRead],
75 // Make large batches of random writes into a store, triggered by periodic
76 // setTimeout calls.
77   [testSporadicWrites, 50, 0, kDontRead],
78 // Make batches of random writes into a store with many indices, triggered by
79 // periodic setTimeout calls.
80   [testSporadicWrites, 5, 10, kDontRead],
81 // Make large batches of random writes into a store with many indices, triggered
82 // by periodic setTimeout calls.
83   [testSporadicWrites, 50, 10, kDontRead],
84 // Make batches of random writes into a store, triggered by periodic setTimeout
85 // calls.  Intersperse read transactions to test read-write lock conflicts.
86   [testSporadicWrites, 5, 0, kAlternateWithReads],
87 // Make large batches of random writes into a store, triggered by periodic
88 // setTimeout calls.  Intersperse read transactions to test read-write lock
89 // conflicts.
90   [testSporadicWrites, 50, 0, kAlternateWithReads],
91 // Make a small bunch of batches of reads of the same keys from an object store.
92   [testReadCache, 10, kDontUseIndex],
93 // Make a bunch of batches of reads of the same keys from an index.
94   [testReadCache, 50, kUseIndex],
95 // Make a small bunch of batches of reads of the same keys from an object store.
96   [testReadCache, 10, kDontUseIndex],
97 // Make a bunch of batches of reads of the same keys from an index.
98   [testReadCache, 50, kUseIndex],
99 // Create and delete an index on a store that already contains data [produces
100 // a timing result for each of creation and deletion].
101   [testCreateAndDeleteIndex, 5000],
102 // Walk through multiple cursors into the same object store, round-robin, until
103 // you've reached the end of each of them.
104   [testWalkingMultipleCursors, 5],
105 // Walk through many cursors into the same object store, round-robin, until
106 // you've reached the end of each of them.
107   [testWalkingMultipleCursors, 50],
108 // Open an object store cursor, then continue(key) to the last value.
109   [testCursorSeeks, 2000, 10, 4, kDontUseIndex],
110 // Open an index key cursor, then continue(key) to the last value.
111   [testCursorSeeks, 2000, 10, 4, kUseIndex],
112 ];
113
114 var currentTest = 0;
115
116 function test() {
117   runNextTest();
118 }
119
120 function runNextTest() {
121   var filter = window.location.hash.slice(1);
122   var test, f;
123   while (currentTest < tests.length) {
124     test = tests[currentTest];
125     f = test.shift();
126     if (!filter || f.name == filter)
127       break;
128     ++currentTest;
129   }
130
131   if (currentTest < tests.length) {
132     test.push(runNextTest);
133     f.apply(null, test);
134     ++currentTest;
135   } else {
136     onAllTestsComplete();
137   }
138 }
139
140 function onAllTestsComplete() {
141   var overallDuration = Date.now() - overallTestStartTime;
142   automation.addResult("OverallTestDuration", overallDuration);
143   automation.setDone();
144 }
145
146 // This is the only test that includes database creation and deletion in its
147 // results; the others just test specific operations.  To see only the
148 // creation/deletion without the specific operations used to build up the data
149 // in the object stores here, subtract off the results of
150 // testCreateKeysInStores.
151 function testCreateAndDeleteDatabase(
152     numKeys, numStores, payloadLength, onTestComplete) {
153   var testName = getDisplayName(arguments);
154   assert(numKeys >= 0);
155   assert(numStores >= 1);
156   var objectStoreNames = [];
157   for (var i=0; i < numStores; ++i) {
158     objectStoreNames.push("store " + i);
159   }
160   var value = stringOfLength(payloadLength);
161   function getValue() {
162     return value;
163   }
164
165   automation.setStatus("Creating database.");
166   var startTime = Date.now();
167
168   createDatabase(testName, objectStoreNames, onCreated, onError);
169
170   function onCreated(db) {
171     automation.setStatus("Constructing transaction.");
172     var transaction =
173         getTransaction(db, objectStoreNames, "readwrite",
174             function() { onValuesWritten(db); });
175     putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
176   }
177
178   function onValuesWritten(db) {
179     automation.setStatus("Deleting database.");
180     db.close();
181     deleteDatabase(testName, onDeleted);
182   }
183
184   function onDeleted() {
185     var duration = Date.now() - startTime;
186     automation.addResult(testName, duration);
187     automation.setStatus("Deleted database.");
188     onTestComplete();
189   }
190 }
191
192 function testCreateKeysInStores(
193     numKeys, numStores, payloadLength, onTestComplete) {
194   var testName = getDisplayName(arguments);
195   assert(numKeys >= 0);
196   assert(numStores >= 1);
197   var objectStoreNames = [];
198   for (var i=0; i < numStores; ++i) {
199     objectStoreNames.push("store " + i);
200   }
201   var value = stringOfLength(payloadLength);
202   function getValue() {
203     return value;
204   }
205
206   automation.setStatus("Creating database.");
207   createDatabase(testName, objectStoreNames, onCreated, onError);
208
209   function onCreated(db) {
210     automation.setStatus("Constructing transaction.");
211     var completionFunc =
212         getCompletionFunc(db, testName, Date.now(), onTestComplete);
213     var transaction =
214         getTransaction(db, objectStoreNames, "readwrite", completionFunc);
215     putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
216   }
217 }
218
219 function testRandomReadsAndWrites(
220     numKeys, numReadsPerTransaction, numWritesPerTransaction, numTransactions,
221     useIndexForReads, onTestComplete) {
222   var indexName;
223   if (useIndexForReads)
224     indexName = "index";
225   var testName = getDisplayName(arguments);
226   var objectStoreNames = ["store"];
227   var getKey = getSimpleKey;
228   var getValue = useIndexForReads ? getIndexableValue : getSimpleValue;
229
230   automation.setStatus("Creating database.");
231   var options;
232   if (useIndexForReads) {
233     options = [{
234       indexName: indexName,
235       indexKeyPath: "id",
236       indexIsUnique: false,
237       indexIsMultiEntry: false,
238     }];
239   }
240   createDatabase(testName, objectStoreNames, onCreated, onError, options);
241
242   function onCreated(db) {
243     automation.setStatus("Setting up test database.");
244     var transaction = getTransaction(db, objectStoreNames, "readwrite",
245         function() { onSetupComplete(db); });
246     putLinearValues(transaction, objectStoreNames, numKeys, null,
247         function() { return "test value"; });
248   }
249
250   function onSetupComplete(db) {
251     automation.setStatus("Setup complete.");
252     var completionFunc =
253         getCompletionFunc(db, testName, Date.now(), onTestComplete);
254     var mode = "readonly";
255     if (numWritesPerTransaction)
256       mode = "readwrite";
257     runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames, mode,
258         completionFunc);
259   }
260
261   function batchFunc(transaction) {
262     getRandomValues(transaction, objectStoreNames, numReadsPerTransaction,
263         numKeys, indexName, getKey);
264     putRandomValues(transaction, objectStoreNames, numWritesPerTransaction,
265         numKeys, getKey, getValue);
266   }
267 }
268
269 function testReadCache(numTransactions, useIndexForReads, onTestComplete) {
270   var numKeys = 10000;
271   var numReadsPerTransaction = 50;
272   var numTransactionsLeft = numTransactions;
273   var indexName;
274   if (useIndexForReads)
275     indexName = "index";
276   var testName = getDisplayName(arguments);
277   var objectStoreNames = ["store"];
278   var getKey = getSimpleKey;
279   var getValue = useIndexForReads ? getIndexableValue : getSimpleValue;
280   var keys = [];
281
282   for (var i=0; i < numReadsPerTransaction; ++i) {
283     keys.push(getKey(Math.floor(random() * numKeys)));
284   }
285
286   automation.setStatus("Creating database.");
287   var options;
288   if (useIndexForReads) {
289     options = [{
290       indexName: indexName,
291       indexKeyPath: "id",
292       indexIsUnique: false,
293       indexIsMultiEntry: false,
294     }];
295   }
296   createDatabase(testName, objectStoreNames, onCreated, onError, options);
297
298   function onCreated(db) {
299     automation.setStatus("Setting up test database.");
300     var transaction = getTransaction(db, objectStoreNames, "readwrite",
301         function() { onSetupComplete(db); });
302     putLinearValues(transaction, objectStoreNames, numKeys, getKey,
303         getValue);
304   }
305
306   var completionFunc;
307   function onSetupComplete(db) {
308     automation.setStatus("Setup complete.");
309     completionFunc =
310         getCompletionFunc(db, testName, Date.now(), onTestComplete);
311     runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames,
312         "readonly", completionFunc);
313   }
314
315   function batchFunc(transaction) {
316     getSpecificValues(transaction, objectStoreNames, indexName, keys);
317   }
318 }
319
320 function testCreateAndDeleteIndex(numKeys, onTestComplete) {
321   var testName = getDisplayName(arguments);
322   var objectStoreNames = ["store"];
323
324   automation.setStatus("Creating database.");
325   createDatabase(testName, objectStoreNames, onCreated, onError);
326
327   var startTime;
328   function onCreated(db) {
329     automation.setStatus("Initializing data.");
330     var transaction = getTransaction(db, objectStoreNames, "readwrite",
331         function() { onPopulated(db); });
332     putLinearValues(transaction, objectStoreNames, numKeys, null, getValue);
333   }
334
335   function getValue(i) {
336     return { firstName: i + " first name", lastName: i + " last name" };
337   }
338
339   function onPopulated(db) {
340     db.close();
341     automation.setStatus("Building index.");
342     startTime = Date.now();
343     var f = function(objectStore) {
344       objectStore.createIndex("index", "firstName", {unique: true});
345     };
346     alterObjectStores(testName, objectStoreNames, f, onIndexCreated, onError);
347   }
348
349   var indexCreationCompleteTime;
350   function onIndexCreated(db) {
351     db.close();
352     indexCreationCompleteTime = Date.now();
353     automation.addResult("testCreateIndex",
354         indexCreationCompleteTime - startTime);
355     var f = function(objectStore) {
356       objectStore.deleteIndex("index");
357     };
358     automation.setStatus("Deleting index.");
359     alterObjectStores(testName, objectStoreNames, f, onIndexDeleted, onError);
360   }
361
362   function onIndexDeleted(db) {
363     var duration = Date.now() - indexCreationCompleteTime;
364     // Ignore the cleanup time for this test.
365     automation.addResult("testDeleteIndex", duration);
366     automation.setStatus("Deleting database.");
367     db.close();
368     deleteDatabase(testName, onDeleted);
369   }
370
371   function onDeleted() {
372     automation.setStatus("Deleted database.");
373     onTestComplete();
374   }
375 }
376
377 function testCursorReadsAndRandomWrites(
378     readKeysOnly, useIndexForReads, writeAlso, sameStoreForWrites,
379     onTestComplete) {
380   // There's no key cursor unless you're reading from an index.
381   assert(useIndexForReads || !readKeysOnly);
382   // If we're writing to another store, having an index would constrain our
383   // writes, as we create both object stores with the same configurations.
384   // We could do that if needed, but it's simpler not to.
385   assert(!useIndexForReads || !writeAlso);
386   var numKeys = 10000;
387   var numReadsPerTransaction = 1000;
388   var testName = getDisplayName(arguments);
389   var objectStoreNames = ["input store"];
390   var outputStoreName;
391   if (writeAlso) {
392     if (sameStoreForWrites) {
393       outputStoreName = objectStoreNames[0];
394     } else {
395       outputStoreName = "output store";
396       objectStoreNames.push(outputStoreName);
397     }
398   }
399   var getKeyForRead = getSimpleKey;
400   var indexName;
401   if (useIndexForReads) {
402     indexName = "index";
403     getKeyForRead = function(i) {
404       // This depends on the implementations of getValuesFromCursor and
405       // getObjectValue.  We reverse the order of the iteration here so that
406       // setting up bounds from k to k+n with n>0 works.  Without this reversal,
407       // the upper bound is below the lower bound.
408       return getBackwardIndexKey(numKeys - i);
409     };
410   }
411
412   automation.setStatus("Creating database.");
413   var options;
414   if (useIndexForReads) {
415     options = [{
416       indexName: indexName,
417       indexKeyPath: "lastName", // depends on getBackwardIndexKey()
418       indexIsUnique: true,
419       indexIsMultiEntry: false,
420     }];
421   }
422   createDatabase(testName, objectStoreNames, onCreated, onError, options);
423
424   function onCreated(db) {
425     automation.setStatus("Setting up test database.");
426     var transaction = getTransaction(db, objectStoreNames, "readwrite",
427         function() { onSetupComplete(db); });
428     putLinearValues(transaction, objectStoreNames, numKeys, getSimpleKey,
429         getObjectValue);
430   }
431   function onSetupComplete(db) {
432     automation.setStatus("Setup complete.");
433     var completionFunc =
434         getCompletionFunc(db, testName, Date.now(), onTestComplete);
435     var mode = "readonly";
436     if (writeAlso)
437       mode = "readwrite";
438     var transaction =
439         getTransaction(db, objectStoreNames, mode, completionFunc);
440
441     getValuesFromCursor(
442         transaction, objectStoreNames[0], numReadsPerTransaction, numKeys,
443         indexName, getKeyForRead, readKeysOnly, outputStoreName);
444   }
445 }
446
447 function testSporadicWrites(
448     numOperationsPerTransaction, numIndices, alternateWithReads,
449     onTestComplete) {
450   var numKeys = 1000;
451   // With 30 transactions, spaced 50ms apart, we'll need at least 1.5s.
452   var numTransactions = 30;
453   if (alternateWithReads)
454     numTransactions *= 2;
455   var delayBetweenBatches = 50;
456   var indexName;
457   var testName = getDisplayName(arguments);
458   var numTransactionsLeft = numTransactions;
459   var objectStoreNames = ["store"];
460   var numTransactionsRunning = 0;
461
462   var getValue = getSimpleValue;
463   if (numIndices)
464     getValue = function (i) { return getNFieldObjectValue(i, numIndices); };
465
466   automation.setStatus("Creating database.");
467   var options = [];
468   for (var i=0; i < numIndices; ++i) {
469     var o = {};
470     o.indexName = "index " + i;
471     o.indexKeyPath = getNFieldName(i);
472     o.indexIsUnique = false;
473     o.indexIsMultiEntry = false;
474     options.push(o);
475   }
476   createDatabase(testName, objectStoreNames, onCreated, onError, options);
477
478   function onCreated(db) {
479     automation.setStatus("Setting up test database.");
480     var transaction = getTransaction(db, objectStoreNames, "readwrite",
481         function() { onSetupComplete(db); });
482     putLinearValues(transaction, objectStoreNames, numKeys, getSimpleKey,
483         getValue);
484   }
485   var completionFunc;
486   function onSetupComplete(db) {
487     automation.setStatus("Setup complete.");
488     completionFunc =
489         getCompletionFunc(db, testName, Date.now(), onTestComplete);
490     runOneBatch(db);
491   }
492
493   function runOneBatch(db) {
494     assert(numTransactionsLeft);
495     if (--numTransactionsLeft) {
496       setTimeout(function () { runOneBatch(db); }, delayBetweenBatches);
497     }
498
499     var mode, transaction;
500     if (alternateWithReads) {
501       ++numTransactionsRunning;
502       transaction =
503           getTransaction(db, objectStoreNames, "readonly", batchComplete);
504       getRandomValues(transaction, objectStoreNames,
505           numOperationsPerTransaction, numKeys);
506     }
507     ++numTransactionsRunning;
508     transaction =
509         getTransaction(db, objectStoreNames, "readwrite", batchComplete);
510     putRandomValues(transaction, objectStoreNames, numOperationsPerTransaction,
511         numKeys);
512
513     function batchComplete() {
514       assert(numTransactionsRunning);
515       if (!--numTransactionsRunning && !numTransactionsLeft)
516         completionFunc();
517     }
518   }
519 }
520
521 function testWalkingMultipleCursors(numCursors, onTestComplete) {
522   var numKeys = 1000;
523   var numHitsPerKey = 10;
524   var testName = getDisplayName(arguments);
525   var objectStoreNames = ["input store"];
526   var indexName = "index name";
527   var getKey = getSimpleKey;
528   var getValue = getIndexableValue;
529
530   automation.setStatus("Creating database.");
531   var options = [{
532     indexName: indexName,
533     indexKeyPath: "id",
534     indexIsUnique: false,
535     indexIsMultiEntry: false,
536   }];
537   createDatabase(testName, objectStoreNames, onCreated, onError, options);
538
539   function onCreated(db) {
540     automation.setStatus("Setting up test database.");
541     var transaction = getTransaction(db, objectStoreNames, "readwrite",
542         function() { onSetupComplete(db); });
543     // This loop adds the same value numHitsPerKey times for each key.
544     for (var i = 0; i < numHitsPerKey; ++i) {
545       putLinearValues(transaction, objectStoreNames, numKeys, getKeyFunc(i),
546           getValue);
547     }
548   }
549   // While the value is the same each time through the putLinearValues loop, we
550   // want the key to keep increaasing for each copy.
551   function getKeyFunc(k) {
552     return function(i) {
553       return getKey(k * numKeys + i);
554     };
555   }
556   var completionFunc;
557   function onSetupComplete(db) {
558     automation.setStatus("Setup complete.");
559     completionFunc =
560         getCompletionFunc(db, testName, Date.now(), onTestComplete);
561     var transaction =
562         getTransaction(db, objectStoreNames, "readonly", verifyComplete);
563
564     walkSeveralCursors(transaction, numKeys);
565   }
566   var responseCounts = [];
567   var cursorsRunning = numCursors;
568   function walkSeveralCursors(transaction, numKeys) {
569     var source = transaction.objectStore(objectStoreNames[0]).index(indexName);
570     var requests = [];
571     var continueCursorIndex = 0;
572     for (var i = 0; i < numCursors; ++i) {
573       var rand = Math.floor(random() * numKeys);
574       // Since we have numHitsPerKey copies of each value in the database,
575       // IDBKeyRange.only will return numHitsPerKey results, each referring to a
576       // different key with the matching value.
577       var request = source.openCursor(IDBKeyRange.only(getSimpleValue(rand)));
578       responseCounts.push(0);
579       request.onerror = onError;
580       request.onsuccess = function(event) {
581         assert(cursorsRunning);
582         var request = event.target;
583         if (!("requestIndex" in request)) {
584           assert(requests.length < numCursors);
585           request.requestIndex = requests.length;
586           requests.push(request);
587         }
588         var cursor = event.target.result;
589         if (cursor) {
590           assert(responseCounts[request.requestIndex] < numHitsPerKey);
591           ++responseCounts[request.requestIndex];
592         } else {
593           assert(responseCounts[request.requestIndex] == numHitsPerKey);
594           --cursorsRunning;
595         }
596         if (cursorsRunning) {
597           if (requests.length == numCursors) {
598             requests[continueCursorIndex++].result.continue();
599             continueCursorIndex %= numCursors;
600           }
601         }
602       };
603     }
604   }
605   function verifyComplete() {
606     assert(!cursorsRunning);
607     completionFunc();
608   }
609 }
610
611 function testCursorSeeks(
612     numKeys, numSeeksPerTransaction, numTransactions, useIndexForReads,
613     onTestComplete) {
614   var testName = getDisplayName(arguments);
615   var objectStoreNames = ["store"];
616   var getKey = useIndexForReads ? getForwardIndexKey : getSimpleKey;
617   var indexName;
618   if (useIndexForReads) {
619     indexName = "index";
620   }
621
622   automation.setStatus("Creating database.");
623   var options;
624   if (useIndexForReads) {
625     options = [{
626       indexName: indexName,
627       indexKeyPath: "firstName",
628       indexIsUnique: true,
629       indexIsMultiEntry: false,
630     }];
631   }
632   createDatabase(testName, objectStoreNames, onCreated, onError, options);
633
634   function onCreated(db) {
635     automation.setStatus("Setting up test database.");
636     var transaction = getTransaction(db, objectStoreNames, "readwrite",
637         function() { onSetupComplete(db); });
638     putLinearValues(transaction, objectStoreNames, numKeys, getSimpleKey,
639         getObjectValue);
640   }
641
642   function onSetupComplete(db) {
643     automation.setStatus("Setup complete.");
644     var completionFunc =
645           getCompletionFunc(db, testName, Date.now(), onTestComplete);
646     var mode = "readonly";
647     runTransactionBatch(db, numTransactions, batchFunc, objectStoreNames, mode,
648         completionFunc);
649   }
650
651   function batchFunc(transaction) {
652     for (var i in objectStoreNames) {
653       var source = transaction.objectStore(objectStoreNames[i]);
654       if (useIndexForReads)
655         source = source.index(indexName);
656       for (var j = 0; j < numSeeksPerTransaction; ++j) {
657         randomSeek(source);
658       }
659     }
660   }
661
662   function randomSeek(source) {
663     var request = useIndexForReads ? source.openKeyCursor()
664           : source.openCursor();
665     var first = true;
666     request.onerror = onError;
667     request.onsuccess = function() {
668       var cursor = request.result;
669       if (cursor && first) {
670         first = false;
671         cursor.continue(getKey(numKeys - 1));
672       }
673     };
674   }
675 }