use of com.google.firebase.firestore.local.TargetData in project firebase-android-sdk by firebase.
the class RemoteStore method raiseWatchSnapshot.
/**
* Takes a batch of changes from the Datastore, repackages them as a RemoteEvent, and passes that
* on to the listener, which is typically the SyncEngine.
*/
private void raiseWatchSnapshot(SnapshotVersion snapshotVersion) {
hardAssert(!snapshotVersion.equals(SnapshotVersion.NONE), "Can't raise event for unknown SnapshotVersion");
RemoteEvent remoteEvent = watchChangeAggregator.createRemoteEvent(snapshotVersion);
// applying the completed RemoteEvent.
for (Entry<Integer, TargetChange> entry : remoteEvent.getTargetChanges().entrySet()) {
TargetChange targetChange = entry.getValue();
if (!targetChange.getResumeToken().isEmpty()) {
int targetId = entry.getKey();
TargetData targetData = this.listenTargets.get(targetId);
// A watched target might have been removed already.
if (targetData != null) {
this.listenTargets.put(targetId, targetData.withResumeToken(targetChange.getResumeToken(), snapshotVersion));
}
}
}
// mismatches.
for (int targetId : remoteEvent.getTargetMismatches()) {
TargetData targetData = this.listenTargets.get(targetId);
// A watched target might have been removed already.
if (targetData != null) {
// Clear the resume token for the query, since we're in a known mismatch state.
this.listenTargets.put(targetId, targetData.withResumeToken(ByteString.EMPTY, targetData.getSnapshotVersion()));
// Cause a hard reset by unwatching and rewatching immediately, but deliberately don't send
// a resume token so that we get a full update.
this.sendUnwatchRequest(targetId);
// Mark the query we send as being on behalf of an existence filter mismatch, but don't
// actually retain that in listenTargets. This ensures that we flag the first re-listen this
// way without impacting future listens of this target (that might happen e.g. on
// reconnect).
TargetData requestTargetData = new TargetData(targetData.getTarget(), targetId, targetData.getSequenceNumber(), QueryPurpose.EXISTENCE_FILTER_MISMATCH);
this.sendWatchRequest(requestTargetData);
}
}
// Finally raise remote event
remoteStoreCallback.handleRemoteEvent(remoteEvent);
}
use of com.google.firebase.firestore.local.TargetData in project firebase-android-sdk by firebase.
the class WatchChangeAggregator method createRemoteEvent.
/**
* Converts the currently accumulated state into a remote event at the provided snapshot version.
* Resets the accumulated changes before returning.
*/
public RemoteEvent createRemoteEvent(SnapshotVersion snapshotVersion) {
Map<Integer, TargetChange> targetChanges = new HashMap<>();
for (Map.Entry<Integer, TargetState> entry : targetStates.entrySet()) {
int targetId = entry.getKey();
TargetState targetState = entry.getValue();
TargetData targetData = queryDataForActiveTarget(targetId);
if (targetData != null) {
if (targetState.isCurrent() && targetData.getTarget().isDocumentQuery()) {
// Document queries for document that don't exist can produce an empty result set. To
// update our local cache, we synthesize a document delete if we have not previously
// received the document. This resolves the limbo state of the document, removing it from
// limboDocumentRefs.
DocumentKey key = DocumentKey.fromPath(targetData.getTarget().getPath());
if (pendingDocumentUpdates.get(key) == null && !targetContainsDocument(targetId, key)) {
MutableDocument result = MutableDocument.newNoDocument(key, snapshotVersion);
removeDocumentFromTarget(targetId, key, result);
}
}
if (targetState.hasChanges()) {
targetChanges.put(targetId, targetState.toTargetChange());
targetState.clearChanges();
}
}
}
Set<DocumentKey> resolvedLimboDocuments = new HashSet<>();
// TODO(gsoltis): Expand on this comment once GC is available in the Android client.
for (Map.Entry<DocumentKey, Set<Integer>> entry : pendingDocumentTargetMapping.entrySet()) {
DocumentKey key = entry.getKey();
Set<Integer> targets = entry.getValue();
boolean isOnlyLimboTarget = true;
for (int targetId : targets) {
TargetData targetData = queryDataForActiveTarget(targetId);
if (targetData != null && !targetData.getPurpose().equals(QueryPurpose.LIMBO_RESOLUTION)) {
isOnlyLimboTarget = false;
break;
}
}
if (isOnlyLimboTarget) {
resolvedLimboDocuments.add(key);
}
}
for (MutableDocument document : pendingDocumentUpdates.values()) {
document.setReadTime(snapshotVersion);
}
RemoteEvent remoteEvent = new RemoteEvent(snapshotVersion, Collections.unmodifiableMap(targetChanges), Collections.unmodifiableSet(pendingTargetResets), Collections.unmodifiableMap(pendingDocumentUpdates), Collections.unmodifiableSet(resolvedLimboDocuments));
// Re-initialize the current state to ensure that we do not modify the generated RemoteEvent.
pendingDocumentUpdates = new HashMap<>();
pendingDocumentTargetMapping = new HashMap<>();
pendingTargetResets = new HashSet<>();
return remoteEvent;
}
use of com.google.firebase.firestore.local.TargetData in project firebase-android-sdk by firebase.
the class RemoteEventTest method testResumeTokenHandledPerTarget.
@Test
public void testResumeTokenHandledPerTarget() {
Map<Integer, TargetData> targetMap = activeQueries(1, 2);
WatchChangeAggregator aggregator = createAggregator(targetMap, noOutstandingResponses, noExistingKeys);
WatchTargetChange change1 = new WatchTargetChange(WatchTargetChangeType.Current, asList(1));
aggregator.handleTargetChange(change1);
ByteString resumeToken2 = ByteString.copyFromUtf8("resumeToken2");
WatchTargetChange change2 = new WatchTargetChange(WatchTargetChangeType.Current, asList(2), resumeToken2);
aggregator.handleTargetChange(change2);
RemoteEvent event = aggregator.createRemoteEvent(version(3));
assertEquals(2, event.getTargetChanges().size());
TargetChange mapping1 = targetChange(resumeToken, true, null, null, null);
assertEquals(mapping1, event.getTargetChanges().get(1));
TargetChange mapping2 = targetChange(resumeToken2, true, null, null, null);
assertEquals(mapping2, event.getTargetChanges().get(2));
}
use of com.google.firebase.firestore.local.TargetData in project firebase-android-sdk by firebase.
the class RemoteEventTest method testDoesNotSynthesizeDeleteWithExistingDocument.
@Test
public void testDoesNotSynthesizeDeleteWithExistingDocument() {
Map<Integer, TargetData> targetMap = activeLimboQueries("foo/doc", 1);
WatchTargetChange hasDocument = new WatchTargetChange(WatchTargetChangeType.Current, asList(1));
RemoteEvent event = createRemoteEvent(3, targetMap, noOutstandingResponses, keySet(key("foo/doc")), hasDocument);
assertEquals(0, event.getDocumentUpdates().size());
assertEquals(0, event.getResolvedLimboDocuments().size());
}
use of com.google.firebase.firestore.local.TargetData in project firebase-android-sdk by firebase.
the class RemoteEventTest method testWillIgnoreEventsForRemovedTargets.
@Test
public void testWillIgnoreEventsForRemovedTargets() {
Map<Integer, TargetData> targetMap = activeQueries();
MutableDocument doc1 = doc("docs/1", 1, map("value", 1));
// We're waiting for the unwatch ack
Map<Integer, Integer> outstanding = new HashMap<>();
outstanding.put(1, 1);
WatchChange change1 = new DocumentChange(asList(1), emptyList(), doc1.getKey(), doc1);
WatchChange change2 = new WatchTargetChange(WatchTargetChangeType.Removed, asList(1));
RemoteEvent event = createRemoteEvent(3, targetMap, outstanding, noExistingKeys, change1, change2);
assertEquals(version(3), event.getSnapshotVersion());
// doc1 is ignored because it was not apart of an active target.
assertEquals(0, event.getDocumentUpdates().size());
// Target 1 is ignored because it was removed
assertEquals(0, event.getTargetChanges().size());
}
Aggregations