darkforest-eth/plugins

indexedDB instead of localStorage for plugins

modukon opened this issue · 5 comments

I did some research and found out that there is another way to store data on the client side.
We don't want to use localStorage because it contains the private key, but what about indexedDB?

It takes much more code to use it but I tried to make a small code example for people to use:
The 2 functions can be used to load and save one object to the database, inside the object you can put whatever you want (i think). But there is of course a size limit on how much you can store in the DB. The indexedDBName should be different for each plugin.

var indexedDBName = "pluginNameDB";

function loadDataFromIndexedDB() {
	let resolve, reject; let promise = new Promise((res, rej) => { resolve=res; reject=rej; });
	const dbOpen = window.indexedDB.open(indexedDBName, 1);
	dbOpen.onupgradeneeded = (ev) => {
		const db = ev.target.result;
		const store = db.createObjectStore("ObjStore", { keyPath: "id", autoIncrement: true, });
		store.createIndex("Idx", "Idx", { unique: false });
	};
	dbOpen.onsuccess = (ev) => {
		const db = ev.target.result;
		const store = db.transaction("ObjStore", "readonly").objectStore("ObjStore");
		const query = store.get(1);
		query.onerror = (ev) => {
			console.error(indexedDBName+" indexedDB query error", ev.target.error.message);
			reject(ev.target.error.message);
		};
		query.onsuccess = (ev) => {
			db.close();
			if (query.result) {
				resolve(query.result.Idx);
			} else {
				resolve(null);
			}
		};
	};
	return promise;
}

function writeDataToIndexedDB(data) {
	let resolve, reject; let promise = new Promise((res, rej) => { resolve=res; reject=rej; });
	const dbOpen = window.indexedDB.open(indexedDBName, 1);
	dbOpen.onupgradeneeded = (ev) => {
		const db = ev.target.result;
		const store = db.createObjectStore("ObjStore", { keyPath: "id", autoIncrement: true, });
		store.createIndex("Idx", "Idx", { unique: false });
	};
	dbOpen.onsuccess = (ev) => {
		const db = ev.target.result;
		const transaction = db.transaction("ObjStore", "readwrite");
		const store = transaction.objectStore("ObjStore");
		store.put({ id: 1, Idx: data });
		transaction.onerror = (ev) => {
			console.error(indexedDBName+" indexedDB transaction error", ev.target.error.message);
			reject(ev.target.error.message);
		};
		transaction.oncomplete = (ev) => {
			db.close();
			resolve();
		};
	};
	return promise;
}

What do you think? Can this be used?

Stx69 commented

ohhhh.... I found this about limits "Maximum storage size depends on 2 things, Browser and Disk space." 🥇 from here!!!

const quota = await navigator.storage.estimate();
const totalSpace = quota.quota;
const usedSpace = quota.usage;

const quota = await navigator.storage.estimate();

Ok this is also interesting, i made this small function to check the usage:

function logIndexedDBUsage() {
    navigator.storage.estimate().then(d => {
        let usage = Math.round(d.usage / d.quota * 1000)/10;
        console.log("indexedDB usage: "+usage+" %")
    })
}

it shows me 2.9%
i assume df stores the map data in the indexedDB (i have not mined a lot this round)
But this way you can see the limit of your mining? I assume when it reaches 100% you cant mine anymore

Stx69 commented

just small part of map here 66mb size and console say to me indexedDB usage: 3 % / browser Brave take from RAM around 3.5Gb , total ram of PC usage ~7.5%. I will ask for some BIGBROTHERS to do test... :D

ichub commented

We could provide an API for this in PersistentChunkStore

We could provide an API for this in PersistentChunkStore

Thanks for the reply! An official API sounds great.

Some ideas:
When I made this issue I did not think about deleting the data again, maybe the PluginManager could handle this somehow?
If you delete a plugin it deletes its storage in the DB too? It gets automatically deleted at the beginning of each round like the map data? The DB could be a way for plugins to share data with eachother? (dont know if this would be used)
Using the DB seems complicated, i think it would be enough if we can just easily store a single object with a name and we can put all the data we want into this object.