Skip to content

Provide warning and potential workaround for iOS Safari bug described by #1670. #1854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 10, 2019

Conversation

mikelehen
Copy link
Contributor

  • On iOS 12.2 we proactively warn about the "internal error was encountered in
    Indexed Database Server" Bug.
  • When the bug is actually encountered on iOS >= 12.2 an < 13 we log a custom
    error message.

Both log message and error link to https://stackoverflow.com/q/56496296/110915 which
provides potential error handling / recovery example.

… by #1670.

* On iOS 12.2 we proactively warn about the "internal error was encountered in
  Indexed Database Server" Bug.
* When the bug is actually encountered on iOS >= 12.2 an < 13 we log a custom
  error message.

Both log message and error link to https://stackoverflow.com/q/56496296/110915 which
provides potential error handling / recovery example.
Copy link
Contributor

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some suggestions for you :)

@@ -15,9 +15,10 @@
* limitations under the License.
*/

import { getUA } from '@firebase/util';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it we don't have API reviews for @firebase/util? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @firebase/* packages are technically considered internal and aren't meant for public consumption. 🤷‍♂

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use Go-style variable names everywhere :)

.split('_')
.slice(0, 2)
.join('.')
: '-1';
return Number(version);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My gut feeling is that version numbers are better represented as strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, potentially... though strings don't necessarily compare properly. In any case, all I did is make getIOSVersion() match the existing getAndroidVersion() function, so I'm inclined to keep it for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Will bug you about it again when we need to block 12.3.1.

@@ -578,7 +599,9 @@ export class SimpleDbStore<
const cursorRequest = this.cursor({});
return new PersistencePromise((resolve, reject) => {
cursorRequest.onerror = (event: Event) => {
reject((event.target as IDBRequest).error!);
const error = (event.target as IDBRequest).error!;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could move this cast into checkForAndReportiOSError, which could then do an instanceof check..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need the cast result for reject(error) too though... and IDBRequest is an interface not a class, so no instanceof checks. :-/

return;
}
const iOSVersion = SimpleDb.getIOSVersion(getUA());
if (iOSVersion >= 12.2 && iOSVersion < 13) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Now I understand why we use a version number and not a version number string.
  2. Do we need to guard this check on the iOS version? We might just want to log the console message whenever we encounter the matching error string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the iOS error message is so generic, I am a little worried about making this check too open-ended and possibly having it hide future bugs in iOS Safari's IndexedDB implementation (I also named this "IOS_INDEXEDDB_BUG1" to allow for future bugs in the future 😛)... So I prefer keeping the version scoped for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Glad you didn't use IOS_INDEXEDDB_BUG_001 :)

reportedIOSError = true;
// Throw a global exception outside of this promise chain, for the user to
// potentially catch.
setTimeout(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be setImmediate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate

This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I learned something today.

(Never develop JS for browsers).

setTimeout(() => {
throw new FirestoreError(
'internal',
`IOS_INDEXEDDB_BUG1: IndexedDb has thrown '${IOS_ERROR}'. This is likely ` +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is IOS_INDEXEDDB_BUG1 intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Future proofing for the next iOS bug that bites us. 😬

Copy link
Contributor Author

@mikelehen mikelehen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! PTAL

@@ -15,9 +15,10 @@
* limitations under the License.
*/

import { getUA } from '@firebase/util';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @firebase/* packages are technically considered internal and aren't meant for public consumption. 🤷‍♂

.split('_')
.slice(0, 2)
.join('.')
: '-1';
return Number(version);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, potentially... though strings don't necessarily compare properly. In any case, all I did is make getIOSVersion() match the existing getAndroidVersion() function, so I'm inclined to keep it for now.

@@ -578,7 +599,9 @@ export class SimpleDbStore<
const cursorRequest = this.cursor({});
return new PersistencePromise((resolve, reject) => {
cursorRequest.onerror = (event: Event) => {
reject((event.target as IDBRequest).error!);
const error = (event.target as IDBRequest).error!;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need the cast result for reject(error) too though... and IDBRequest is an interface not a class, so no instanceof checks. :-/

return;
}
const iOSVersion = SimpleDb.getIOSVersion(getUA());
if (iOSVersion >= 12.2 && iOSVersion < 13) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the iOS error message is so generic, I am a little worried about making this check too open-ended and possibly having it hide future bugs in iOS Safari's IndexedDB implementation (I also named this "IOS_INDEXEDDB_BUG1" to allow for future bugs in the future 😛)... So I prefer keeping the version scoped for now.

reportedIOSError = true;
// Throw a global exception outside of this promise chain, for the user to
// potentially catch.
setTimeout(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate

This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.

setTimeout(() => {
throw new FirestoreError(
'internal',
`IOS_INDEXEDDB_BUG1: IndexedDb has thrown '${IOS_ERROR}'. This is likely ` +
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Future proofing for the next iOS bug that bites us. 😬

Copy link
Contributor

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ship it.

@@ -15,9 +15,10 @@
* limitations under the License.
*/

import { getUA } from '@firebase/util';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use Go-style variable names everywhere :)

.split('_')
.slice(0, 2)
.join('.')
: '-1';
return Number(version);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Will bug you about it again when we need to block 12.3.1.

return;
}
const iOSVersion = SimpleDb.getIOSVersion(getUA());
if (iOSVersion >= 12.2 && iOSVersion < 13) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Glad you didn't use IOS_INDEXEDDB_BUG_001 :)

reportedIOSError = true;
// Throw a global exception outside of this promise chain, for the user to
// potentially catch.
setTimeout(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I learned something today.

(Never develop JS for browsers).

@mikelehen mikelehen merged commit 6673a92 into master Jun 10, 2019
@mikelehen mikelehen deleted the mikelehen/indexeddb-error branch June 10, 2019 14:05
@firebase firebase locked and limited conversation to collaborators Oct 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants