Merge pull request #253 from vector-im/bwindels/fix-160

Prevent endless loop when hitting server bug at top of timeline
This commit is contained in:
Bruno Windels 2021-03-02 18:38:05 +00:00 committed by GitHub
commit 5c02735ff7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 12 deletions

View file

@ -55,8 +55,8 @@ export class TimelineViewModel extends ViewModel {
if (firstTile.shape === "gap") {
return await firstTile.fill();
} else {
await this._timeline.loadAtTop(10);
return false;
const topReached = await this._timeline.loadAtTop(10);
return topReached;
}
}

View file

@ -441,7 +441,7 @@ export class Room extends EventEmitter {
storage: this._storage,
fragmentIdComparer: this._fragmentIdComparer,
});
gapResult = await gapWriter.writeFragmentFill(fragmentEntry, response, txn);
gapResult = await gapWriter.writeFragmentFill(fragmentEntry, response, txn, log);
} catch (err) {
txn.abort();
throw err;

View file

@ -74,13 +74,19 @@ export class Timeline {
}
// tries to prepend `amount` entries to the `entries` list.
/**
* [loadAtTop description]
* @param {[type]} amount [description]
* @return {boolean} true if the top of the timeline has been reached
*
*/
async loadAtTop(amount) {
if (this._disposables.isDisposed) {
return;
return true;
}
const firstEventEntry = this._remoteEntries.array.find(e => !!e.eventType);
if (!firstEventEntry) {
return;
return true;
}
const readerRequest = this._disposables.track(this._timelineReader.readFrom(
firstEventEntry.asEventKey(),
@ -90,6 +96,7 @@ export class Timeline {
try {
const entries = await readerRequest.complete();
this._remoteEntries.setManySorted(entries);
return entries.length < amount;
} finally {
this._disposables.disposeTracked(readerRequest);
}

View file

@ -26,7 +26,7 @@ export class GapWriter {
this._fragmentIdComparer = fragmentIdComparer;
}
// events is in reverse-chronological order (last event comes at index 0) if backwards
async _findOverlappingEvents(fragmentEntry, events, txn) {
async _findOverlappingEvents(fragmentEntry, events, txn, log) {
let expectedOverlappingEventId;
if (fragmentEntry.hasLinkedFragment) {
expectedOverlappingEventId = await this._findExpectedOverlappingEventId(fragmentEntry, txn);
@ -49,8 +49,12 @@ export class GapWriter {
// TODO: check here that the neighbourEvent is at the correct edge of it's fragment
// get neighbour fragment to link it up later on
const neighbourEvent = await txn.timelineEvents.getByEventId(this._roomId, duplicateEventId);
if (neighbourEvent.fragmentId === fragmentEntry.fragmentId) {
log.log("hit #160, prevent fragment linking to itself", log.level.Warn);
} else {
const neighbourFragment = await txn.timelineFragments.get(this._roomId, neighbourEvent.fragmentId);
neighbourFragmentEntry = fragmentEntry.createNeighbourEntry(neighbourFragment);
}
// trim overlapping events
remainingEvents = null;
} else {
@ -192,10 +196,11 @@ export class GapWriter {
return changedFragments;
}
async writeFragmentFill(fragmentEntry, response, txn) {
async writeFragmentFill(fragmentEntry, response, txn, log) {
const {fragmentId, direction} = fragmentEntry;
// chunk is in reverse-chronological order when backwards
const {chunk, start, end, state} = response;
const {chunk, start, state} = response;
let {end} = response;
let entries;
if (!Array.isArray(chunk)) {
@ -229,8 +234,11 @@ export class GapWriter {
const {
nonOverlappingEvents,
neighbourFragmentEntry
} = await this._findOverlappingEvents(fragmentEntry, chunk, txn);
} = await this._findOverlappingEvents(fragmentEntry, chunk, txn, log);
if (!neighbourFragmentEntry && nonOverlappingEvents.length === 0 && typeof end === "string") {
log.log("hit #160, clearing token", log.level.Warn);
end = null;
}
// create entries for all events in chunk, add them to entries
entries = this._storeEvents(nonOverlappingEvents, lastKey, direction, state, txn);
const fragments = await this._updateFragments(fragmentEntry, neighbourFragmentEntry, end, entries, txn);