stream_topic_history: Track the most recent message in streams.

This adds a way to keep track of max_message_id of a
stream and fetch it using the method get_max_message_id().

This will be useful for sorting streams by most recent
activity which will be implemented in the upcoming commit.

Essentially rewritten by tabbott to have a coherent tracking system,
and provide documentation.

Part of #10794.
This commit is contained in:
Hashir Sarwar
2020-04-30 15:11:18 +05:00
committed by Tim Abbott
parent 5598f8f6b0
commit 735785f985
2 changed files with 55 additions and 6 deletions

View File

@@ -16,7 +16,9 @@ run_test('basics', () => {
});
let history = stream_topic_history.get_recent_topic_names(stream_id);
let max_message_id = stream_topic_history.get_max_message_id(stream_id);
assert.deepEqual(history, ['toPic1']);
assert.deepEqual(max_message_id, 101);
stream_topic_history.add_message({
stream_id: stream_id,
@@ -24,7 +26,9 @@ run_test('basics', () => {
topic_name: 'Topic1',
});
history = stream_topic_history.get_recent_topic_names(stream_id);
max_message_id = stream_topic_history.get_max_message_id(stream_id);
assert.deepEqual(history, ['Topic1']);
assert.deepEqual(max_message_id, 102);
stream_topic_history.add_message({
stream_id: stream_id,
@@ -32,7 +36,9 @@ run_test('basics', () => {
topic_name: 'topic2',
});
history = stream_topic_history.get_recent_topic_names(stream_id);
max_message_id = stream_topic_history.get_max_message_id(stream_id);
assert.deepEqual(history, ['topic2', 'Topic1']);
assert.deepEqual(max_message_id, 103);
// Removing first topic1 message has no effect.
stream_topic_history.remove_message({
@@ -41,6 +47,9 @@ run_test('basics', () => {
});
history = stream_topic_history.get_recent_topic_names(stream_id);
assert.deepEqual(history, ['topic2', 'Topic1']);
// Removing a topic message shouldn't effect the max_message_id.
max_message_id = stream_topic_history.get_max_message_id(stream_id);
assert.deepEqual(max_message_id, 103);
// Removing second topic1 message removes the topic.
stream_topic_history.remove_message({

View File

@@ -44,26 +44,55 @@ exports.stream_has_topics = function (stream_id) {
exports.per_stream_history = function (stream_id) {
/*
Each stream has a dictionary of topics.
The main getter of this object is
get_recent_topic_names, and we just
sort on the fly every time we are
called.
For a given stream, this structure has a dictionary of topics.
The main getter of this object is get_recent_topic_names, and
we just sort on the fly every time we are called.
Attributes for a topic are:
* message_id: The latest message_id in the topic. Only usable
for imprecise applications like sorting. The message_id
cannot be fully accurate given message editing and deleting
(as we don't have a way to handle the latest message in a
stream having its stream edited or deleted).
TODO: We can probably fix this limitation by doing a
single-message `GET /messages` query with anchor="latest",
num_before=0, num_after=0, to update this field when its
value becomes ambiguous. Or probably better to avoid a
thundering herd (of a fast query), having the server send
the data needed to do this update in stream/topic-edit and
delete events (just the new max_message_id for the relevant
topic would likely suffice, though we need to think about
private stream corner cases).
* pretty_name: The topic_name, with original case.
* historical: Whether the user actually received any messages in
the topic (has UserMessage rows) or is just viewing the stream.
* count: Number of known messages in the topic. Used to detect
when the last messages in a topic were moved to other topics or
deleted.
*/
const topics = new FoldDict();
// Most recent message ID for the stream.
let max_message_id = 0;
const self = {};
self.has_topics = function () {
return topics.size !== 0;
};
self.update_stream_max_message_id = function (message_id) {
if (message_id > max_message_id) {
max_message_id = message_id;
}
};
self.add_or_update = function (opts) {
const topic_name = opts.topic_name;
let message_id = opts.message_id || 0;
message_id = parseInt(message_id, 10);
self.update_stream_max_message_id(message_id);
const existing = topics.get(topic_name);
@@ -137,6 +166,7 @@ exports.per_stream_history = function (stream_id) {
pretty_name: topic_name,
historical: true,
});
self.update_stream_max_message_id(message_id);
}
};
@@ -159,6 +189,10 @@ exports.per_stream_history = function (stream_id) {
return names;
};
self.get_max_message_id = function () {
return max_message_id;
};
return self;
};
@@ -233,6 +267,12 @@ exports.get_recent_topic_names = function (stream_id) {
return history.get_recent_topic_names();
};
exports.get_max_message_id = function (stream_id) {
const history = exports.find_or_create(stream_id);
return history.get_max_message_id();
};
exports.reset = function () {
// This is only used by tests.
stream_dict.clear();