Skip to content

Instantly share code, notes, and snippets.

@shadow-light
Created April 17, 2024 05:22
Show Gist options
  • Save shadow-light/e00da7bf774f343917627b72a03e075c to your computer and use it in GitHub Desktop.
Save shadow-light/e00da7bf774f343917627b72a03e075c to your computer and use it in GitHub Desktop.
Electron IndexedDB corruption
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title>IndexedDB disk space test</title>
</head>
<body>
<h2>Versions</h2>
Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
<h2>Add MB to database</h2>
<div>
<input type='number' class='amount' value='10' />
<button class='add'>Add</button>
</div>
<h2>Stats</h2>
<table>
<tr>
<th>Real disk space</th>
<td>(accessible to OS)</td>
</tr>
<tr>
<td>Total</td>
<td class='real_total'></td>
</tr>
<tr>
<td>Used</td>
<td class='real_used'></td>
</tr>
<tr>
<td>Remaining</td>
<td class='real_remaining'></td>
</tr>
</table>
<table>
<tr>
<th>Browser disk space</th>
<td>(accessible to Chromium)</td>
</tr>
<tr>
<td>Total</td>
<td class='browser_total'></td>
</tr>
<tr>
<td>Used</td>
<td class='browser_used'></td>
</tr>
<tr>
<td>Remaining</td>
<td class='browser_remaining'></td>
</tr>
</table>
<script src="./renderer.js"></script>
</body>
</html>
/* IndexedDB may get wipped if storage limit reached
This test must be run with Electron set to save data in a disk with little space left
Linux guide to creating a small virtual disk: https://stackoverflow.com/questions/16044204/testing-out-of-disk-space-in-linux
1. Set path below to a disk with little space (e.g. 100 MB)
[Repeat next steps several times while observing IndexedDB in dev tools]
2. Rapidly click the add button beyond the storage's limit
3. Close and reopen the app
4. Keep repeating until IndexedDB gets reset and the following error appears in Electron's console:
[7451:0417/145053.223225:ERROR:indexed_db_bucket_context.cc(338)] Failed to open LevelDB database from /.../IndexedDB/file__0.indexeddb.leveldb,Corruption: checksum mismatch
[7451:0417/145053.601039:ERROR:indexed_db_bucket_context.cc(1501)] IndexedDB recovering from a corrupted (and deleted) database.
*/
const { app, BrowserWindow, ipcMain } = require('electron')
const check_disk_space = require('check-disk-space')
const path = require('node:path')
// TODO Set this to a virtual disk with e.g. 100 MB free
app.setPath('userData', '/tmp/my_disk')
// Function for creating window
function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html')
mainWindow.webContents.openDevTools()
}
// Create window
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// Provide browser with real disk space stats when it asks (for the disk used for user data)
ipcMain.handle('space', async event => {
return check_disk_space.default(app.getPath('userData'))
})
{
"name": "innate-brass-flood-7hli6",
"productName": "innate-brass-flood-7hli6",
"description": "My Electron application description",
"keywords": [],
"main": "./main.js",
"version": "1.0.0",
"author": "me",
"scripts": {
"start": "electron ."
},
"dependencies": {
"check-disk-space": "3.4.0"
},
"devDependencies": {
"electron": "30.0.0"
}
}
const {ipcRenderer, contextBridge} = require('electron')
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
// Method for asking OS what real disk space is
contextBridge.exposeInMainWorld('electronAPI', {
check_disk_space: async () => {
return ipcRenderer.invoke('space')
},
})
// Create db
const open_db = indexedDB.open('test', 1)
open_db.onupgradeneeded = (event) => {
event.target.result.createObjectStore("store", {autoIncrement: true})
}
// Utils
const amb = 1024 * 1024
const to_mb_str = bytes => parseInt(bytes / amb).toLocaleString()
const percent = (portion, total) => (portion / total * 100).toLocaleString() + '%'
// Update storage stats
async function update_stats (){
// Get real disk space
const real = await window.electronAPI.check_disk_space()
// Get browser space (total is app's usage + remaining)
const browser = await navigator.storage.estimate()
// Update real values
document.querySelector('.real_total').textContent = to_mb_str(real.size)
const real_used = real.size - real.free
document.querySelector('.real_used').textContent =
to_mb_str(real_used) + ` (${percent(real_used, real.size)})`
document.querySelector('.real_remaining').textContent =
to_mb_str(real.free) + ` (${percent(real.free, real.size)})`
// Update browser values
document.querySelector('.browser_total').textContent = to_mb_str(browser.quota)
document.querySelector('.browser_used').textContent =
to_mb_str(browser.usage) + ` (${percent(browser.usage, browser.quota)})`
const browser_remaining = browser.quota - browser.usage
document.querySelector('.browser_remaining').textContent =
to_mb_str(browser_remaining) + ` (${percent(browser_remaining, browser.quota)})`
}
// Initial update
update_stats()
// Handle addition of data
document.querySelector('.add').addEventListener('click', () => {
// Generate the data and add it to the database
const mb = parseInt(document.querySelector('.amount').value)
const data = new ArrayBuffer(mb * amb)
open_db.result.transaction('store', 'readwrite').objectStore('store').add(data)
// Check disk space after a delay so new addition affects it
setTimeout(update_stats, 1000)
})
body {
max-width: 600px;
margin: 50px auto;
font-family: monospace;
}
h2, table {
margin: 48px 0 24px 0;
}
th, td {
padding: 6px 24px 6px 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment