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

Using serverTimestamp to set a new doc and query immediately returns null as the timestamp value #1929

Closed
kerryjj opened this issue Jun 29, 2019 · 2 comments

Comments

@kerryjj
Copy link

kerryjj commented Jun 29, 2019

Environment

  • Operating System version: OSX Version 10.14.5
  • Browser version: iOS simulator Version 10.2.1 (SimulatorApp-880.5 CoreSimulator-587.35)
  • Firebase SDK version: [email protected]
  • Firebase Product: firestore
  • React Native: 0.59.8
  • React: 16.8.3

The problem

When using doc.set() to create a new doc a query for the list of docs before the set() has completed returns the new doc but with the timestamp value set to null.

I've replicated this on a single client by not waiting for the set() promise to return before running the query. I realise this isn't how a single client should be implemented. However, it does make me worry that if one client is running set() while a different client runs a query that it's possible for other clients to receive a doc where the timestamp value is null.

I also read a firebase developer advocate on stackoverflow stating that it's not possible for a document to be returned without the serverTimestamp value and that seems to make sense to me. So I wouldn't expect to be seeing this document returned in the query.

I've also tried to replicate this issue using the node.js SDK running from the CLI and can't replicate it there. However, I'm definitely seeing it in React Native using the Web SDK.

Steps to reproduce:

I've replicated in the iOS emulator and my react native application using this code.

    const ref = db.collection('bug');
    ref.doc().set({'ts': firebase.firestore.FieldValue.serverTimestamp()});
    ref.orderBy('ts', 'desc')
        .limit(1)
        .get()
        .then((docRefs) => {
      console.log(docRefs.docs[0].data());
    });

Here's the result of running it a few times

Object {
  "ts": null,
}
Object {
  "ts": null,
}
Object {
  "ts": null,
}
Object {
  "ts": null,
}
Object {
  "ts": null,
}
Object {
  "ts": null,
}
Object {
  "ts": null,
}
Object {
  "ts": e {
    "nanoseconds": 765000000,
    "seconds": 1561795381,
  },
}
Object {
  "ts": null,
}
Object {
  "ts": e {
    "nanoseconds": 828000000,
    "seconds": 1561795382,
  },
}

Relevant Code:

I'm not sure how to create a working database query on StackBlitz without also sharing database credentials.

Here's the code that replicates the issue though.

const ref = db.collection('bug');
    ref.doc().set({'ts': firebase.firestore.FieldValue.serverTimestamp()});
    ref.orderBy('ts', 'desc')
        .limit(1)
        .get()
        .then((docRefs) => {
      console.log(docRefs.docs[0].data());
    });
@schmidt-sebastian
Copy link
Contributor

schmidt-sebastian commented Jun 29, 2019

@kerryjj Thanks for filing this issue!

ServerTimestamps are resolved by our backend when they first receive the document write. The backend immediately resolves them to the time that the request was received, and persists a timestamp in the document. As you stated, once a document is written to the backend, we no longer see ServerTimestamps. Instead, ServerTimestamps become regular Timestamps.

The only client that knows that a Timestamp was set via FieldValue.serverTimestamp() is the one that originated the write. Since we raise a latency-compensated Snapshot as soon as you invoke the set() or update() API, we don't know what value the backend will use for the ServerTimestamp. Until we receive the timestamp, any snapshot for the affected document will (by default) return a null value for ServerTimestamp field. Note that this only happens on the client that issued the write, since other client do not know about the document change until after is is persisted by the backend(*).

To offer users a way to customize this behavior, we added SnapshotOptions. If you pass {serverTimestamps: 'estimate'} to DocumentSnapshot.get() or DocumentSnapshot.data(), your ServerTimestamps will resolve to the local client time while we wait for the backend to accept the write. If you pass {serverTimestamps: 'previous'} we will instead return the value that the field had before the ServerTimestamp was written (especially useful if you want to prevent UI flicker) .

(*) If you enable multi-tab persistence, latency-compensated documents are shared between tabs, in which case an unresolved ServerTimestamp can be seen across tabs.

@kerryjj
Copy link
Author

kerryjj commented Jun 30, 2019

OK great!

Thanks heaps @schmidt-sebastian

@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

No branches or pull requests

3 participants