Skip to content

Commit

Permalink
Add createTime to Document. (#6781)
Browse files Browse the repository at this point in the history
* Add createTime to Document.

* More createTime tests

* All tests pass except for Bundles tests.

* Don't use createTime in isEqual.

* getBaseDocument should always get results from remoteDocumentCache.

* Bring back tests.

* Add test that checks createTime gets updated after remote event.

* Add test for Set->RemoteEvent->Ack.

* try higher timeout.

Co-authored-by: Wu-Hui <wu.hui.github@gmail.com>
  • Loading branch information
ehsannas and wu-hui authored Dec 2, 2022
1 parent 3bd0316 commit 649e7f3
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 46 deletions.
22 changes: 5 additions & 17 deletions packages/firestore/src/local/local_documents_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import { FieldMask } from '../model/field_mask';
import {
calculateOverlayMutation,
mutationApplyToLocalView,
MutationType,
PatchMutation
} from '../model/mutation';
import { Overlay } from '../model/overlay';
Expand Down Expand Up @@ -92,7 +91,7 @@ export class LocalDocumentsView {
.getOverlay(transaction, key)
.next(value => {
overlay = value;
return this.getBaseDocument(transaction, key, overlay);
return this.remoteDocumentCache.getEntry(transaction, key);
})
.next(document => {
if (overlay !== null) {
Expand Down Expand Up @@ -424,11 +423,11 @@ export class LocalDocumentsView {
if (originalDocs.get(key)) {
return PersistencePromise.resolve();
}
return this.getBaseDocument(transaction, key, overlay).next(
doc => {
return this.remoteDocumentCache
.getEntry(transaction, key)
.next(doc => {
modifiedDocs = modifiedDocs.insert(key, doc);
}
);
});
}
)
.next(() =>
Expand Down Expand Up @@ -550,15 +549,4 @@ export class LocalDocumentsView {
return results;
});
}

/** Returns a base document that can be used to apply `overlay`. */
private getBaseDocument(
transaction: PersistenceTransaction,
key: DocumentKey,
overlay: Overlay | null
): PersistencePromise<MutableDocument> {
return overlay === null || overlay.mutation.type === MutationType.Patch
? this.remoteDocumentCache.getEntry(transaction, key)
: PersistencePromise.resolve(MutableDocument.newInvalidDocument(key));
}
}
43 changes: 35 additions & 8 deletions packages/firestore/src/model/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ export interface Document {
*/
readonly readTime: SnapshotVersion;

/**
* The timestamp at which the document was created. This value increases
* monotonically when a document is deleted then recreated. It can also be
* compared to `createTime` of other documents and the `readTime` of a query.
*/
readonly createTime: SnapshotVersion;

/** The underlying data of this document or an empty value if no data exists. */
readonly data: ObjectValue;

Expand Down Expand Up @@ -163,6 +170,7 @@ export class MutableDocument implements Document {
private documentType: DocumentType,
public version: SnapshotVersion,
public readTime: SnapshotVersion,
public createTime: SnapshotVersion,
public data: ObjectValue,
private documentState: DocumentState
) {}
Expand All @@ -175,8 +183,9 @@ export class MutableDocument implements Document {
return new MutableDocument(
documentKey,
DocumentType.INVALID,
SnapshotVersion.min(),
SnapshotVersion.min(),
/* version */ SnapshotVersion.min(),
/* readTime */ SnapshotVersion.min(),
/* createTime */ SnapshotVersion.min(),
ObjectValue.empty(),
DocumentState.SYNCED
);
Expand All @@ -189,13 +198,15 @@ export class MutableDocument implements Document {
static newFoundDocument(
documentKey: DocumentKey,
version: SnapshotVersion,
createTime: SnapshotVersion,
value: ObjectValue
): MutableDocument {
return new MutableDocument(
documentKey,
DocumentType.FOUND_DOCUMENT,
version,
SnapshotVersion.min(),
/* version */ version,
/* readTime */ SnapshotVersion.min(),
/* createTime */ createTime,
value,
DocumentState.SYNCED
);
Expand All @@ -209,8 +220,9 @@ export class MutableDocument implements Document {
return new MutableDocument(
documentKey,
DocumentType.NO_DOCUMENT,
version,
SnapshotVersion.min(),
/* version */ version,
/* readTime */ SnapshotVersion.min(),
/* createTime */ SnapshotVersion.min(),
ObjectValue.empty(),
DocumentState.SYNCED
);
Expand All @@ -228,8 +240,9 @@ export class MutableDocument implements Document {
return new MutableDocument(
documentKey,
DocumentType.UNKNOWN_DOCUMENT,
version,
SnapshotVersion.min(),
/* version */ version,
/* readTime */ SnapshotVersion.min(),
/* createTime */ SnapshotVersion.min(),
ObjectValue.empty(),
DocumentState.HAS_COMMITTED_MUTATIONS
);
Expand All @@ -243,6 +256,18 @@ export class MutableDocument implements Document {
version: SnapshotVersion,
value: ObjectValue
): MutableDocument {
// If a document is switching state from being an invalid or deleted
// document to a valid (FOUND_DOCUMENT) document, either due to receiving an
// update from Watch or due to applying a local set mutation on top
// of a deleted document, our best guess about its createTime would be the
// version at which the document transitioned to a FOUND_DOCUMENT.
if (
this.createTime.isEqual(SnapshotVersion.min()) &&
(this.documentType === DocumentType.NO_DOCUMENT ||
this.documentType === DocumentType.INVALID)
) {
this.createTime = version;
}
this.version = version;
this.documentType = DocumentType.FOUND_DOCUMENT;
this.data = value;
Expand Down Expand Up @@ -340,6 +365,7 @@ export class MutableDocument implements Document {
this.documentType,
this.version,
this.readTime,
this.createTime,
this.data.clone(),
this.documentState
);
Expand All @@ -350,6 +376,7 @@ export class MutableDocument implements Document {
`Document(${this.key}, ${this.version}, ${JSON.stringify(
this.data.value
)}, ` +
`{createTime: ${this.createTime}}), ` +
`{documentType: ${this.documentType}}), ` +
`{documentState: ${this.documentState}})`
);
Expand Down
31 changes: 27 additions & 4 deletions packages/firestore/src/remote/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,8 @@ export function toDocument(
return {
name: toName(serializer, document.key),
fields: document.data.value.mapValue.fields,
updateTime: toTimestamp(serializer, document.version.toTimestamp())
updateTime: toTimestamp(serializer, document.version.toTimestamp()),
createTime: toTimestamp(serializer, document.createTime.toTimestamp())
};
}

Expand All @@ -415,8 +416,19 @@ export function fromDocument(
): MutableDocument {
const key = fromName(serializer, document.name!);
const version = fromVersion(document.updateTime!);
// If we read a document from persistence that is missing createTime, it's due
// to older SDK versions not storing this information. In such cases, we'll
// set the createTime to zero. This can be removed in the long term.
const createTime = document.createTime
? fromVersion(document.createTime)
: SnapshotVersion.min();
const data = new ObjectValue({ mapValue: { fields: document.fields } });
const result = MutableDocument.newFoundDocument(key, version, data);
const result = MutableDocument.newFoundDocument(
key,
version,
createTime,
data
);
if (hasCommittedMutations) {
result.setHasCommittedMutations();
}
Expand All @@ -435,8 +447,11 @@ function fromFound(
assertPresent(doc.found.updateTime, 'doc.found.updateTime');
const key = fromName(serializer, doc.found.name);
const version = fromVersion(doc.found.updateTime);
const createTime = doc.found.createTime
? fromVersion(doc.found.createTime)
: SnapshotVersion.min();
const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
return MutableDocument.newFoundDocument(key, version, data);
return MutableDocument.newFoundDocument(key, version, createTime, data);
}

function fromMissing(
Expand Down Expand Up @@ -502,10 +517,18 @@ export function fromWatchChange(
);
const key = fromName(serializer, entityChange.document.name);
const version = fromVersion(entityChange.document.updateTime);
const createTime = entityChange.document.createTime
? fromVersion(entityChange.document.createTime)
: SnapshotVersion.min();
const data = new ObjectValue({
mapValue: { fields: entityChange.document.fields }
});
const doc = MutableDocument.newFoundDocument(key, version, data);
const doc = MutableDocument.newFoundDocument(
key,
version,
createTime,
data
);
const updatedTargetIds = entityChange.targetIds || [];
const removedTargetIds = entityChange.removedTargetIds || [];
watchChange = new DocumentWatchChange(
Expand Down
Loading

0 comments on commit 649e7f3

Please sign in to comment.