You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-09-01 21:21:58 +03:00
Restructure timeline batches to have high numbers = newest; it's less confusing that way.
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
* --------------
|
||||
* Retrieving a room requires the $ROOMID which then pulls out the current state
|
||||
* from room_$ROOMID_state. A defined starting batch of timeline events are then
|
||||
* extracted from the lowest numbered $INDEX for room_$ROOMID_timeline_$INDEX
|
||||
* extracted from the highest numbered $INDEX for room_$ROOMID_timeline_$INDEX
|
||||
* (more indices as required). The $INDEX may be negative. These are
|
||||
* added to the timeline in the same way as /initialSync (old state will diverge).
|
||||
* If there exists a room_$ROOMID_timeline_live key, then a timeline sync should
|
||||
@@ -30,9 +30,9 @@
|
||||
* The earliest event the Room instance knows about is E. Retrieving earlier
|
||||
* messages requires a Room which has a storageToken defined.
|
||||
* This token maps to the index I where the Room is at. Events are then retrieved from
|
||||
* room_$ROOMID_timeline_{I} and events after E are extracted. If the limit
|
||||
* demands more events, I+1 is retrieved, up until I=max $INDEX where it gives
|
||||
* less than the limit.
|
||||
* room_$ROOMID_timeline_{I} and elements before E are extracted. If the limit
|
||||
* demands more events, I-1 is retrieved, up until I=min $INDEX where it gives
|
||||
* less than the limit. Index may go negative if you have paginated in the past.
|
||||
*
|
||||
* Full Insertion
|
||||
* --------------
|
||||
@@ -45,8 +45,7 @@
|
||||
* Incremental Insertion
|
||||
* ---------------------
|
||||
* As events arrive, the store can quickly persist these new events. This
|
||||
* involves pushing the events to room_$ROOMID_timeline_live. This results in an
|
||||
* inverted ordering where the highest number is the most recent entry. If the
|
||||
* involves pushing the events to room_$ROOMID_timeline_live. If the
|
||||
* current room state has been modified by the new event, then
|
||||
* room_$ROOMID_state should be updated in addition to the timeline.
|
||||
*
|
||||
@@ -56,24 +55,23 @@
|
||||
* events. This is computationally expensive to perform on every new event, so
|
||||
* is deferred by inserting live events to room_$ROOMID_timeline_live. A
|
||||
* timeline sync reconciles timeline_live and timeline_$INDEX. This involves
|
||||
* retrieving _live and the lowest numbered $INDEX batch. If the batch is < B,
|
||||
* the earliest entries are inserted into the $INDEX (the earliest entries are
|
||||
* inverted in _live, so the earliest entry is at index 0, not len-1) until the
|
||||
* batch == B. Then, the remaining entries in _live are batched to $INDEX-1,
|
||||
* $INDEX-2, and so on. This will result in negative indices. The easiest way to
|
||||
* visualise this is that the timeline goes from new to old, left to right:
|
||||
* retrieving _live and the highest numbered $INDEX batch. If the batch is < B,
|
||||
* the earliest entries from _live are inserted into the $INDEX until the
|
||||
* batch == B. Then, the remaining entries in _live are batched to $INDEX+1,
|
||||
* $INDEX+2, and so on. The easiest way to visualise this is that the timeline
|
||||
* goes from old to new, left to right:
|
||||
* -2 -1 0 1
|
||||
* <--NEW---------------------------------------OLD-->
|
||||
* <--OLD---------------------------------------NEW-->
|
||||
* [a,b,c] [d,e,f] [g,h,i] [j,k,l]
|
||||
*
|
||||
* Purging
|
||||
* -------
|
||||
* Events from the timeline can be purged by removing the highest
|
||||
* Events from the timeline can be purged by removing the lowest
|
||||
* timeline_$INDEX in the store.
|
||||
*
|
||||
* Example
|
||||
* -------
|
||||
* A room with room_id !foo:bar has 9 messages (M1->9 where 1=newest) with a
|
||||
* A room with room_id !foo:bar has 9 messages (M1->9 where 9=newest) with a
|
||||
* batch size of 4. The very first time, there is no entry for !foo:bar until
|
||||
* storeRoom() is called, which results in the keys: [Full Insert]
|
||||
* room_!foo:bar_timeline_0 : [M1, M2, M3, M4]
|
||||
@@ -81,31 +79,33 @@
|
||||
* room_!foo:bar_timeline_2 : [M9]
|
||||
* room_!foo:bar_state: { ... }
|
||||
*
|
||||
* 5 new messages (N1-5, 1=newest) arrive and are then added: [Incremental Insert]
|
||||
* room_!foo:bar_timeline_live: [N5]
|
||||
* room_!foo:bar_timeline_live: [N5, N4]
|
||||
* room_!foo:bar_timeline_live: [N5, N4, N3]
|
||||
* room_!foo:bar_timeline_live: [N5, N4, N3, N2]
|
||||
* room_!foo:bar_timeline_live: [N5, N4, N3, N2, N1]
|
||||
* 5 new messages (N1-5, 5=newest) arrive and are then added: [Incremental Insert]
|
||||
* room_!foo:bar_timeline_live: [N1]
|
||||
* room_!foo:bar_timeline_live: [N1, N2]
|
||||
* room_!foo:bar_timeline_live: [N1, N2, N3]
|
||||
* room_!foo:bar_timeline_live: [N1, N2, N3, N4]
|
||||
* room_!foo:bar_timeline_live: [N1, N2, N3, N4, N5]
|
||||
*
|
||||
* App is shutdown. Restarts. The timeline is synced [Timeline Sync]
|
||||
* room_!foo:bar_timeline_-1 : [N2, N3, N4, N5]
|
||||
* room_!foo:bar_timeline_-2 : [N1]
|
||||
* room_!foo:bar_timeline_2 : [M9, N1, N2, N3]
|
||||
* room_!foo:bar_timeline_3 : [N4, N5]
|
||||
* room_!foo:bar_timeline_live: []
|
||||
*
|
||||
* And the room is retrieved with 8 messages: [Room Retrieval]
|
||||
* Room.timeline: [N1, N2, N3, N4, N5, M1, M2, M3]
|
||||
* Room.storageToken: => early_index 0
|
||||
* Room.timeline: [M7, M8, M9, N1, N2, N3, N4, N5]
|
||||
* Room.storageToken: => early_index = 1 because that's where M7 is.
|
||||
*
|
||||
* 3 earlier messages are requested: [Earlier retrieval]
|
||||
* Use storageToken to find batch index 0. Scan batch for earliest event ID.
|
||||
* earliest event = M3
|
||||
* events = room_!foo:bar_timeline[0] where event > M3 = [M4]
|
||||
* Too few events, use next index and get 2 more:
|
||||
* events = room_!foo:bar_timeline[1] = [M5, M6, M7, M8] => [M5, M6]
|
||||
* Use storageToken to find batch index 1. Scan batch for earliest event ID.
|
||||
* earliest event = M7
|
||||
* events = room_!foo:bar_timeline_1 where event < M7 = [M5, M6]
|
||||
* Too few events, use next index (0) and get 1 more:
|
||||
* events = room_!foo:bar_timeline_0 = [M1, M2, M3, M4] => [M4]
|
||||
* Return concatentation:
|
||||
* [M4, M5, M6]
|
||||
*
|
||||
* Purge oldest events: [Purge]
|
||||
* del room_!foo:bar_timeline_2
|
||||
* del room_!foo:bar_timeline_0
|
||||
* </pre>
|
||||
* @module store/webstorage
|
||||
*/
|
||||
@@ -178,7 +178,6 @@ WebStorageStore.prototype.getRoom = function(roomId) {
|
||||
}
|
||||
var timelineKeys = getTimelineIndices(this.store, roomId);
|
||||
if (timelineKeys.indexOf("live") !== -1) {
|
||||
console.log("Syncing live");
|
||||
this._syncTimeline(roomId, timelineKeys);
|
||||
}
|
||||
return loadRoom(this.store, roomId, this.batchSize);
|
||||
@@ -237,27 +236,25 @@ WebStorageStore.prototype._syncTimeline = function(roomId, timelineIndices) {
|
||||
timelineIndices = timelineIndices || getTimelineIndices(this.store, roomId);
|
||||
var liveEvents = this.store.getItem(keyName(roomId, "timeline", "live")) || [];
|
||||
|
||||
// get the lowest numbered $INDEX batch
|
||||
var lowestIndex = getLowestIndex(timelineIndices);
|
||||
var lowKey = keyName(roomId, "timeline", lowestIndex);
|
||||
var lowestBatch = this.store.getItem(lowKey) || [];
|
||||
console.log("Live Events = %s, Low batch = %s (i=%s;b=%s)", liveEvents.length,
|
||||
lowestBatch.length, lowestIndex, this.batchSize);
|
||||
// get the highest numbered $INDEX batch
|
||||
var highestIndex = getHighestIndex(timelineIndices);
|
||||
var hiKey = keyName(roomId, "timeline", highestIndex);
|
||||
var hiBatch = this.store.getItem(hiKey) || [];
|
||||
// fill up the existing batch first.
|
||||
while (lowestBatch.length < this.batchSize && liveEvents.length > 0) {
|
||||
lowestBatch.unshift(liveEvents.shift());
|
||||
while (hiBatch.length < this.batchSize && liveEvents.length > 0) {
|
||||
hiBatch.push(liveEvents.shift());
|
||||
}
|
||||
this.store.setItem(lowKey, lowestBatch);
|
||||
this.store.setItem(hiKey, hiBatch);
|
||||
|
||||
// start adding new batches as required
|
||||
var batch = [];
|
||||
while (liveEvents.length > 0) {
|
||||
batch.unshift(liveEvents.shift());
|
||||
batch.push(liveEvents.shift());
|
||||
if (batch.length === this.batchSize || liveEvents.length === 0) {
|
||||
// persist the full batch and make another
|
||||
lowestIndex--;
|
||||
lowKey = keyName(roomId, "timeline", lowestIndex);
|
||||
this.store.setItem(lowKey, batch);
|
||||
highestIndex++;
|
||||
hiKey = keyName(roomId, "timeline", highestIndex);
|
||||
this.store.setItem(hiKey, batch);
|
||||
batch = [];
|
||||
}
|
||||
}
|
||||
@@ -284,7 +281,7 @@ function SerialisedRoom(roomId) {
|
||||
*/
|
||||
SerialisedRoom.fromRoom = function(room, batchSize) {
|
||||
var self = new SerialisedRoom(room.roomId);
|
||||
var ptr;
|
||||
var index;
|
||||
self.state.pagination_token = room.oldState.paginationToken;
|
||||
// [room_$ROOMID_state] downcast to POJO from MatrixEvent
|
||||
utils.forEach(utils.keys(room.currentState.events), function(eventType) {
|
||||
@@ -300,16 +297,16 @@ SerialisedRoom.fromRoom = function(room, batchSize) {
|
||||
|
||||
// [room_$ROOMID_timeline_$INDEX]
|
||||
if (batchSize > 0) {
|
||||
ptr = 0;
|
||||
while (ptr * batchSize < room.timeline.length) {
|
||||
self.timeline[ptr] = room.timeline.slice(
|
||||
ptr * batchSize, (ptr + 1) * batchSize
|
||||
index = 0;
|
||||
while (index * batchSize < room.timeline.length) {
|
||||
self.timeline[index] = room.timeline.slice(
|
||||
index * batchSize, (index + 1) * batchSize
|
||||
);
|
||||
self.timeline[ptr] = utils.map(self.timeline[ptr], function(me) {
|
||||
self.timeline[index] = utils.map(self.timeline[index], function(me) {
|
||||
// use POJO not MatrixEvent
|
||||
return me.event;
|
||||
});
|
||||
ptr++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else { // don't batch
|
||||
@@ -345,7 +342,7 @@ function loadRoom(store, roomId, numEvents) {
|
||||
|
||||
// add most recent numEvents
|
||||
var recentEvents = [];
|
||||
var index = getLowestIndex(getTimelineIndices(store, roomId));
|
||||
var index = getHighestIndex(getTimelineIndices(store, roomId));
|
||||
var i, key, batch;
|
||||
while (recentEvents.length < numEvents) {
|
||||
key = keyName(roomId, "timeline", index);
|
||||
@@ -354,14 +351,15 @@ function loadRoom(store, roomId, numEvents) {
|
||||
// nothing left in the store.
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < batch.length; i++) {
|
||||
for (i = batch.length - 1; i >= 0; i--) {
|
||||
recentEvents.unshift(new MatrixEvent(batch[i]));
|
||||
if (recentEvents.length === numEvents) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
index--;
|
||||
}
|
||||
// add events backwards to diverge old state correctly.
|
||||
room.addEventsToTimeline(recentEvents.reverse(), true);
|
||||
room.oldState.paginationToken = currentStateMap.pagination_token;
|
||||
return room;
|
||||
@@ -390,16 +388,15 @@ function getTimelineIndices(store, roomId) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
function getLowestIndex(timelineIndices) {
|
||||
var lowestIndex = 0;
|
||||
var index;
|
||||
function getHighestIndex(timelineIndices) {
|
||||
var highestIndex, index;
|
||||
for (var i = 0; i < timelineIndices.length; i++) {
|
||||
index = parseInt(timelineIndices[i]);
|
||||
if (index && index < lowestIndex) {
|
||||
lowestIndex = index;
|
||||
if (!isNaN(index) && (highestIndex === undefined || index > highestIndex)) {
|
||||
highestIndex = index;
|
||||
}
|
||||
}
|
||||
return lowestIndex;
|
||||
return highestIndex;
|
||||
}
|
||||
|
||||
function keyName(roomId, key, index) {
|
||||
|
Reference in New Issue
Block a user