naturalcrit / homebrewery

Create authentic looking D&D homebrews using only markdown
https://homebrewery.naturalcrit.com
MIT License
1.04k stars 318 forks source link

LocalStorage backups of brews #3070

Open calculuschild opened 9 months ago

calculuschild commented 9 months ago

It happens often enough that a user will mistakenly overwrite one of their brews or delete something they didn't mean to. Without Google Drive, the chances of getting the file back are slim, and require a lot of crossing fingers that a good enough version is in our database backups (not to mention how slow and obnoxious searching the backup system is in MongoDB Atlas).

As mentioned in #2975 , it might be a good idea to save a local copy of the work in the browser, similar to what we do in /new.

Given the size of brews being fairly small, I don't think we need to do anything as complex as diffs. In fact, it may be helpful to record multiple copies of each brew based on a logarithmic dropoff time or something.

Say we store 5 versions of each brew. Upon each autosave trigger, whether autosave is enabled or not, we check if any backup slots are available due to becoming expired, according to the following schedule. When a snapshot lower on the list expires, the others move down to take its place.

Backup Slot Time before expiry
1 2 minutes
2 10 minutes
3 1 hour
4 12 hours
5 2 days

To explain the sequence, imagine this:

A user opens the edit page of a brew. We mark the current time.

The user is typing away, and then stops for a moment, triggering an autosave. It hasn't yet been 2 minutes, so no additional Snapshot is taken at this time.

A little while later, the user stops again, and another autosave is triggered. We see that at least 2 minutes have passed since our marked time, so Snapshot 1 will be taken. Since there are no older snapshots in slots 4-5, it can move down to fill the lowest possible space, landing in slot 5, where it will wait for 2 days. We again mark the current time.

Now that slot 1 is open again, if another autosave occurs after 2 more minutes, we take another snapshot and move it down to slot 4. A few minutes later, and all 5 slots are now filled.

Now, when the next 2 minutes are up, slot 1 expires, and becomes available for a new snapshot. When 10 minutes are up, Slot 2 will expire, and the current slot 1 will take its place. When 1 hour is up, Slot 3 will expire, and the current slot 2 will take its place, and the current slot 1 will take slot 2. etc.

The benefits of this are that we only have to store 5 brews at a time, rather than hundreds of brews tracking every possible change, and the user doesn't lose large blocks of work if they only need to go back a few minutes. We also don't have to deal with complex diff tracking.

The downside is when each slot duration is only a maximum. I.e., after 2 days when Slot 5 expires, the 2-day backup slot is really only going back 12 hours to the previous Slot 4. But this could be fixed by just adding a 6th or more backup.

Noting that in this example, all expiry times are multiples of each other, we should be sure to only expire the oldest item if multiple expiry times happen to align at the same moment so all backups are not lost at the same time.

5e-Cleric commented 2 months ago

So, how would this fit with autosave to db? i see no mention of when to do the request, once a day? every 2 hours?

calculuschild commented 2 months ago

Autosave to DB would be untouched (3 seconds after the last activity).

This just adds additional backup copies to localstorage.

5e-Cleric commented 2 months ago

oh mb this is not to replace autosave, but to provide previous backup as a faux versioning system, right?

I mean, it would be an actual versioning system, even if rudimentary.

calculuschild commented 2 months ago

Yes. Essentially provides temporary a versioning system locally.

5e-Cleric commented 2 months ago

Adn about time stamps, i guess it is as simple as checking all 5 timestamp diffs in every autosave.

calculuschild commented 2 months ago

Check all timestamp diffs, starting with the largest diff (2 days), and expire only the first one found (oldest) that is past its expiry time. Then move all the other backups down one slot, and add a new backup at the lowest open slot.