Add server failover rotation for classworkscloud provider#36
Conversation
Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
Deploying classworks with
|
| Latest commit: |
a997e35
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://830afba1.classworks.pages.dev |
| Branch Preview URL: | https://copilot-add-server-polling-f.classworks.pages.dev |
…O limitation Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
Co-authored-by: Sunwuyuan <88357633+Sunwuyuan@users.noreply.github.com>
|
@copilot getEffectiveServerUrl似乎会导致资源浪费,改成直接使用第一个url访问资源,出网络问题了再试下一个 |
There was a problem hiding this comment.
Pull request overview
This PR implements automatic server failover for the classworkscloud provider to handle outages by trying servers in priority order (kv-service.houlang.cloud → kv-service.wuyuan.dev). The implementation adds a new rotation utility and integrates it across the API layer while maintaining backward compatibility with other providers.
Changes:
- New server rotation utility with fallback mechanism for
classworkscloudprovider - Integration of rotation logic into all data provider methods (loadData, saveData, loadKeys, loadNamespaceInfo, updateNamespaceInfo)
- Enhanced connection diagnostics showing attempted servers and their status
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/serverRotation.js | New utility implementing tryWithRotation, getEffectiveServerUrl, and isRotationEnabled for server failover |
| src/utils/providers/kvServerProvider.js | All five API methods updated to use rotation when classworkscloud provider is active |
| src/utils/api.js | getNamespaceInfo method updated to support rotation |
| src/utils/dataProvider.js | getKeyCloudUrl updated to use effective server URL for classworkscloud |
| src/utils/socketClient.js | Socket connection uses effective server URL (primary server only, no mid-connection rotation) |
| src/components/settings/cards/DataProviderSettingsCard.vue | Connection test enhanced to display attempt log with server status |
Comments suppressed due to low confidence (1)
src/utils/providers/kvServerProvider.js:50
- The rotation logic pattern is duplicated across all five methods in this file (loadNamespaceInfo, updateNamespaceInfo, loadData, saveData, loadKeys). This creates a maintenance burden where changes to the rotation behavior must be applied in five separate locations. Consider extracting this into a helper function that wraps the server-specific logic.
async loadNamespaceInfo() {
try {
// Use rotation for classworkscloud provider
if (isRotationEnabled()) {
return await tryWithRotation(async (serverUrl) => {
const res = await axios.get(`${serverUrl}/kv/_info`, {
headers: getHeaders(),
});
return formatResponse(res.data);
});
}
// Standard single-server mode
const serverUrl = getSetting("server.domain");
const res = await axios.get(`${serverUrl}/kv/_info`, {
headers: getHeaders(),
});
// 直接返回新格式 API 数据,包含 device 和 account 信息
return formatResponse(res.data);
} catch (error) {
console.error("获取命名空间信息失败:", error);
return formatError(
error.response?.data?.message || "获取命名空间信息失败",
"NAMESPACE_ERROR"
);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (isRotationEnabled()) { | ||
| return await tryWithRotation(async (serverUrl) => { | ||
| const res = await axios.get(`${serverUrl}/kv/${key}`, { | ||
| headers: getHeaders(), | ||
| }); | ||
| return formatResponse(res.data); | ||
| }); |
There was a problem hiding this comment.
Same error handling inconsistency as in other methods: when tryWithRotation fails, the error thrown from serverRotation.js may not have the error.response structure expected by the catch block at lines 97-106, particularly when checking error.response?.status and error.response?.data?.message.
| if (isRotationEnabled()) { | ||
| return await tryWithRotation(async (serverUrl) => { | ||
| await axios.post(`${serverUrl}/kv/${key}`, data, { | ||
| headers: getHeaders(), | ||
| }); | ||
| return formatResponse(true); | ||
| }); |
There was a problem hiding this comment.
Same error handling inconsistency as in other methods: when tryWithRotation fails, the error thrown from serverRotation.js may not have the error.response structure expected by the catch block at lines 126-132.
| if (isRotationEnabled()) { | ||
| return await tryWithRotation(async (serverUrl) => { | ||
| const res = await axios.get(`${serverUrl}/kv/_keys?${params}`, { | ||
| headers: getHeaders(), | ||
| }); | ||
| return formatResponse(res.data); | ||
| }); |
There was a problem hiding this comment.
Same error handling inconsistency as in other methods: when tryWithRotation fails, the error thrown from serverRotation.js may not have the error.response structure expected by the catch block at lines 190-205, particularly when checking error.response?.status for 404, 403, and 401 errors.
| // All servers failed | ||
| console.error("All servers failed. Tried:", triedServers); | ||
| const error = lastError || new Error("All servers failed"); | ||
| error.triedServers = triedServers; |
There was a problem hiding this comment.
When all servers fail, the error thrown here preserves the lastError from the final server attempt, which is an axios error with error.response structure. However, if the operation function throws a non-axios error (e.g., a validation error or custom error), the error handling in calling code that expects error.response will break. Consider documenting that the operation function should throw axios errors, or wrapping errors consistently to ensure they have the expected structure for downstream error handlers.
| error.triedServers = triedServers; | |
| error.triedServers = triedServers; | |
| // Ensure downstream handlers that expect an axios-style error.response do not break | |
| if (!error.response) { | |
| error.response = { | |
| data: { | |
| message: error.message || "All servers failed", | |
| triedServers, | |
| }, | |
| }; | |
| } |
|
|
||
| import {io} from 'socket.io-client'; | ||
| import {getSetting} from '@/utils/settings'; | ||
| import {getEffectiveServerUrl, isRotationEnabled, tryWithRotation} from '@/utils/serverRotation'; |
There was a problem hiding this comment.
The tryWithRotation import is unused in this file. Only getEffectiveServerUrl and isRotationEnabled are actually used. Remove the unused import to keep the code clean.
| import {getEffectiveServerUrl, isRotationEnabled, tryWithRotation} from '@/utils/serverRotation'; | |
| import {getEffectiveServerUrl, isRotationEnabled} from '@/utils/serverRotation'; |
|
|
||
| if (triedServers.length > 0) { | ||
| const serverList = triedServers.map((s, i) => | ||
| `${i + 1}. ${s.url} (失败${s.error ? `: ${s.error}` : ""})` |
There was a problem hiding this comment.
The error message formatting has inconsistent spacing. Line 206 shows "失败${s.error ? : ${s.error} : ""}" which will produce output like "失败: error message" (with Chinese characters followed by a colon and space). However, in Chinese text formatting, it's more conventional to use Chinese punctuation (如:"失败:error message") or omit the space. Consider using either "失败: ${s.error}" with proper Chinese formatting or ensuring consistent spacing throughout the user-facing messages.
| `${i + 1}. ${s.url} (失败${s.error ? `: ${s.error}` : ""})` | |
| `${i + 1}. ${s.url} (失败${s.error ? `:${s.error}` : ""})` |
| if (isRotationEnabled()) { | ||
| return await tryWithRotation(async (serverUrl) => { | ||
| const res = await axios.get(`${serverUrl}/kv/_info`, { | ||
| headers: getHeaders(), | ||
| }); | ||
| return formatResponse(res.data); | ||
| }); |
There was a problem hiding this comment.
Error handling for rotation failures is inconsistent with the non-rotation path. When tryWithRotation fails, the error object will be thrown from serverRotation.js line 80 and caught here. However, this error may not have the same structure as axios errors (e.g., error.response), which the catch block at lines 44-49 expects when checking error.response?.data?.message. This could result in less informative error messages when all servers fail during rotation. Consider normalizing the error structure from tryWithRotation or adjusting the error handling to work with both error types.
| if (isRotationEnabled()) { | ||
| return await tryWithRotation(async (serverUrl) => { | ||
| const res = await axios.put(`${serverUrl}/kv/_info`, data, { | ||
| headers: getHeaders(), | ||
| }); | ||
| return res; | ||
| }); |
There was a problem hiding this comment.
Same error handling inconsistency as in loadNamespaceInfo: when tryWithRotation fails, the error thrown from serverRotation.js may not have the error.response structure expected by the catch block at lines 71-76. This affects the quality of error messages presented to users when all servers fail.
| if (isRotationEnabled()) { | ||
| const response = await tryWithRotation(async (serverUrl) => { | ||
| return await axios.get(`${serverUrl}/kv/_info`, { | ||
| headers: getHeaders(), | ||
| }); | ||
| }); | ||
| return response.data; |
There was a problem hiding this comment.
Inconsistent error handling: when tryWithRotation fails, it throws an error that may not have the error.response structure expected by line 55 when checking error.response?.data?.message. This could result in less informative error messages when all servers fail during rotation.
| // For classworkscloud, create socket with the first server in rotation | ||
| // Note: Socket.IO's built-in reconnection will retry the same server URL. | ||
| // Server rotation is handled at the HTTP request level, not Socket.IO level. | ||
| // If the Socket.IO server goes down, the connection will fail until the server recovers. |
There was a problem hiding this comment.
Socket.IO connections require a stable server URL and cannot benefit from HTTP-level rotation during the WebSocket connection lifecycle. The comment correctly notes that Socket.IO's built-in reconnection will retry the same server URL. However, this creates a potential availability issue: if the primary server (kv-service.houlang.cloud) fails after the socket is established, the application will lose real-time connectivity until that specific server recovers, even though the secondary server might be available. Consider implementing a mechanism to detect socket connection failures and recreate the socket with the next server in rotation, or document this limitation more prominently for users.
| // For classworkscloud, create socket with the first server in rotation | |
| // Note: Socket.IO's built-in reconnection will retry the same server URL. | |
| // Server rotation is handled at the HTTP request level, not Socket.IO level. | |
| // If the Socket.IO server goes down, the connection will fail until the server recovers. | |
| // For classworkscloud, create the Socket.IO connection to a single effective server URL. | |
| // IMPORTANT: Socket.IO's built-in reconnection will retry only this same server URL. | |
| // Server rotation is applied when selecting the URL, but not during the WebSocket lifecycle. | |
| // If this specific Socket.IO server becomes unavailable after connection, real-time updates | |
| // from this client will be lost until that server recovers or the application explicitly | |
| // tears down and recreates the socket (for example, after a full page reload or via | |
| // custom app logic that re-calls getSocket() with a different server URL). |
|
此拉取请求已在 Kernyr 上被提及。那里可能有相关详细信息: |
Implements automatic failover across multiple server endpoints for the
classworkscloudprovider to handle server outages. Servers are tried in priority order (kv-service.houlang.cloud→kv-service.wuyuan.dev) on every request, allowing immediate recovery when primary server returns.Changes
New rotation utility (
src/utils/serverRotation.js)tryWithRotation(): Executes operations with automatic server fallbackgetEffectiveServerUrl(): Returns primary server URL for initial connectionsisRotationEnabled(): Guards rotation logic toclassworkscloudprovider onlyProvider layer updates
kvServerProvider.js: All API methods (loadData,saveData,loadKeys,loadNamespaceInfo,updateNamespaceInfo) use rotation when enabledapi.js,dataProvider.js: Apply rotation to namespace operations and cloud URL generationsocketClient.js: Uses effective server URL for Socket.IO (rotation not applicable at transport layer)Enhanced connection diagnostics
DataProviderSettingsCard.vue: Connection test button now displays detailed attempt log showing which servers were tried and their statusExample
Backward compatibility:
kv-localandkv-serverproviders unaffected—rotation only activates forclassworkscloud.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.