use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class LocalStore method applyRemoteEvent.
/**
* Updates the "ground-state" (remote) documents. We assume that the remote event reflects any
* write batches that have been acknowledged or rejected (i.e. we do not re-apply local mutations
* to updates from this event).
*
* <p>LocalDocuments are re-calculated if there are remaining mutations in the queue.
*/
public ImmutableSortedMap<DocumentKey, Document> applyRemoteEvent(RemoteEvent remoteEvent) {
SnapshotVersion remoteVersion = remoteEvent.getSnapshotVersion();
// TODO: Call queryEngine.handleDocumentChange() appropriately.
return persistence.runTransaction("Apply remote event", () -> {
Map<Integer, TargetChange> targetChanges = remoteEvent.getTargetChanges();
long sequenceNumber = persistence.getReferenceDelegate().getCurrentSequenceNumber();
for (Map.Entry<Integer, TargetChange> entry : targetChanges.entrySet()) {
Integer boxedTargetId = entry.getKey();
int targetId = boxedTargetId;
TargetChange change = entry.getValue();
TargetData oldTargetData = queryDataByTarget.get(targetId);
if (oldTargetData == null) {
// we persist the updated query data along with the updated assignment.
continue;
}
targetCache.removeMatchingKeys(change.getRemovedDocuments(), targetId);
targetCache.addMatchingKeys(change.getAddedDocuments(), targetId);
TargetData newTargetData = oldTargetData.withSequenceNumber(sequenceNumber);
if (remoteEvent.getTargetMismatches().contains(targetId)) {
newTargetData = newTargetData.withResumeToken(ByteString.EMPTY, SnapshotVersion.NONE).withLastLimboFreeSnapshotVersion(SnapshotVersion.NONE);
} else if (!change.getResumeToken().isEmpty()) {
newTargetData = newTargetData.withResumeToken(change.getResumeToken(), remoteEvent.getSnapshotVersion());
}
queryDataByTarget.put(targetId, newTargetData);
// since the last update).
if (shouldPersistTargetData(oldTargetData, newTargetData, change)) {
targetCache.updateTargetData(newTargetData);
}
}
Map<DocumentKey, MutableDocument> documentUpdates = remoteEvent.getDocumentUpdates();
Set<DocumentKey> limboDocuments = remoteEvent.getResolvedLimboDocuments();
for (DocumentKey key : documentUpdates.keySet()) {
if (limboDocuments.contains(key)) {
persistence.getReferenceDelegate().updateLimboDocument(key);
}
}
DocumentChangeResult result = populateDocumentChanges(documentUpdates);
Map<DocumentKey, MutableDocument> changedDocs = result.changedDocuments;
// HACK: The only reason we allow snapshot version NONE is so that we can synthesize
// remote events when we get permission denied errors while trying to resolve the
// state of a locally cached document that is in limbo.
SnapshotVersion lastRemoteVersion = targetCache.getLastRemoteSnapshotVersion();
if (!remoteVersion.equals(SnapshotVersion.NONE)) {
hardAssert(remoteVersion.compareTo(lastRemoteVersion) >= 0, "Watch stream reverted to previous snapshot?? (%s < %s)", remoteVersion, lastRemoteVersion);
targetCache.setLastRemoteSnapshotVersion(remoteVersion);
}
return localDocuments.getLocalViewOfDocuments(changedDocs, result.existenceChangedKeys);
});
}
use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class SQLiteIndexManager method start.
@Override
public void start() {
Map<Integer, FieldIndex.IndexState> indexStates = new HashMap<>();
// Fetch all index states if persisted for the user. These states contain per user information
// on how up to date the index is.
db.query("SELECT index_id, sequence_number, read_time_seconds, read_time_nanos, document_key, " + "largest_batch_id FROM index_state WHERE uid = ?").binding(uid).forEach(row -> {
int indexId = row.getInt(0);
long sequenceNumber = row.getLong(1);
SnapshotVersion readTime = new SnapshotVersion(new Timestamp(row.getLong(2), row.getInt(3)));
DocumentKey documentKey = DocumentKey.fromPath(EncodedPath.decodeResourcePath(row.getString(4)));
int largestBatchId = row.getInt(5);
indexStates.put(indexId, FieldIndex.IndexState.create(sequenceNumber, readTime, documentKey, largestBatchId));
});
// Fetch all indices and combine with user's index state if available.
db.query("SELECT index_id, collection_group, index_proto FROM index_configuration").forEach(row -> {
try {
int indexId = row.getInt(0);
String collectionGroup = row.getString(1);
List<FieldIndex.Segment> segments = serializer.decodeFieldIndexSegments(Index.parseFrom(row.getBlob(2)));
// If we fetched an index state for the user above, combine it with this index.
// We use the default state if we don't have an index state (e.g. the index was
// created while a different user as logged in).
FieldIndex.IndexState indexState = indexStates.containsKey(indexId) ? indexStates.get(indexId) : FieldIndex.INITIAL_STATE;
FieldIndex fieldIndex = FieldIndex.create(indexId, collectionGroup, segments, indexState);
// Store the index and update `memoizedMaxIndexId` and `memoizedMaxSequenceNumber`.
memoizeIndex(fieldIndex);
} catch (InvalidProtocolBufferException e) {
throw fail("Failed to decode index: " + e);
}
});
started = true;
}
use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class StreamTest method testWriteStreamStopAfterHandshake.
@Test
public void testWriteStreamStopAfterHandshake() throws Exception {
AsyncQueue testQueue = new AsyncQueue();
Datastore datastore = new Datastore(IntegrationTestUtil.testEnvDatabaseInfo(), testQueue, new EmptyCredentialsProvider(), new EmptyAppCheckTokenProvider(), ApplicationProvider.getApplicationContext(), null);
final WriteStream[] writeStreamWrapper = new WriteStream[1];
StreamStatusCallback streamCallback = new StreamStatusCallback() {
@Override
public void onHandshakeComplete() {
assertThat(writeStreamWrapper[0].getLastStreamToken()).isNotEmpty();
super.onHandshakeComplete();
}
@Override
public void onWriteResponse(SnapshotVersion commitVersion, List<MutationResult> mutationResults) {
assertThat(mutationResults).hasSize(1);
assertThat(writeStreamWrapper[0].getLastStreamToken()).isNotEmpty();
super.onWriteResponse(commitVersion, mutationResults);
}
};
WriteStream writeStream = writeStreamWrapper[0] = datastore.createWriteStream(streamCallback);
testQueue.enqueueAndForget(writeStream::start);
waitFor(streamCallback.openSemaphore);
// Writing before the handshake should throw
testQueue.enqueueAndForget(() -> assertThrows(Throwable.class, () -> writeStream.writeMutations(mutations)));
// Handshake should always be called
testQueue.enqueueAndForget(writeStream::writeHandshake);
waitFor(streamCallback.handshakeSemaphore);
// Now writes should succeed
testQueue.enqueueAndForget(() -> writeStream.writeMutations(mutations));
waitFor(streamCallback.responseReceivedSemaphore);
testQueue.runSync(writeStream::stop);
}
use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class TestUtil method addedRemoteEvent.
public static RemoteEvent addedRemoteEvent(List<MutableDocument> docs, List<Integer> updatedInTargets, List<Integer> removedFromTargets) {
Preconditions.checkArgument(!docs.isEmpty(), "Cannot pass empty docs array");
WatchChangeAggregator aggregator = new WatchChangeAggregator(new WatchChangeAggregator.TargetMetadataProvider() {
@Override
public ImmutableSortedSet<DocumentKey> getRemoteKeysForTarget(int targetId) {
return DocumentKey.emptyKeySet();
}
@Override
public TargetData getTargetDataForTarget(int targetId) {
ResourcePath collectionPath = docs.get(0).getKey().getCollectionPath();
return targetData(targetId, QueryPurpose.LISTEN, collectionPath.toString());
}
});
SnapshotVersion version = SnapshotVersion.NONE;
for (MutableDocument doc : docs) {
DocumentChange change = new DocumentChange(updatedInTargets, removedFromTargets, doc.getKey(), doc);
aggregator.handleDocumentChange(change);
version = doc.getVersion().compareTo(version) > 0 ? doc.getVersion() : version;
}
return aggregator.createRemoteEvent(version);
}
use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class TestUtil method version.
public static SnapshotVersion version(long versionMicros) {
long seconds = versionMicros / 1000000;
int nanos = (int) (versionMicros % 1000000L) * 1000;
return new SnapshotVersion(new Timestamp(seconds, nanos));
}
Aggregations