I needed to write 50k+ records to IndexedDB in multiple stores (tables). I noticed, that the more stores you write to, the slower the writes are. After some testing, it seems to me that the writes aren’t actually executed in parallel.
MDN says:
Only specify a readwrite transaction mode when necessary. You can concurrently run multiple readonly transactions with overlapping scopes, but you can have only one readwrite transaction for an object store.
as does W3:
Multiple “readwrite” transactions can’t run at the same time if their scopes are overlapping since that would mean that they can modify each other’s data in the middle of the transaction.
But I am writing to different stores (scopes) so i would expect the writes operations to be parallel too?
This is what i tried. Writing 50k records to store1 takes about 3 seconds as does writing to store2. Writing to both at the same time takes about 6 seconds. So correct me if I’m wrong, but that doesn’t seem to be parallel.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<script>
let db;
const request = indexedDB.open('idb_test', 1);
request.onupgradeneeded = () => {
const db = request.result;
db.createObjectStore('store1');
db.createObjectStore('store2');
};
request.onsuccess = () => {
db = request.result;
};
function createMany(storeName) {
const tx = db.transaction(storeName, 'readwrite');
for (let i = 0; i < 50000; i++) {
const key = crypto.randomUUID();
const val = i.toString();
tx.objectStore(storeName).put(val, key);
}
tx.oncomplete = () => {
console.timeEnd(storeName);
};
}
function store1() {
console.time('store1');
createMany('store1');
}
function store2() {
console.time('store2');
createMany('store2');
}
function bothStores() {
console.time('store1');
console.time('store2');
createMany('store1');
createMany('store2');
}
</script>
<body>
<button onclick="store1();">store1</button>
<button onclick="store2();">store2</button>
<button onclick="bothStores();">store1 + store2</button>
</body>
</html>
I also tried this with Web Worker just to see if it made any difference, but with same results.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<script>
// dev server is needed for this to work
const worker1 = new Worker('worker.js');
const worker2 = new Worker('worker.js');
worker1.onmessage = (event) => {
console.timeEnd(event.data);
};
worker2.onmessage = (event) => {
console.timeEnd(event.data);
};
function store1() {
console.time('store1');
worker1.postMessage('store1');
}
function store2() {
console.time('store2');
worker1.postMessage('store2');
}
function bothStores() {
console.time('store1');
console.time('store2');
worker1.postMessage('store1');
worker2.postMessage('store2');
}
</script>
<body>
<button onclick="store1();">store1</button>
<button onclick="store2();">store2</button>
<button onclick="bothStores();">store1 + store2</button>
</body>
</html>
worker.js:
let db;
const request = indexedDB.open('idb_test', 1);
request.onupgradeneeded = () => {
const db = request.result;
db.createObjectStore('store1');
db.createObjectStore('store2');
};
request.onsuccess = () => {
db = request.result;
};
function createMany(storeName) {
const tx = db.transaction(storeName, 'readwrite');
for (let i = 0; i < 50000; i++) {
const key = crypto.randomUUID();
const val = i.toString();
tx.objectStore(storeName).put(val, key);
}
return tx;
}
self.onmessage = (event) => {
const storeName = event.data;
const tx = createMany(storeName);
tx.oncomplete = () => {
postMessage(storeName);
};
};
So I tried profiling.
Chrome profiling
I think it’s pretty clear what’s happening. This is from Chrome 121. I’m pretty sure the grey blobs are the actual writes happening (it just says “Task”).
Firefox was a bit different but still not parallel. Also almost 2x faster than Chrome.
Firefox profiling
So am I doing something wrong or are IndexedDB writes actually not parallel?