Skip to content
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

Enumerable circular references in many firestore objects #4258

Closed
mattnathan opened this issue Jan 5, 2021 · 1 comment · Fixed by #4298
Closed

Enumerable circular references in many firestore objects #4258

mattnathan opened this issue Jan 5, 2021 · 1 comment · Fixed by #4298
Assignees

Comments

@mattnathan
Copy link

mattnathan commented Jan 5, 2021

[REQUIRED] Describe your environment

  • Operating System version: Win 10
  • Browser version: 87.0.4280.88
  • Firebase SDK version: 8.2.1
  • Firebase Product: firestore+core

[REQUIRED] Describe the problem

Many of the firestore objects have enumerable/public properties that result in circular references when deeply scanning through these objects, for example when JSON.stringify a DocumentReference. The JSON example is just the most common deep scan method that might be applied to the object, there are other cases where deep scanning is used that can run foul of these circular references.

In our specific application we're using Vue + Vuex + Firestore. We store the firestore documents in our vuex store which exposes these objects to the Vue reactivity system. While this works most of the time there are a few standard corner cases where cyclic references cause issues for us

Deep watchers
If we have watchers that observe any object that contains a firestore reference then this causes the Vue reactivity system to stack overflow while it follows the infinite loop.

Vue(x) Dev Tools
When passing data between the page context and the dev tools/plugin context the Vue dev tools plugin attempts to serialise the Vuex state to a string using JSON.stringify. If the state contains any firestore objects with circular references then this fails and the plugin falls back to a slower, more memory intensive, json process that deals with this. This causes the vue dev tools to crash frequently with out-of-memory. It's totally understandable that this could be a Vue dev tools issue, but it could also be solved by removing circular references in firebase.

Steps to reproduce:

Try to JSON.stringify any of the following directly, or objects that have properties that reference these:

  • firebase.firestore.DocumentReference
  • firebase.firestore.CollectionReference
  • firebase.firestore.Query

There are probably plenty of others, but these are the ones that have been spotted in our app.

Relevant Code:

const app = firebase.initialiseApp();
const db = app.firestore();
const docRef = db.doc('docs/doc');
JSON.stringify(docRef); // an example of walking the object tree
// Outputs: Uncaught TypeError: Converting circular structure to JSON
//    --> starting at object with constructor 'FirebaseAppImpl'
//    |     property 'firebase_' -> object with constructor 'Object'
//    |     property 'apps' -> object with constructor 'Array'
//    --- index 0 closes the circle
//    at JSON.stringify (<anonymous>)

Places I've noticed circular references

  • The apps list in FirebaseAppImpl.firebase_.apps has a reference to the enclosing FirebaseAppImpl
  • The providers map in FirebaseAppImpl.container.providers contains a provider for the app that keeps track of the instance that contains the providers.

There might be others.

Working around the issue

In our app we've had to work around the issue in some very specific ways.

  1. We deep scan all firestore objects before they get put into our store and Object.defineProperty(..., {enumerable: false}) any DocumentReference.* we find. This fixes the vast majority of cases but has a performance impact and is flaky
  2. We are careful to avoid exposing non-required firestore objects to vue or vuex. This is harder than you'd think as even arguments to mutators in vuex count as exposure
@thebrianchen
Copy link

@mattnathan Thanks for filing such a detailed issue as well as your workaround!

Firestore classes like DocumentReference, Query, and CollectionReference hold references to the Firestore class. The Firestore class has a cyclic dependency with FirebaseApp, which creates the error you see when calling JSON.stringify. I'll investigate how to break this cycle next week, and keep you updated!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
3 participants