-
Notifications
You must be signed in to change notification settings - Fork 934
[Multi-Tab] Detect and recover from GCed Remote Document Changelog #1272
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
3a110a5
to
e6b0768
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.
Mostly LGTM.
* have already been manually synchronized. | ||
*/ | ||
// PORTING NOTE: This is only used for multi-tab synchronization. | ||
resetLastProcessedDocumentChange( |
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.
"lastProcessedDocumentChange" is using implementation terminology and should instead use the same terminology as the rest of this interface, which probably means something like "resetDocumenChanges()" (to match "getNewDocumentChanges()"). The comment should also be updated to be more implementation-agnostic (including not mentioning IndexedDb).
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.
On second thought, can we get rid of this method and just have getNewDocumentChanges() maintain its guarantee that it'll only return new changes since the last call, even if it hits the error? That is, have it still "consume" all of the new changes, updating lastProcessedChangeId_, even if there was an error?
That seems sensible and simplifies the interface (and maybe our implementation?).
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 removed the method and inlined the call that resets the change log pointer.
) | ||
); | ||
} | ||
firstIteration = false; |
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.
Per my comment elsewhere, perhaps rework this so that getNewDocumentChanges() always advances past all document changes, even if it ultimately returns an error... e.g. by changing firstIteration to minChangeId and checking it in the next() block...
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.
Consider it done.
} | ||
} | ||
|
||
export function isRemoteDocumentChangesGarbageCollectedError( |
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 like verbosity, but "is...Error" gets hard to parse when there are 5 words in between. How about isDocumentChangeMissingError()?
I was also wondering if you could just:
export const DOCUMENT_CHANGE_MISSING_ERROR = new Error(...);
But I am not sure how JS works. Would that mess up the stack trace in the error or something?
It's also a little dubious to be using errors for control flow. We could change getNewDocumentChanges() to return null if it encountered a missing change or something, but I think I'm not that thrilled about the idea.
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 changed the name to isDocumentChangeMissingError
.
In general, I would like to shy away from statically allocating errors as it does mess up the stacktraces (even in JS).
I'm gonna use the current fire alarm in SFO as an excuse to not comment on the API contract. I can't really think of a much better way to convey this information.
'readonly', | ||
txn => { | ||
return this.cache.getNewDocumentChanges(txn); | ||
} | ||
); | ||
} | ||
|
||
resetLastProcessDocumentChange(): Promise<void> { |
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'm hoping this method goes away, but if not you're missing 'ed' here and below.
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.
And it's gone!
'readwrite-primary', | ||
txn => { | ||
return (this | ||
.cache as IndexedDbRemoteDocumentCache).removeDocumentChangesThroughChangeId( |
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.
Is this cast necessary? If you moved your if check above into this closure it probably wouldn't be...
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, I managed to get rid of it by moving the assert.
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.
LGTM with one remaining question for consideration. Also, can you please try to test this? :-)
// Reset the `lastProcessedDocumentChangeId` to allow further | ||
// invocations to successfully return the changes after this | ||
// rejection. | ||
return this.synchronizeLastDocumentChangeId(changesStore).next(() => |
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.
Just to verify, did you consider just letting this iteration continue so that line 199 can correctly update _lastProcessedDocumentChangeId, and then we could (I think?) get rid of the synchronizeLastDocumentChangeId? I think the reduced code complexity would outweigh any potential performance saving of getting to skip the changedKeys accounting...
I tested this PR manually this morning. I found one area of improvement (we should set firstIteration to false before we return, since the iterate function just keeps iterating). Outside of this PR, I did find a slew of things that were not working/no longer working in Multi-Tab, so thanks for asking for an extra QA run :) |
This is meant to address the case when one tab removed document changes from the document change log that a throttled tab has not yet seen. The detection relies on the fact that change IDs are incremented by 1 each time (as guaranteed by https://www.w3.org/TR/IndexedDB/#key-generator-construct)