mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 14:03:30 +00:00
streams: Add LazySet for subscribers.
This defers O(N*S) operations, where
N = number of streams
S = number of subscribers per stream
In many cases we never do an O(N) operation on
a stream. Exceptions include:
- checking stream links from the compose box
- editing a stream
- adding members to a newly added stream
An operation that used to be O(N)--computing
the number of subscribers--is now O(1), and we
don't even pay O(N) on a one-time basis to
compute it (not counting the cost to build the
array from JSON, but we have to do that).
This commit is contained in:
@@ -9,6 +9,8 @@ const noop = function () {};
|
||||
set_global('$', global.make_zjquery());
|
||||
set_global('i18n', global.stub_i18n);
|
||||
|
||||
const LazySet = zrequire('lazy_set.js').LazySet;
|
||||
|
||||
const _navigator = {
|
||||
platform: '',
|
||||
};
|
||||
@@ -1328,13 +1330,13 @@ run_test('on_events', () => {
|
||||
(function test_stream_name_completed_triggered() {
|
||||
const handler = $(document).get_on_handler('streamname_completed.zulip');
|
||||
stream_data.add_sub(compose_state.stream_name(), {
|
||||
subscribers: Dict.from_array([1, 2]),
|
||||
subscribers: LazySet([1, 2]),
|
||||
});
|
||||
|
||||
let data = {
|
||||
stream: {
|
||||
name: 'Denmark',
|
||||
subscribers: Dict.from_array([1, 2, 3]),
|
||||
subscribers: LazySet([1, 2, 3]),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1379,7 +1381,7 @@ run_test('on_events', () => {
|
||||
stream: {
|
||||
invite_only: true,
|
||||
name: 'Denmark',
|
||||
subscribers: Dict.from_array([1]),
|
||||
subscribers: LazySet([1]),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ zrequire('marked', 'third/marked/lib/marked');
|
||||
const actual_pygments_data = zrequire('actual_pygments_data', 'generated/pygments_data');
|
||||
zrequire('settings_org');
|
||||
const th = zrequire('typeahead_helper');
|
||||
const LazySet = zrequire('lazy_set.js').LazySet;
|
||||
|
||||
stream_data.create_streams([
|
||||
{name: 'Dev', subscribed: true, color: 'blue', stream_id: 1},
|
||||
@@ -24,13 +25,9 @@ stream_data.create_streams([
|
||||
]);
|
||||
|
||||
run_test('sort_streams', () => {
|
||||
const popular = {num_items: function () {
|
||||
return 10;
|
||||
}};
|
||||
const popular = LazySet([1, 2, 3, 4, 5, 6]);
|
||||
|
||||
const unpopular = {num_items: function () {
|
||||
return 2;
|
||||
}};
|
||||
const unpopular = LazySet([1]);
|
||||
|
||||
let test_streams = [
|
||||
{name: 'Dev', pin_to_top: false, subscribers: unpopular, subscribed: true},
|
||||
|
||||
@@ -31,6 +31,7 @@ import "../search_util.js";
|
||||
import "../keydown_util.js";
|
||||
import "../lightbox_canvas.js";
|
||||
import "../rtl.js";
|
||||
import "../lazy_set.js";
|
||||
import "../dict.ts";
|
||||
import "../scroll_util.js";
|
||||
import "../components.js";
|
||||
|
||||
70
static/js/lazy_set.js
Normal file
70
static/js/lazy_set.js
Normal file
@@ -0,0 +1,70 @@
|
||||
exports.LazySet = function (vals) {
|
||||
/*
|
||||
This class is optimized for a very
|
||||
particular use case.
|
||||
|
||||
We often have lots of subscribers on
|
||||
a stream. We get an array from the
|
||||
backend, because it's JSON.
|
||||
|
||||
Often the only operation we need
|
||||
on subscribers is to get the length,
|
||||
which is plenty cheap as an array.
|
||||
|
||||
Making an array from a set is cheap
|
||||
for one stream, but it's expensive
|
||||
for all N streams at page load.
|
||||
|
||||
Once somebody does an operation
|
||||
where sets are useful, such
|
||||
as has/add/del, we convert it over
|
||||
to a set for a one-time cost.
|
||||
*/
|
||||
const self = {};
|
||||
self.arr = vals;
|
||||
self.set = undefined;
|
||||
|
||||
self.keys = function () {
|
||||
if (self.set !== undefined) {
|
||||
return Array.from(self.set);
|
||||
}
|
||||
return self.arr;
|
||||
};
|
||||
|
||||
function make_set() {
|
||||
if (self.set !== undefined) {
|
||||
return;
|
||||
}
|
||||
self.set = new Set(self.arr);
|
||||
self.arr = undefined;
|
||||
}
|
||||
|
||||
self.num_items = function () {
|
||||
if (self.set !== undefined) {
|
||||
return self.set.size;
|
||||
}
|
||||
|
||||
return self.arr.length;
|
||||
};
|
||||
|
||||
self.map = function (f) {
|
||||
return _.map(self.keys(), f);
|
||||
};
|
||||
|
||||
self.has = function (v) {
|
||||
make_set();
|
||||
return self.set.has(v);
|
||||
};
|
||||
|
||||
self.add = function (v) {
|
||||
make_set();
|
||||
self.set.add(v);
|
||||
};
|
||||
|
||||
self.del = function (v) {
|
||||
make_set();
|
||||
self.set.delete(v);
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
const Dict = require('./dict').Dict;
|
||||
const LazySet = require('./lazy_set').LazySet;
|
||||
|
||||
|
||||
// The stream_info variable maps stream names to stream properties objects
|
||||
@@ -74,7 +75,7 @@ exports.unsubscribe_myself = function (sub) {
|
||||
|
||||
exports.add_sub = function (stream_name, sub) {
|
||||
if (!_.has(sub, 'subscribers')) {
|
||||
sub.subscribers = Dict.from_array([]);
|
||||
sub.subscribers = LazySet([]);
|
||||
}
|
||||
|
||||
stream_info.set(stream_name, sub);
|
||||
@@ -507,7 +508,7 @@ exports.maybe_get_stream_name = function (stream_id) {
|
||||
};
|
||||
|
||||
exports.set_subscribers = function (sub, user_ids) {
|
||||
sub.subscribers = Dict.from_array(user_ids || []);
|
||||
sub.subscribers = LazySet(user_ids || []);
|
||||
};
|
||||
|
||||
exports.add_subscriber = function (stream_name, user_id) {
|
||||
@@ -521,7 +522,7 @@ exports.add_subscriber = function (stream_name, user_id) {
|
||||
blueslip.error("We tried to add invalid subscriber: " + user_id);
|
||||
return false;
|
||||
}
|
||||
sub.subscribers.set(user_id, true);
|
||||
sub.subscribers.add(user_id);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -36,12 +36,9 @@ exports.is_sub_settings_active = function (sub) {
|
||||
};
|
||||
|
||||
exports.get_email_of_subscribers = function (subscribers) {
|
||||
const emails = [];
|
||||
subscribers.each(function (o, i) {
|
||||
const email = people.get_person_from_user_id(i).email;
|
||||
emails.push(email);
|
||||
return subscribers.map(function (user_id) {
|
||||
return people.get_person_from_user_id(user_id).email;
|
||||
});
|
||||
return emails;
|
||||
};
|
||||
|
||||
function clear_edit_panel() {
|
||||
|
||||
Reference in New Issue
Block a user