-
Notifications
You must be signed in to change notification settings - Fork 938
Port performance optimizations to speed up reading large collections from Android #1433
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
Changes from 6 commits
3da020e
9717fef
204443e
c56820b
f3174ca
36c53d3
942b0fa
7767b60
a1ad6d2
cc29731
307e0b3
62c627b
8f293eb
4e23f3b
fb751dd
431f618
1681f3d
55c7ff3
5afa305
56eefbc
5e506fa
7b11cec
1a32b22
17a69a6
4c270c7
512667e
6cb4bfb
77bf92e
e7b8c8e
a0d25f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,16 +20,16 @@ import { | |
documentKeySet, | ||
DocumentMap, | ||
documentMap, | ||
DocumentSizeEntry, | ||
DocumentSizeEntries, | ||
nullableMaybeDocumentMap, | ||
NullableMaybeDocumentMap, | ||
DocumentSizeEntry, | ||
MaybeDocumentMap, | ||
maybeDocumentMap | ||
maybeDocumentMap, | ||
nullableMaybeDocumentMap, | ||
NullableMaybeDocumentMap | ||
} from '../model/collections'; | ||
import { Document, MaybeDocument, NoDocument } from '../model/document'; | ||
import { SortedMap } from '../util/sorted_map'; | ||
import { DocumentKey } from '../model/document_key'; | ||
import { SortedMap } from '../util/sorted_map'; | ||
|
||
import { SnapshotVersion } from '../core/snapshot_version'; | ||
import { assert, fail } from '../util/assert'; | ||
|
@@ -187,9 +187,21 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache { | |
transaction: PersistenceTransaction, | ||
documentKeys: DocumentKeySet | ||
): PersistencePromise<NullableMaybeDocumentMap> { | ||
return this.getSizedEntries(transaction, documentKeys).next( | ||
result => result.maybeDocuments | ||
); | ||
let results = nullableMaybeDocumentMap(); | ||
return this.forEachDbEntry( | ||
transaction, | ||
documentKeys, | ||
(key, dbRemoteDoc) => { | ||
if (dbRemoteDoc) { | ||
results = results.insert( | ||
key, | ||
this.serializer.fromDbRemoteDocument(dbRemoteDoc) | ||
); | ||
} else { | ||
results = results.insert(key, null); | ||
} | ||
} | ||
).next(() => results); | ||
} | ||
|
||
/** | ||
|
@@ -206,46 +218,72 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache { | |
): PersistencePromise<DocumentSizeEntries> { | ||
let results = nullableMaybeDocumentMap(); | ||
let sizeMap = new SortedMap<DocumentKey, number>(DocumentKey.comparator); | ||
return this.forEachDbEntry( | ||
transaction, | ||
documentKeys, | ||
(key, dbRemoteDoc) => { | ||
if (dbRemoteDoc) { | ||
results = results.insert( | ||
key, | ||
this.serializer.fromDbRemoteDocument(dbRemoteDoc) | ||
); | ||
sizeMap = sizeMap.insert(key, dbDocumentSize(dbRemoteDoc)); | ||
} else { | ||
results = results.insert(key, null); | ||
sizeMap = sizeMap.insert(key, 0); | ||
} | ||
} | ||
).next(() => { | ||
return { maybeDocuments: results, sizeMap }; | ||
}); | ||
} | ||
|
||
forEachDbEntry( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. private please There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
transaction: PersistenceTransaction, | ||
documentKeys: DocumentKeySet, | ||
callback: (key: DocumentKey, doc: DbRemoteDocument | null) => void | ||
): PersistencePromise<void> { | ||
if (documentKeys.isEmpty()) { | ||
return PersistencePromise.resolve({ maybeDocuments: results, sizeMap }); | ||
return PersistencePromise.resolve(); | ||
} | ||
|
||
const range = IDBKeyRange.bound( | ||
documentKeys.first()!.path.toArray(), | ||
documentKeys.last()!.path.toArray() | ||
); | ||
let key = documentKeys.first(); | ||
const keyIter = documentKeys.getIterator(); | ||
let nextKey: DocumentKey | null = keyIter.getNext(); | ||
|
||
return remoteDocumentsStore(transaction) | ||
.iterate({ range }, (potentialKeyRaw, dbRemoteDoc, control) => { | ||
const potentialKey = DocumentKey.fromSegments(potentialKeyRaw); | ||
while (DocumentKey.comparator(key!, potentialKey) != 1) { | ||
if (key!.isEqual(potentialKey)) { | ||
results = results.insert( | ||
key!, | ||
this.serializer.fromDbRemoteDocument(dbRemoteDoc) | ||
); | ||
sizeMap = sizeMap.insert(key!, dbDocumentSize(dbRemoteDoc)); | ||
} else { | ||
results = results.insert(key!, null); | ||
sizeMap = sizeMap.insert(key!, 0); | ||
} | ||
|
||
key = documentKeys.firstAfter(key!); | ||
if (!key) { | ||
control.done(); | ||
return; | ||
} | ||
control.skip(key!.path.toArray()); | ||
// Go through keys not found in cache. | ||
while (nextKey && DocumentKey.comparator(nextKey!, potentialKey) < 0) { | ||
callback(nextKey!, null); | ||
nextKey = keyIter.getNext(); | ||
} | ||
|
||
if (nextKey && nextKey!.isEqual(potentialKey)) { | ||
// Key found in cache. | ||
callback(nextKey!, dbRemoteDoc); | ||
nextKey = keyIter.hasNext() ? keyIter.getNext() : null; | ||
} | ||
|
||
// Skip to the next key (if there is one). | ||
if (nextKey) { | ||
control.skip(nextKey!.path.toArray()); | ||
} else { | ||
control.done(); | ||
} | ||
}) | ||
.next(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a comment. // The rest of the keys must not be in the cache. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done (with minor addition). |
||
while (key) { | ||
results = results.insert(key, null); | ||
sizeMap = sizeMap.insert(key, 0); | ||
key = documentKeys.firstAfter(key!); | ||
// The rest of the keys are not in the cache. One case where `iterate` | ||
// above won't go through them is when the cache is empty. | ||
while (nextKey) { | ||
callback(nextKey!, null); | ||
nextKey = keyIter.hasNext() ? keyIter.getNext() : null; | ||
} | ||
return { maybeDocuments: results, sizeMap }; | ||
}); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,8 +108,7 @@ export abstract class RemoteDocumentChangeBuffer { | |
} | ||
|
||
/** | ||
* Looks up several entries in the cache. | ||
* checked, and if no buffered change applies, this will forward to | ||
* Looks up several entries in the cache, orwarding to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. forwarding :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! |
||
* `RemoteDocumentCache.getEntry()`. | ||
* | ||
* @param transaction The transaction in which to perform any persistence | ||
|
@@ -127,12 +126,14 @@ export abstract class RemoteDocumentChangeBuffer { | |
|
||
// Record the size of everything we load from the cache so we can compute | ||
// a delta later. | ||
return this.getAllFromCache(transaction, documentKeys).next(getResult => { | ||
getResult.sizeMap.forEach((documentKey, size) => { | ||
this.documentSizes.set(documentKey, size); | ||
}); | ||
return getResult.maybeDocuments; | ||
}); | ||
return this.getAllFromCache(transaction, documentKeys).next( | ||
({ maybeDocuments, sizeMap }) => { | ||
sizeMap.forEach((documentKey, size) => { | ||
this.documentSizes.set(documentKey, size); | ||
}); | ||
return maybeDocuments; | ||
} | ||
); | ||
} | ||
|
||
/** | ||
|
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.
Sorry, now that getEntries() is no longer a wrapper around getSizedEntries(), can we drop sizeMap and have this be new SortedMap<DocumentKey, DocumentSizeEntry|null> ?
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.
First, I don't feel strongly about this. The reason I set it up that way is so that
getEntries
inRemoteDocumentChangeBuffer
can return theMaybeDocumentMap
directly. If this were a sorted map ofDocumentSizeEntry
s, thengetEntries
would have to create a newMaybeDocumentMap
and fill it with justMaybeDocument
s.It's probably not a big deal, so if you think code clarity is more important here, I'll do the change.
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.
Oh, sorry! I was confused. I thought it was just so the old getEntries() implementation could return the MaybeDocumentMap directly. But I see now that RemoteDocumentChangeBuffer.getEntries() ends up calling getSizedEntries() and using the sizes and also passing the MaybeDocumentMap straight through. So it needs both, and the way it's structured right now makes sense.
So nevermind. Please keep it the way it is.