From 684355cc5275d4ff9778ef0f0f4d7be42781752d Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 5 Mar 2017 18:48:35 -0500 Subject: [PATCH] Improve IndexedDB versioning and recover from version mismatch --- assets/javascripts/app/app.coffee | 1 - assets/javascripts/app/db.coffee | 82 +++++++++++++------ .../javascripts/templates/error_tmpl.coffee | 3 + 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/assets/javascripts/app/app.coffee b/assets/javascripts/app/app.coffee index 034417ad..731c05fe 100644 --- a/assets/javascripts/app/app.coffee +++ b/assets/javascripts/app/app.coffee @@ -201,7 +201,6 @@ return if @quotaExceeded @quotaExceeded = true new app.views.Notif 'QuotaExceeded', autoHide: null - Raven.captureMessage 'QuotaExceededError', level: 'warning' return onCookieBlocked: (key, value, actual) -> diff --git a/assets/javascripts/app/db.coffee b/assets/javascripts/app/db.coffee index 75688682..be99699e 100644 --- a/assets/javascripts/app/db.coffee +++ b/assets/javascripts/app/db.coffee @@ -1,9 +1,9 @@ class app.DB NAME = 'docs' + VERSION = 15 constructor: -> @useIndexedDB = @useIndexedDB() - @appVersion = @appVersion() @callbacks = [] db: (fn) -> @@ -13,7 +13,7 @@ class app.DB try @open = true - req = indexedDB.open(NAME, @schemaVersion()) + req = indexedDB.open(NAME, VERSION * 1e9 + @userVersion()) req.onsuccess = @onOpenSuccess req.onerror = @onOpenError req.onupgradeneeded = @onUpgradeNeeded @@ -26,21 +26,16 @@ class app.DB if db.objectStoreNames.length is 0 try db.close() + @open = false @fail 'empty' - return - - unless @checkedBuggyIDB - @checkedBuggyIDB = true - try - @idbTransaction(db, stores: $.makeArray(db.objectStoreNames)[0..1], mode: 'readwrite').abort() # https://bugs.webkit.org/show_bug.cgi?id=136937 - catch error - try db.close() - @fail 'buggy', error - return - - @runCallbacks(db) - @open = false - db.close() + else if error = @buggyIDB(db) + try db.close() + @open = false + @fail 'buggy', error + else + @runCallbacks(db) + @open = false + db.close() return onOpenError: (event) => @@ -48,15 +43,17 @@ class app.DB @open = false error = event.target.error - if error.name is 'QuotaExceededError' - @reset() - @db() - app.onQuotaExceeded() - else - @fail 'cant_open', error + switch error.name + when 'QuotaExceededError' + @onQuotaExceededError() + when 'VersionError' + @onVersionError() + else + @fail 'cant_open', error return fail: (reason, error) -> + @cachedDocs = null @useIndexedDB = false @reason or= reason @error or= error @@ -65,6 +62,39 @@ class app.DB Raven.captureException error, level: 'warning' if error return + onQuotaExceededError: -> + @reset() + @db() + app.onQuotaExceeded() + Raven.captureMessage 'QuotaExceededError', level: 'warning' + return + + onVersionError: -> + req = indexedDB.open(NAME) + req.onsuccess = (event) => + @handleVersionMismatch event.target.result.version + req.onerror = (event) -> + event.preventDefault() + @fail 'cant_open', error + return + + handleVersionMismatch: (actualVersion) -> + if Math.floor(actualVersion / 1e9) isnt VERSION + @fail 'version' + else + @setUserVersion actualVersion - VERSION * 1e9 + @db() + return + + buggyIDB: (db) -> + return if @checkedBuggyIDB + @checkedBuggyIDB = true + try + @idbTransaction(db, stores: $.makeArray(db.objectStoreNames)[0..1], mode: 'readwrite').abort() # https://bugs.webkit.org/show_bug.cgi?id=136937 + return + catch error + return error + runCallbacks: (db) -> fn(db) while fn = @callbacks.shift() return @@ -340,11 +370,9 @@ class app.DB app.settings.set('schema', @userVersion() + 1) return - schemaVersion: -> - @appVersion * 10 + @userVersion() + setUserVersion: (version) -> + app.settings.set('schema', version) + return userVersion: -> app.settings.get('schema') - - appVersion: -> - if app.config.env is 'production' then app.config.version else Math.floor(Date.now() / 1000) diff --git a/assets/javascripts/templates/error_tmpl.coffee b/assets/javascripts/templates/error_tmpl.coffee index e0dfef55..cfd49466 100644 --- a/assets/javascripts/templates/error_tmpl.coffee +++ b/assets/javascripts/templates/error_tmpl.coffee @@ -40,6 +40,9 @@ app.templates.offlineError = (reason, exception) -> """ An error occured when trying to open the IndexedDB database:
#{exception.name}: #{exception.message}
This could be because you're browsing in private mode or have disallowed offline storage on the domain. """ + when 'version' + """ The IndexedDB database was modified with a newer version of the app.
+ Reload the page to use offline mode. """ when 'empty' """ The IndexedDB database appears to be corrupted. Try resetting the app. """