Skip to content

feat(serverRotation): latency-aware server selection with 404-safe rotation#52

Merged
Sunwuyuan merged 2 commits into
mainfrom
copilot/optimize-server-selection-code
Apr 17, 2026
Merged

feat(serverRotation): latency-aware server selection with 404-safe rotation#52
Sunwuyuan merged 2 commits into
mainfrom
copilot/optimize-server-selection-code

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 17, 2026

Server rotation blindly retried all errors (including 404) across every server, always started from the same fixed server, and re-probed on every request with no caching.

Changes

  • 404 ≠ rotation triggershouldRotateOnError() now only rotates on network errors or HTTP 5xx. A 4xx response means the server is healthy; propagate immediately instead of wasting a round-trip on the backup.

  • Latency-based preferenceprobeServer() / updateServerPreference() concurrently HEAD-probes all cloud servers and caches the fastest reachable one. getOrderedCloudServers() puts the preferred server first without blocking the caller.

  • 5-minute TTL cache, no polling — Preference is stored in a module-level serverPreference object. Stale cache triggers a fire-and-forget background re-probe; callers are never blocked. Real-traffic successes in tryWithRotation also update the cache via setCachedPreference().

  • getEffectiveServerUrl now returns the cached preferred server instead of always hardcoding CLASSWORKS_CLOUD_SERVERS[0].

// Before: any error (including 404) caused rotation + always started from server[0]
// After: 404 from server A propagates immediately; background probe keeps fastest server first

function shouldRotateOnError(error) {
  if (!error.response) return true;      // network / timeout → rotate
  return error.response.status >= 500;   // 5xx → rotate; 4xx → throw immediately
}

Copilot AI and others added 2 commits April 17, 2026 13:49
…ing, and 404-aware rotation

Agent-Logs-Url: https://github.com/Moonrend/Classworks/sessions/d254def7-bda7-413a-83b1-c553c1571523

Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
… serverRotation

Agent-Logs-Url: https://github.com/Moonrend/Classworks/sessions/d254def7-bda7-413a-83b1-c553c1571523

Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
Copilot AI self-assigned this Apr 17, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
classworks Ready Ready Preview, Comment Apr 17, 2026 1:53pm

@Sunwuyuan Sunwuyuan merged commit e9a7b66 into main Apr 17, 2026
8 of 10 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances Classworks Cloud server selection by making rotation error-aware (avoid rotating on 4xx), adding latency-based preferred server caching with background probing, and updating the effective server URL to use the cached preference. It also includes widespread Vue template formatting/slot syntax cleanup and a small ESLint globals update to support the new probing implementation.

Changes:

  • Update serverRotation to rotate only on network/5xx errors, and to prefer/cached the fastest reachable server via background probes.
  • Replace hardcoded Classworks Cloud base server selection with cached preference in getEffectiveServerUrl.
  • Apply broad Vue template formatting normalization and add AbortController to ESLint globals.

Reviewed changes

Copilot reviewed 64 out of 71 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/serverRotation.js Latency-aware preferred server caching, 4xx-safe rotation logic, background probing/TTL behavior
src/pages/socket-debugger.vue Template formatting normalization
src/pages/settings.vue Template formatting normalization
src/pages/list/index.vue Template formatting normalization
src/pages/list/[id].vue Template formatting normalization
src/pages/index.vue Template formatting normalization
src/pages/examschedule.vue Template formatting normalization (incl. placeholder escaping)
src/pages/exam-editor/[id].vue Template formatting normalization
src/pages/debug.vue Template formatting normalization
src/pages/debug-socket.vue Template formatting normalization
src/pages/debug-init.vue Template formatting normalization
src/pages/cses2wakeup.vue Template formatting normalization
src/pages/authorize.vue Template formatting normalization
src/pages/CacheManagement.vue Template formatting normalization
src/pages/404.vue Template formatting normalization
src/components/settings/cards/ThemeSettingsCard.vue Minor option API reordering + template formatting
src/components/settings/cards/SubjectManagementCard.vue Slot syntax normalization + template formatting
src/components/settings/cards/RefreshSettingsCard.vue Template formatting normalization
src/components/settings/cards/RandomPickerCard.vue Template formatting normalization
src/components/settings/cards/KvDatabaseCard.vue Template formatting normalization
src/components/settings/cards/HomeworkTemplateCard.vue Slot syntax normalization + template formatting
src/components/settings/cards/EditSettingsCard.vue Template formatting normalization
src/components/settings/cards/EchoChamberCard.vue Lifecycle hook ordering + template formatting
src/components/settings/cards/DisplaySettingsCard.vue Template formatting normalization
src/components/settings/cards/DataProviderSettingsCard.vue Template formatting normalization (rotation integration context)
src/components/settings/cards/CloudNamespaceInfoCard.vue Template formatting normalization
src/components/settings/cards/BackgroundSettingsCard.vue Template formatting normalization
src/components/settings/TeacherListCard.vue Template formatting normalization (incl. placeholder escaping)
src/components/settings/StudentListCard.vue Template formatting normalization
src/components/settings/SettingsExplorer.vue Template formatting normalization
src/components/settings/SettingItem.vue Slot syntax normalization + template formatting
src/components/settings/SettingGroup.vue Template formatting normalization
src/components/settings/NotificationSoundSettings.vue Template formatting normalization
src/components/settings/CloudMigrationDialog.vue Slot syntax normalization + template formatting
src/components/settings/AboutCard.vue Template formatting normalization
src/components/home/HomeworkGrid.vue Minor option API reordering + template formatting
src/components/home/HomeActions.vue Template formatting normalization
src/components/home/ExamScheduleCard.vue Template formatting normalization
src/components/home/ConciseExamCard.vue Template formatting normalization
src/components/error/404.vue Template formatting + minor router call simplification
src/components/common/HomeSkeleton.vue Template formatting normalization
src/components/common/AsyncLoadingPlaceholder.vue Template formatting normalization
src/components/auth/TokenInputDialog.vue Template formatting normalization
src/components/auth/ProgressiveRegisterPage.vue Template formatting normalization
src/components/auth/FirstTimeGuide.vue Template formatting normalization
src/components/auth/DeviceAuthDialog.vue Template formatting normalization
src/components/auth/AlternativeCodeDialog.vue Template formatting normalization
src/components/attendance/AttendanceSidebar.vue Template formatting normalization
src/components/attendance/AttendanceManagementDialog.vue Template formatting normalization
src/components/UrgentTestDialog.vue Template formatting normalization
src/components/UrgentNotification.vue Template formatting normalization
src/components/TimeCard.vue Template formatting normalization
src/components/StudentNameManager.vue Template formatting normalization
src/components/SettingsLinkGenerator.vue Watcher reordering + template formatting normalization
src/components/SettingsCard.vue Template formatting normalization
src/components/ReadOnlyTokenWarning.vue Template formatting normalization
src/components/RateLimitModal.vue Template formatting (introduces a rendering bug with divider scope)
src/components/RandomPicker.vue Template formatting normalization
src/components/PwaInstallCard.vue Template formatting normalization
src/components/NoiseMonitorCard.vue Template formatting normalization
src/components/MessageLog.vue Template formatting normalization
src/components/HomeworkEditDialog.vue Template formatting normalization
src/components/HitokotoSettings.vue Template formatting normalization
src/components/HelloWorld.vue Template formatting normalization
src/components/GlobalMessage.vue Template formatting normalization
src/components/FloatingToolbar.vue Template formatting normalization
src/components/CacheManager.vue Template formatting normalization
src/components/AppHeader.vue Slot syntax normalization + template formatting
src/App.vue Template formatting normalization
eslint.config.js Add AbortController to globals for new probing code

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +79 to +99
async function updateServerPreference() {
if (serverPreference.probing) return;
serverPreference.probing = true;
try {
const results = await Promise.all(
CLASSWORKS_CLOUD_SERVERS.map(async (url) => ({
url,
latency: await probeServer(url),
}))
);
const reachable = results
.filter((r) => r.latency < Infinity)
.sort((a, b) => a.latency - b.latency);
if (reachable.length > 0) {
setCachedPreference(reachable[0].url);
}
} catch {
// Probe failure is non-fatal; keep existing preference
} finally {
serverPreference.probing = false;
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateServerPreference() only updates cachedAt when at least one server is reachable. If all probes fail (e.g. outage/CORS), cachedAt stays 0 so getOrderedCloudServers() will trigger a new background probe on every call, causing repeated HEAD requests and undermining the intended 5‑minute TTL. Consider recording a lastProbedAt/updating cachedAt even on failure (or adding backoff) to avoid continuous probing during outages.

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +191
// For HTTP 4xx errors the server is alive — propagate immediately without rotation
if (!shouldRotateOnError(error)) {
triedServers[triedServers.length - 1].status = "client-error";
if (hasCallback) {
onServerTried({ url: serverUrl, status: "client-error", error, tried: [...triedServers] });
}
throw error;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the non-rotating (4xx) path, the last triedServers entry is marked as client-error but its .error field is never populated. Callers that display per-server failure details (e.g. mapping s.error) will lose the status/message for the final attempt. Consider storing a normalized error string on that entry as well (similar to the failed path).

Copilot uses AI. Check for mistakes.
Comment on lines 28 to +51
@@ -27,12 +44,11 @@
{{ request.method }} {{ request.path }}
</v-list-item-subtitle>
</v-list-item>
</v-list
>
</v-list>
<v-divider
v-if="index < activeRequests.length - 1"
class="my-3"
></v-divider>
/>
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<v-divider v-if="index < activeRequests.length - 1" ... /> is outside the v-for that defines index, so index is undefined here and this will either fail template compilation or behave incorrectly. Move the divider inside the loop (or wrap the item+divider in a <template v-for> block) so index is in scope.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants