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:
commit
5c02735ff7
4 changed files with 27 additions and 12 deletions
|
@ -55,8 +55,8 @@ export class TimelineViewModel extends ViewModel {
|
||||||
if (firstTile.shape === "gap") {
|
if (firstTile.shape === "gap") {
|
||||||
return await firstTile.fill();
|
return await firstTile.fill();
|
||||||
} else {
|
} else {
|
||||||
await this._timeline.loadAtTop(10);
|
const topReached = await this._timeline.loadAtTop(10);
|
||||||
return false;
|
return topReached;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -441,7 +441,7 @@ export class Room extends EventEmitter {
|
||||||
storage: this._storage,
|
storage: this._storage,
|
||||||
fragmentIdComparer: this._fragmentIdComparer,
|
fragmentIdComparer: this._fragmentIdComparer,
|
||||||
});
|
});
|
||||||
gapResult = await gapWriter.writeFragmentFill(fragmentEntry, response, txn);
|
gapResult = await gapWriter.writeFragmentFill(fragmentEntry, response, txn, log);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
txn.abort();
|
txn.abort();
|
||||||
throw err;
|
throw err;
|
||||||
|
|
|
@ -74,13 +74,19 @@ export class Timeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
// tries to prepend `amount` entries to the `entries` list.
|
// 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) {
|
async loadAtTop(amount) {
|
||||||
if (this._disposables.isDisposed) {
|
if (this._disposables.isDisposed) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
const firstEventEntry = this._remoteEntries.array.find(e => !!e.eventType);
|
const firstEventEntry = this._remoteEntries.array.find(e => !!e.eventType);
|
||||||
if (!firstEventEntry) {
|
if (!firstEventEntry) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
const readerRequest = this._disposables.track(this._timelineReader.readFrom(
|
const readerRequest = this._disposables.track(this._timelineReader.readFrom(
|
||||||
firstEventEntry.asEventKey(),
|
firstEventEntry.asEventKey(),
|
||||||
|
@ -90,6 +96,7 @@ export class Timeline {
|
||||||
try {
|
try {
|
||||||
const entries = await readerRequest.complete();
|
const entries = await readerRequest.complete();
|
||||||
this._remoteEntries.setManySorted(entries);
|
this._remoteEntries.setManySorted(entries);
|
||||||
|
return entries.length < amount;
|
||||||
} finally {
|
} finally {
|
||||||
this._disposables.disposeTracked(readerRequest);
|
this._disposables.disposeTracked(readerRequest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class GapWriter {
|
||||||
this._fragmentIdComparer = fragmentIdComparer;
|
this._fragmentIdComparer = fragmentIdComparer;
|
||||||
}
|
}
|
||||||
// events is in reverse-chronological order (last event comes at index 0) if backwards
|
// 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;
|
let expectedOverlappingEventId;
|
||||||
if (fragmentEntry.hasLinkedFragment) {
|
if (fragmentEntry.hasLinkedFragment) {
|
||||||
expectedOverlappingEventId = await this._findExpectedOverlappingEventId(fragmentEntry, txn);
|
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
|
// TODO: check here that the neighbourEvent is at the correct edge of it's fragment
|
||||||
// get neighbour fragment to link it up later on
|
// get neighbour fragment to link it up later on
|
||||||
const neighbourEvent = await txn.timelineEvents.getByEventId(this._roomId, duplicateEventId);
|
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);
|
const neighbourFragment = await txn.timelineFragments.get(this._roomId, neighbourEvent.fragmentId);
|
||||||
neighbourFragmentEntry = fragmentEntry.createNeighbourEntry(neighbourFragment);
|
neighbourFragmentEntry = fragmentEntry.createNeighbourEntry(neighbourFragment);
|
||||||
|
}
|
||||||
// trim overlapping events
|
// trim overlapping events
|
||||||
remainingEvents = null;
|
remainingEvents = null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -192,10 +196,11 @@ export class GapWriter {
|
||||||
return changedFragments;
|
return changedFragments;
|
||||||
}
|
}
|
||||||
|
|
||||||
async writeFragmentFill(fragmentEntry, response, txn) {
|
async writeFragmentFill(fragmentEntry, response, txn, log) {
|
||||||
const {fragmentId, direction} = fragmentEntry;
|
const {fragmentId, direction} = fragmentEntry;
|
||||||
// chunk is in reverse-chronological order when backwards
|
// chunk is in reverse-chronological order when backwards
|
||||||
const {chunk, start, end, state} = response;
|
const {chunk, start, state} = response;
|
||||||
|
let {end} = response;
|
||||||
let entries;
|
let entries;
|
||||||
|
|
||||||
if (!Array.isArray(chunk)) {
|
if (!Array.isArray(chunk)) {
|
||||||
|
@ -229,8 +234,11 @@ export class GapWriter {
|
||||||
const {
|
const {
|
||||||
nonOverlappingEvents,
|
nonOverlappingEvents,
|
||||||
neighbourFragmentEntry
|
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
|
// create entries for all events in chunk, add them to entries
|
||||||
entries = this._storeEvents(nonOverlappingEvents, lastKey, direction, state, txn);
|
entries = this._storeEvents(nonOverlappingEvents, lastKey, direction, state, txn);
|
||||||
const fragments = await this._updateFragments(fragmentEntry, neighbourFragmentEntry, end, entries, txn);
|
const fragments = await this._updateFragments(fragmentEntry, neighbourFragmentEntry, end, entries, txn);
|
||||||
|
|
Reference in a new issue