main
tag: launch-2026-06-01
tag: magicmirror-v0.1.1
tag: magicmirror-v0.3.0
tag: mnw-cli-v0.1.2
tag: mnw-cli-v0.1.3
tag: mnw-cli-v0.1.4
tag: pom-v0.4.1
tag: pom-v0.4.2
tag: pom-v0.4.3
tag: pom-v0.4.4
tag: pom-v0.4.5
tag: wam-v0.3.0
tag: wam-v0.3.1
Files
Commits
Tags
Notes
Issues
mt: spawn the read-position bump off the thread GET
The thread view awaited update_read_position inline, so every tracked viewer of
an unauthenticated, unthrottled GET blocked the page render on a write to the
request pool (fuzz-2026-07-06 write-on-GET). Spawn it best-effort — the page
renders immediately and a dropped bump just re-bumps on the next view. The
read-position test polls for the async write.
Co-Authored-By
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 files changed,
+30 insertions,
-16 deletions
123
123
// Track the thread
124
124
mt_db::mutations::track_thread(&h.db, user_id, thread_id).await.unwrap();
125
125
126
-
// View thread — should update read position
126
+
// View thread — should update read position. The bump is now spawned off the
127
+
// response path, so poll briefly for it rather than reading immediately.
127
128
let thread_url = format!("/p/test/general/{}", thread_id);
128
129
h.client.get(&thread_url).await;
129
130
130
-
// Check last_read_post_id is set
131
-
let row: Option<(Option<uuid::Uuid>,)> = sqlx::query_as(
132
-
"SELECT last_read_post_id FROM tracked_threads WHERE user_id = $1 AND thread_id = $2",
133
-
)
134
-
.bind(user_id)
135
-
.bind(thread_id)
136
-
.fetch_optional(&h.db)
137
-
.await
138
-
.unwrap();
139
-
assert!(row.is_some(), "Tracking row should exist");
140
-
assert!(row.unwrap().0.is_some(), "last_read_post_id should be set after viewing");
131
+
let mut last_read: Option<uuid::Uuid> = None;
132
+
for _ in 0..50 {
133
+
let row: Option<(Option<uuid::Uuid>,)> = sqlx::query_as(
134
+
"SELECT last_read_post_id FROM tracked_threads WHERE user_id = $1 AND thread_id = $2",
135
+
)
136
+
.bind(user_id)
137
+
.bind(thread_id)
138
+
.fetch_optional(&h.db)
139
+
.await
140
+
.unwrap();
141
+
assert!(row.is_some(), "Tracking row should exist");
142
+
last_read = row.unwrap().0;
143
+
if last_read.is_some() {
144
+
break;
145
+
}
146
+
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
147
+
}
148
+
assert!(last_read.is_some(), "last_read_post_id should be set after viewing");
141
149
}
142
150
143
151
#[tokio::test]
198
198
})
199
199
.collect();
200
200
201
-
// If tracked, update read position to the last post on the current page
201
+
// If tracked, update read position to the last post on the current page.
202
+
// Spawned off the response path: this is a DB write on an unauthenticated,
203
+
// unthrottled GET, so awaiting it inline made every tracked viewer block the
204
+
// page render on a write to the request pool (fuzz-2026-07-06 write-on-GET).
205
+
// Best-effort — a dropped bump just re-bumps on the next view.
202
206
if is_tracked
203
207
&& let Some(ref user) = session_user
204
208
&& let Some(last_post) = posts.last()
205
209
&& let Ok(last_post_id) = uuid::Uuid::parse_str(&last_post.id)
206
210
{
207
-
let _ = mt_db::mutations::update_read_position(
208
-
&state.db, user.user_id, thread_uuid, last_post_id,
209
-
).await;
211
+
let db = state.db.clone();
212
+
let user_id = user.user_id;
213
+
tokio::spawn(async move {
214
+
let _ = mt_db::mutations::update_read_position(&db, user_id, thread_uuid, last_post_id).await;
215
+
});
210
216
}
211
217
212
218
let session_user = session_user.as_ref().map(|u| template_user(u, state.config.platform_admin_id));