-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add GetOptions for controlling offline get behaviour #655
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
Add option to allow the user to control where DocumentReference.getDocument() fetches from. By default, it fetches from the server (if possible) and falls back to the local cache. It's now possible to alternatively fetch from the local cache only, or to fetch from the server only (though in the server only case, latency compensation is still enabled). Still TODO: Also enable this for CollectionReferenece.getDocuments().
Enable GetOptions for CollectionReference / Query .getDocuments().
@wilhuff Pay particular attention to Firestore/Source/Core/FSTFirestoreClient.m since I'm not sure what I'm doing there is at all valid. |
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 mostly looks good except for the manual computation of views. These views are highly unlikely to conform to all the invariants we expect and will make the Cache-only snapshot substantially different form the first snapshot with default options.
FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES | ||
includeDocumentMetadataChanges:NO | ||
waitForSyncWhenOnline:NO]; | ||
FSTListenOptions *options = |
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.
All the changes in this file look like whitespace-only changes. Are you on clang 6?
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.
$ clang --version
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
I think I see what happened though. I (mistakenly) changed this file in 80a40bd and then backed out those changes in 58ccdba. However, the run of style.sh was in between.
If you like, I can revert this file completely. OTOH, style.sh thinks this is better, and since it's already fixed it...
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.
Hmm.. thinking about this further, I'll just back this out.
|
||
@import FirebaseFirestore; | ||
|
||
#import <FirebaseFirestore/FIRFirestore.h> |
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 should be covered by importing the module above.
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.
* acknowledge the writes. (This is expected to be called primarily when | ||
* offline, so waiting for the server doesn't make sense.) | ||
*/ | ||
- (void)updateData { |
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.
Perhaps name this in a way that indicates it doesn't wait? Perhaps updateDataNoWait
?
However, see above: it's not clear after calling this when a test makes an assertion about _testDoc
but not _testCol
why not. These tests would be clearer if you updated the things you cared about in the test.
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.
No longer relevant.
- (void)setUp { | ||
[super setUp]; | ||
|
||
_emptyDoc = [self documentRef]; |
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.
Factoring out these common parts is a good thing from a DRY perspective but it has the effect of making these tests tightly coupled, brittle, hard to read, and ultimately do more work than they need to do.
For example, in testGetDocumentWhileOnlineWithDefaultGetOptions
, it's not obvious why [self readDocumentForRef:_testDoc]
would necessarily satisfy the requirement that _testDoc "exists, is not from the cache, and matches the initial data".
This was the subject TotT 258 (from 2012), available externally here: https://testing.googleblog.com/2017/01/testing-on-toilet-keep-cause-and-effect.html
Please set up what documents you need per test.
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.
Undone the refactor commit.
XCTAssertEqualObjects(result.data, _testDocInitialData); | ||
} | ||
|
||
- (void)testGetDocumentsWhileOnlineWithDefaultGetOptions { |
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 test name differs from the above by a single 's'. Something like testGetCollection...
would make the distinction clearer.
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.
So does the api. :) (getDocumentWithCompletion vs getDocumentsWithCompletion. Though admittedly, they're at least on different objects.)
Done (throughout.)
Firestore/Source/API/FIRQuery.m
Outdated
code:FIRFirestoreErrorCodeUnavailable | ||
userInfo:@{ | ||
NSLocalizedDescriptionKey : | ||
@"Failed to get documents from server." |
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.
More error text to explain that this is a user-requested error condition (as above).
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.
"Failed to get documents from server. (However, these documents may exist in the local cache. Run again without setting FIRServer in the GetOptions to retrieve the cached documents.)"
nil, [NSError errorWithDomain:FIRFirestoreErrorDomain | ||
code:FIRFirestoreErrorCodeUnavailable | ||
userInfo:@{ | ||
NSLocalizedDescriptionKey : @"Failed to get document from server.", |
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.
More error text to explain that this is a user-requested error condition (as above).
Also in this case, this is a cache-only request so the server has nothing to do with it, right?
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.
Re cache vs server: Oops.
Updated text:
"Failed to get document from cache. (However, this document may exist on the server. Run again without setting FIRCache in the GetOptions to attempt to retrieve the document from the server.)"
oldDocuments:oldDocuments | ||
documentChanges:@[] | ||
fromCache:YES | ||
hasPendingWrites:NO |
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 seems like the wrong value. The local store will apply local mutations if present.
Also the documentChanges array being empty seems suspicious: the view processing code will normally make all the documents in the initial snapshot adds.
These are both examples of reasons why you shouldn't compose the view snapshots yourself.
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; ptal.
@@ -195,6 +196,11 @@ NS_SWIFT_NAME(DocumentReference) | |||
- (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion | |||
NS_SWIFT_NAME(getDocument(completion:)); | |||
|
|||
// clang-format off | |||
- (void)getDocumentWithOptions:(FIRGetOptions *)options completion:(FIRDocumentSnapshotBlock)completion | |||
NS_SWIFT_NAME(getDocument(options:completion:)); |
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.
Please add a swift-language test that verifies this looks correct from swift.
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.
* stale with respect to the value on the server.) For a single document, the | ||
* get will fail if the document doesn't exist. | ||
*/ | ||
typedef NS_ENUM(NSUInteger, FIRSource) { FIRDefault, FIRServer, FIRCache } NS_SWIFT_NAME(Source); |
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 order for swift bridging to work correctly the enumeration names need to all be suffixes of the type.
That means these need to be FIRSourceDefault, FIRSourceServer, etc.
I'm a little worried that "FIRSource" is too generic a term: we share the FIR namespace with the rest of Firebase, unfortunately. We should flag this as a potential issue in the API proposal.
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. (Also flagged in the api review doc.)
Still have a few (more in depth) comments to address.
Artifacts of an earlier attempt at this. :(
58ccdba
to
2a32091
Compare
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.
updates LGTM after nitpicking.
Still need to use FSTViewChange to actually compose the snapshot, but I know you're still working on that.
* try to give a consistent (server-retrieved) snapshot, or else revert to the | ||
* cache to provide a value. | ||
* | ||
* FIRServer causes Firestore to avoid the cache (generating an error if a |
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.
Comments should match the names: s/FIRServer/FIRSourceServer/
, here and throughout.
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.
NSLocalizedDescriptionKey : | ||
@"Failed to get document from server. (However, this " | ||
@"document does exist in the local cache. Run again " | ||
@"without setting FIRServer in the GetOptions to " |
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 line mixes FIR-prefixed Server (which should be FIRSourceServer, incidentally) with non-prefixed GetOptions. These should be consistent (and probably prefixed).
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.
I think the view computation you've added is sound. A few final nitpicks and we're good to go.
extern "C" NSArray<NSArray<id> *> *FIRQuerySnapshotGetDocChangesData(FIRQuerySnapshot *docs) { | ||
NSMutableArray<NSMutableArray<id> *> *result = [NSMutableArray array]; | ||
for (FIRDocumentChange *docChange in docs.documentChanges) { | ||
NSMutableArray<id> *docChangeData = [NSMutableArray array]; |
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.
Two bits of feedback here:
- You can create an array of known contents with far less typing by using an array literal, like so:
NSArray<id> *docChangeData = @[
@(docChange.type), docChange.document.documentID, docChange.document.data
];
- I'm not wild about making this untyped and unstructured. Historically the way we've done this is to make a test helper for concisely creating the expected result, usually something like what's in FSTHelpers.h and then defining isEqual/describe on it such that we can XCTAssertEqualObjects and have the right thing happen.
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.
FSTViewDocumentChanges *viewDocChanges = [view computeChangesWithDocuments:docs]; | ||
FSTViewChange *viewChange = [view applyChangesToDocuments:viewDocChanges]; | ||
FSTAssert(viewChange.limboChanges.count == 0, | ||
@"View returned limbo docs before target ack from the server."); |
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 assertion text makes no sense in this context (there won't be a target ack from the server since we're not starting a listen with the server).
Rather this should be @"View returned limbo documents during local-only query execution"
.
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.
@@ -228,11 +228,11 @@ func readDocument(at docRef: DocumentReference) { | |||
func readDocumentWithOptions(at docRef: DocumentReference) { | |||
docRef.getDocument(options:GetOptions.defaultOptions()) { document, error in | |||
} | |||
docRef.getDocument(options:GetOptions.init(source:Source.default)) { document, error in | |||
docRef.getDocument(options:GetOptions.init(source:GetSource.default)) { document, error in |
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.
Try making one of these use the unqualified form:
docRef.getDocument(options:GetOptions(source:.default)) { // ...
Also, explicitly calling init is not required. Using a constructor-style invocation calls the init function.
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.
Swift api limbo error message
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.
That last commit is a cure worse than the disease. I can see now that there's no easy way to construct a FIRDocumentChange in a test that would compare equal to the generated values so let's just drop the final commit.
Also, this API hasn't been formally approved yet, so let's squash and merge into the firestore-api-changes branch. |
36fc2da
to
893e56a
Compare
Add option to allow the user to control where DocumentReference.getDocument() and CollectionReference.getDocuments() fetches from. By default, it fetches from the server (if possible) and falls back to the local cache. It's now possible to alternatively fetch from the local cache only, or to fetch from the server only (though in the server only case, latency compensation is still enabled).
Add option to allow the user to control where DocumentReference.getDocument() and CollectionReference.getDocuments() fetches from. By default, it fetches from the server (if possible) and falls back to the local cache. It's now possible to alternatively fetch from the local cache only, or to fetch from the server only (though in the server only case, latency compensation is still enabled).
Add option to allow the user to control where DocumentReference.getDocument() and Query.getDocuments() fetches from. By default, they fetch from the server (if possible) and fall back to the local cache. It's now possible to alternatively fetch from the local cache only, or to
fetch from the server only (though in the server only case, latency compensation is still enabled).