这是indexloc提供的服务,不要输入任何密码
Skip to content

Enumerable circular references in many firestore objects #4258

@mattnathan

Description

@mattnathan

[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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions