DEV Community

Ivan V.
Ivan V.

Posted on

Handling IndexedDB Upgrade Version Conflict

IndexedDB is a large-scale, NoSQL storage system. It lets you store almost anything in the user's browser (google uses this technology inside google docs extensively).

But with great power comes great responsibility. When working with traditional ( server side ) databases you are in control of the database
schema and deploying new versions or rolling back to previous ones.

However, when the database is in client control, doing those things reliably is not easy.

The Issue

The issue that I'm going to address in this blog post is about the case when the user has multiple tabs (or windows ) open of the same site. Since all tabs are using the same underlying database, there is a possibility that a user when opening the new tab of the same site receives the new version of the application (just pushed to production) and that version brings changes to the database schema.

When that happens new tab has a newer version of the database and code running in that tab (since this is the newest version of the application ) is made to work with the new DB schema, however in the previous tab old version of the code works with different DB schema and if we leave that code running it can corrupt the database.

The Solution

Luckily creators of the indexedDB have foreseen this problem early in the design phase of the technology itself and they have given us some tools to handle such conflicts.

There are two events that we can listen to when we connect to the database:

  • The onversionchange event handler handles the version change event, fired when a database structure change was requested elsewhere ( new tab/ window)
  • The onblocked event handles the scenario when we try to upgrade the database (change to the new schema) but the database is still in use somewhere (another tab/window) even after the onversionchange event is sent.

I'm going to use these two events to safely upgrade the database.

These are the required steps:

  • Deploy the new version of the database ( increase the version number)
  • Close the database connection in tab 1 by listening to onversionchange
  • Force the user to reload tab 1 by creating the overlay and disabling any interaction with the site.

Here is how it looks with three chromium windows on the same site:
indexedDB upgrade process animation

The Code

//database version starts at 1
const version = 1
const dbName = "testing"
let db = null

// open the database connection
let openRequest = indexedDB.open(dbName, version)

openRequest.onsuccess = function(event) {
  // store the result of opening the database connection in the db variable.
  db = openRequest.result

  // add listener to handle database version change
  db.onversionchange = function() {
    //!important when version change is detected close the database immediately
    db.close() // hint:1
    // instruct the user to reload the page via popup
    // and block the interaction
    launchPopup($newVersionPopupContent)
  }
}
Enter fullscreen mode Exit fullscreen mode

To trigger the onversionchange event listener all we need to do is increase the version variable that is passed to the indexedDB.open method.

Code at hint:1 ( db.close) is very important, if we do not close the database connection when the onversionchange event is triggered new code ( tab 2 ) will also be blocked.

Remember, onversionchange is not triggered in the new tab ( new db - tab 2) this event will be triggered in all tabs with the old db.

Concluson

This is my solution to safely handle IndexedDB upgrade conflict. Some people might not like forcing the user to reload the page but that is the safest way, and it is highly unlikely that the user will be in the situation when two different application versions are running in the browser in the first place, but hey it can happen. It happened to me. So just make them reload.

I have made the demo code available via github.

If you want to learn more about IndexedDB Google web developer portal has a great intro to the subject.

Top comments (2)

Collapse
 
stephencweiss profile image
Stephen Charles Weiss

@ivandotv - do you know if this works even if the other tabs were offline at the time? (I would imagine it does, but haven't had a chance to test yet)

Collapse
 
ivandotv profile image
Ivan V.

I believe that is should work correctly.