atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
benchmarks/benches/room_bench.rsRust208 lines7.4 KB
1
use std::time::Duration;
2
3
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
4
use matrix_sdk::{
5
    cross_process_lock::CrossProcessLockConfig, store::RoomLoadSettings,
6
    test_utils::mocks::MatrixMockServer,
7
};
8
use matrix_sdk_base::{
9
    BaseClient, DmRoomDefinition, RoomInfo, RoomState, SessionMeta, StateChanges, StateStore,
10
    ThreadingSupport, store::StoreConfig,
11
};
12
use matrix_sdk_sqlite::SqliteStateStore;
13
use matrix_sdk_test::{JoinedRoomBuilder, base64_sha256_hash, event_factory::EventFactory};
14
use matrix_sdk_ui::timeline::{TimelineBuilder, TimelineFocus};
15
use ruma::{
16
    EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId,
17
    api::client::membership::get_member_events,
18
    events::room::member::{MembershipState, RoomMemberEvent},
19
    mxc_uri, owned_device_id, owned_room_id, owned_user_id,
20
    serde::Raw,
21
};
22
use tokio::runtime::Builder;
23
use wiremock::{Request, ResponseTemplate};
24
25
pub fn receive_all_members_benchmark(c: &mut Criterion) {
26
    const MEMBERS_IN_ROOM: usize = 100000;
27
28
    let runtime = Builder::new_multi_thread().build().expect("Can't create runtime");
29
    let room_id = owned_room_id!("!homohominilupusest:example.com");
30
31
    let f = EventFactory::new().room(&room_id);
32
    let mut member_events: Vec<Raw<RoomMemberEvent>> = Vec::with_capacity(MEMBERS_IN_ROOM);
33
    for i in 0..MEMBERS_IN_ROOM {
34
        let user_id = OwnedUserId::try_from(format!("@user_{i}:matrix.org")).unwrap();
35
        let event = f
36
            .member(&user_id)
37
            .membership(MembershipState::Join)
38
            .avatar_url(mxc_uri!("mxc://example.org/SEsfnsuifSDFSSEF"))
39
            .display_name("Alice Margatroid")
40
            .reason("Looking for support")
41
            .into_raw();
42
        member_events.push(event);
43
    }
44
45
    // Create a fake list of changes, and a session to recover from.
46
    let mut changes = StateChanges::default();
47
    changes.add_room(RoomInfo::new(&room_id, RoomState::Joined));
48
    for member_event in member_events.iter() {
49
        let event = member_event.clone().cast();
50
        changes.add_state_event(&room_id, event.deserialize().unwrap(), event);
51
    }
52
53
    // Sqlite
54
    let sqlite_dir = tempfile::tempdir().unwrap();
55
    let sqlite_store = runtime.block_on(SqliteStateStore::open(sqlite_dir.path(), None)).unwrap();
56
    runtime
57
        .block_on(sqlite_store.save_changes(&changes))
58
        .expect("initial filling of sqlite failed");
59
60
    let base_client = BaseClient::new(
61
        StoreConfig::new(CrossProcessLockConfig::multi_process(
62
            "cross-process-store-locks-holder-name",
63
        ))
64
        .state_store(sqlite_store),
65
        ThreadingSupport::Disabled,
66
        DmRoomDefinition::default(),
67
    );
68
69
    runtime
70
        .block_on(base_client.activate(
71
            SessionMeta {
72
                user_id: owned_user_id!("@somebody:example.com"),
73
                device_id: owned_device_id!("DEVICE_ID"),
74
            },
75
            RoomLoadSettings::default(),
76
            None,
77
        ))
78
        .expect("Could not set session meta");
79
80
    base_client.get_or_create_room(&room_id, RoomState::Joined);
81
82
    let request = get_member_events::v3::Request::new(room_id.clone());
83
    let response = get_member_events::v3::Response::new(member_events);
84
85
    let count = MEMBERS_IN_ROOM;
86
    let name = format!("{count} members");
87
    let mut group = c.benchmark_group("Test");
88
    group.throughput(Throughput::Elements(count as u64));
89
    group.sample_size(50);
90
91
    group.bench_function(BenchmarkId::new("Handle /members request [SQLite]", name), |b| {
92
        b.to_async(&runtime).iter(|| async {
93
            base_client.receive_all_members(&room_id, &request, &response).await.unwrap();
94
        });
95
    });
96
97
    {
98
        let _guard = runtime.enter();
99
        drop(base_client);
100
    }
101
102
    group.finish();
103
}
104
105
pub fn load_pinned_events_benchmark(c: &mut Criterion) {
106
    const PINNED_EVENTS_COUNT: usize = 100;
107
108
    let runtime = Builder::new_multi_thread().enable_all().build().expect("Can't create runtime");
109
    let room_id = owned_room_id!("!homohominilupusest:example.com");
110
    let sender_id = owned_user_id!("@sender:example.com");
111
112
    let f = EventFactory::new().room(&room_id).sender(&sender_id);
113
114
    let mut joined_room_builder =
115
        JoinedRoomBuilder::new(&room_id).add_state_event(f.room_encryption());
116
117
    let pinned_event_ids: Vec<OwnedEventId> = (0..PINNED_EVENTS_COUNT)
118
        .map(|i| {
119
            EventId::new_v2_or_v3(&base64_sha256_hash(format!("${i}").as_bytes()))
120
                .expect("Invalid event id")
121
        })
122
        .collect();
123
    joined_room_builder =
124
        joined_room_builder.add_state_bulk(vec![f.room_pinned_events(pinned_event_ids).into()]);
125
126
    let (server, client, room) = runtime.block_on(async move {
127
        let server = MatrixMockServer::new().await;
128
        let client = server.client_builder().build().await;
129
130
        let room = server.sync_room(&client, joined_room_builder).await;
131
132
        server
133
            .mock_room_event()
134
            .respond_with(move |r: &Request| {
135
                let segments: Vec<&str> = r.url.path_segments().expect("Invalid path").collect();
136
                let event_id_str = segments[6];
137
                let event_id = EventId::parse(event_id_str).expect("Invalid event id in response");
138
                let event = f
139
                    .text_msg(format!("Message {event_id_str}"))
140
                    .event_id(&event_id)
141
                    .server_ts(MilliSecondsSinceUnixEpoch::now())
142
                    .into_raw_sync();
143
                ResponseTemplate::new(200)
144
                    .set_delay(Duration::from_millis(50))
145
                    .set_body_json(event.json())
146
            })
147
            .mount()
148
            .await;
149
150
        client.event_cache().subscribe().unwrap();
151
152
        (server, client, room)
153
    });
154
155
    let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
156
    assert!(!pinned_event_ids.is_empty());
157
    assert_eq!(pinned_event_ids.len(), PINNED_EVENTS_COUNT);
158
159
    let count = PINNED_EVENTS_COUNT;
160
    let name = format!("{count} pinned events");
161
    let mut group = c.benchmark_group("Load pinned events");
162
    group.throughput(Throughput::Elements(count as u64));
163
    group.sample_size(10);
164
165
    group.bench_function(BenchmarkId::new("Load pinned events [memory]", name), |b| {
166
        b.to_async(&runtime).iter(|| async {
167
            let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
168
            assert!(!pinned_event_ids.is_empty());
169
            assert_eq!(pinned_event_ids.len(), PINNED_EVENTS_COUNT);
170
171
            // Reset cache so it always loads the events from the mocked endpoint
172
            client
173
                .event_cache_store()
174
                .lock()
175
                .await
176
                .unwrap()
177
                .as_clean()
178
                .unwrap()
179
                .clear_all_linked_chunks()
180
                .await
181
                .unwrap();
182
183
            let timeline = TimelineBuilder::new(&room)
184
                .with_focus(TimelineFocus::PinnedEvents)
185
                .build()
186
                .await
187
                .expect("Could not create timeline");
188
189
            let (items, _) = timeline.subscribe().await;
190
            assert_eq!(items.len(), PINNED_EVENTS_COUNT + 1);
191
            timeline.clear().await;
192
        });
193
    });
194
195
    {
196
        let _guard = runtime.enter();
197
        drop(server);
198
    }
199
200
    group.finish();
201
}
202
203
criterion_group! {
204
    name = room;
205
    config = Criterion::default();
206
    targets = receive_all_members_benchmark, load_pinned_events_benchmark,
207
}
208
criterion_main!(room);