Reduce Meet poll load with shared cache, ETag, and slower clients.
Deploy Ladill Meet / deploy (push) Successful in 2m44s

Heavy per-participant poll queries were saturating the shared host during
live rooms. Cache the shared room payload for 2s, return 304 when unchanged,
throttle breakout expiry and captions, and slow client polling (6s / 20s
when hidden) so concurrent attendees no longer fan out full Laravel work.
This commit is contained in:
isaacclad
2026-07-15 19:59:52 +00:00
parent cdb81b82da
commit 86fa37b9fd
3 changed files with 193 additions and 72 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ Alpine.data('waitingRoom', () => ({
pollTimer: null,
init() {
this.pollTimer = setInterval(() => this.poll(), 2500);
this.pollTimer = setInterval(() => this.poll(), 5000);
this.poll();
},
+68 -5
View File
@@ -180,6 +180,11 @@ function meetRoom() {
pictureInPictureActive: false,
pictureInPictureSupported: false,
pollTimer: null,
pollInFlight: false,
pollEtag: null,
pollIntervalMs: 6000,
pollHiddenIntervalMs: 20000,
_lastCaptionSentAt: 0,
config: {},
init() {
@@ -308,7 +313,7 @@ function meetRoom() {
this.connectionStatus = 'Video unavailable — configure LiveKit';
}
this.pollTimer = setInterval(() => this.poll(), 3000);
this.startPolling();
this.applyStageLayout();
this.$nextTick(() => {
@@ -319,6 +324,34 @@ function meetRoom() {
this.setupPictureInPicture();
},
currentPollInterval() {
if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {
return this.pollHiddenIntervalMs;
}
return this.pollIntervalMs;
},
startPolling() {
if (this.pollTimer) {
clearInterval(this.pollTimer);
this.pollTimer = null;
}
this.poll();
this.pollTimer = setInterval(() => this.poll(), this.currentPollInterval());
if (typeof document !== 'undefined' && !this._pollVisibilityBound) {
this._pollVisibilityBound = true;
document.addEventListener('visibilitychange', () => {
if (this.goingLive) {
return;
}
this.startPolling();
});
}
},
connectionErrorMessage(error) {
const name = error?.name || '';
const message = error?.message || '';
@@ -1739,13 +1772,20 @@ function meetRoom() {
return;
}
this.captionText = normalized;
if (normalized === this._lastCaptionSent) {
this.captionText = normalized;
return;
}
// Throttle caption posts — speech recognition fires very frequently.
const now = Date.now();
if (now - (this._lastCaptionSentAt || 0) < 2000) {
return;
}
this._lastCaptionSent = normalized;
this.captionText = normalized;
this._lastCaptionSentAt = now;
fetch(this.config.captionUrl, {
method: 'POST',
@@ -3079,7 +3119,7 @@ function meetRoom() {
window.location.replace(data.room_url || `/room/${data.session_uuid}`);
} catch {
this.goingLive = false;
this.pollTimer = setInterval(() => this.poll(), 3000);
this.startPolling();
}
},
@@ -3201,8 +3241,29 @@ function meetRoom() {
},
async poll() {
if (this.pollInFlight || !this.config.pollUrl) {
return;
}
this.pollInFlight = true;
try {
const res = await fetch(this.config.pollUrl, { headers: { Accept: 'application/json' } });
const headers = { Accept: 'application/json' };
if (this.pollEtag) {
headers['If-None-Match'] = this.pollEtag;
}
const res = await fetch(this.config.pollUrl, { headers, credentials: 'same-origin' });
const responseEtag = res.headers.get('ETag');
if (responseEtag) {
this.pollEtag = responseEtag;
}
if (res.status === 304) {
return;
}
if (!res.ok) {
if (res.status === 404 && this.config.endedUrl) {
this.redirectToMeetingEnded(this.config.endedUrl);
@@ -3253,6 +3314,8 @@ function meetRoom() {
this.processReactionsFromPoll(data.reactions || []);
} catch (e) {
// ignore poll errors
} finally {
this.pollInFlight = false;
}
},