-
Notifications
You must be signed in to change notification settings - Fork 938
Implement BundleCache for IDB and memory. #3170
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
Conversation
# Conflicts: # packages/firestore/src/util/bundle_reader.ts # packages/firestore/test/unit/util/bundle.test.ts
# Conflicts: # packages/firestore/src/util/bundle_reader.ts # packages/firestore/test/unit/util/bundle.test.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Some nits and one general question: Do we have a story for how to validate the actual JSON contents? We may want to validate the bundle data that we read from the user before writing it to persistence.
export interface Bundle { | ||
readonly id: string; | ||
readonly version: number; | ||
// When the saved bundle is built from the server SDKs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you point out in the documentation that this is to SnapshotVersion.MIN if the above is not true? In its current form, the comment raises the question why the type is not nullable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what do you mean here, sorry!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like:
Set to the snapshot version of the bundle if created by the Server SDKs. Otherwise set to SnapshotVersion.MIN.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
): PersistencePromise<Bundle | undefined>; | ||
|
||
/** | ||
* Saves a `BundleMetadata` from a bundle into local storage, using its id as the persistent key. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Wrap at 80 characters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
.get(bundleId) | ||
.next(bundle => { | ||
if (bundle) { | ||
return this.serializer.fromDbBundle(bundle!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to drop the bang.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
.get(queryName) | ||
.next(query => { | ||
if (query) { | ||
return this.serializer.fromDbNamedQuery(query!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to drop the bang.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
version: metadata.version!, | ||
createTime: this.remoteSerializer.fromVersion(metadata.createTime!) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, we should not add any more code to the main SDK for "optional" features. Do you mind exploring what it would entail to make these functions free-standing? They would then look like:
function fromBundleMetadata(serializer: RemoteSerializer, metadata: bundleProto.BundleMetadata): Bundle {
return {
id: metadata.id!,
version: metadata.version!,
createTime: serializer.fromVersion(metadata.createTime!)
};
}
If you have to make a ton of state public, you can look at what we did here:
class DatastoreImpl extends Datastore { |
DatastoreImp has public state, but in itself is private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, validation will be added in future PRs, when I implement bundle loading.
): PersistencePromise<Bundle | undefined>; | ||
|
||
/** | ||
* Saves a `BundleMetadata` from a bundle into local storage, using its id as the persistent key. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
.get(bundleId) | ||
.next(bundle => { | ||
if (bundle) { | ||
return this.serializer.fromDbBundle(bundle!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
.get(queryName) | ||
.next(query => { | ||
if (query) { | ||
return this.serializer.fromDbNamedQuery(query!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
version: metadata.version!, | ||
createTime: this.remoteSerializer.fromVersion(metadata.createTime!) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
export interface Bundle { | ||
readonly id: string; | ||
readonly version: number; | ||
// When the saved bundle is built from the server SDKs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what do you mean here, sorry!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some final nits, but looks great. Thanks for the updates!
export interface Bundle { | ||
readonly id: string; | ||
readonly version: number; | ||
// When the saved bundle is built from the server SDKs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like:
Set to the snapshot version of the bundle if created by the Server SDKs. Otherwise set to SnapshotVersion.MIN.
* @param source The data source to use. | ||
* @param bytesPerRead How many bytes each `read()` from the returned reader | ||
* will read. It is ignored if the passed in source does not provide | ||
* such control(example: ReadableStream). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space before (
.
Your indent also seems slightly off. We usually indent by four spaces (not 6), but I do see some counterexamples in our sources (both 6 and 0 exists, but 6 is an outlier).
} | ||
throw new Error( | ||
'Source of `toByteStreamReader` has to be Uint8Array, ArrayBuffer or ReadableStream' | ||
'Source of `toByteStreamReader` has to be a ArrayBuffer or ReadableStream' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/a/an/
} | ||
|
||
/** | ||
* Encodes a `BundledQuery` from bundle proto to a Query object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/from bundle/from a bundle/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
export interface NamedQuery { | ||
readonly name: string; | ||
readonly query: Query; | ||
// When the results for this query are read to the saved bundle. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** The time at which the results for this query were read. */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
bundledQuery: bundleProto.BundledQuery | ||
): Query { | ||
const query = serializer.remoteSerializer.convertQueryTargetToQuery({ | ||
parent: bundledQuery.parent!, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you file a bug to validate the bundle contents? As part of this we need to validate that the parent matches the Firestore project that we are loading the SDK into.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b/158846981
return { | ||
id: dbBundle.bundleId, | ||
createTime: serializer.fromDbTimestamp(dbBundle.createTime), | ||
version: dbBundle.version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is our versioning strategy? I wonder if we should reject any bundle with a version greater than 1. If so, please file a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b/158845787
} | ||
|
||
/** Encodes a BundleMetadata to a DbBundle. */ | ||
export function toDbBundle( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have an actual opinion, but I am a bit surprised that you moved this function out of the serializer file. I think it makes sense that even the free-standing functions are next to the main serializer, but I can also see why you moved them to. Just wanted to know whether you considered this?
No need to move them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved them back. I think putting them here also makes sense, but I don't feel strongly anyways.
import { expect } from 'chai'; | ||
import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; | ||
import { filter, orderBy, path } from '../../util/helpers'; | ||
import * as persistenceHelpers from './persistence_test_helpers'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I would suggest to avoid namespace imports (import * as
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
let persistence: IndexedDbPersistence; | ||
beforeEach(async () => { | ||
persistence = await persistenceHelpers.testIndexedDbPersistence({ | ||
synchronizeTabs: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would omit this setting. It poses questions as to why you added it here, but doesn't (yet) at much benefit - other than remind me that I forgot to reply to your question about multi-tab sync...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
No description provided.