refactor(frontend): remove HTTP polling fallback — WS-only for translate runs
Previously startTranslationRun() used both WebSocket + 2s HTTP polling.
After WS debug logging was added, the HTTP fallback is no longer needed:
- WS streams status every 1s from backend
- WS handler already detects terminal states + fires onComplete
- WS errors are now logged (onerror → console.warn)
Removed: pollStatus(), _pollingInterval, _pollCount, MAX_POLLS,
fetchRunStatus import, setInterval in startTranslationRun,
clearInterval in stopTranslationRun.
Build ✅ 698 tests ✅
This commit is contained in:
@@ -2,19 +2,8 @@
|
||||
// @BRIEF Global store for active translation run progress — uses WebSocket for real-time
|
||||
// progress + polling fallback for structured status. Survives page navigation.
|
||||
// @LAYER UI
|
||||
// @RELATION DEPENDS_ON -> [TranslateRunsApi.fetchRunStatus]
|
||||
// @RELATION DEPENDS_ON -> [ApiModule.getTranslateRunWsUrl]
|
||||
// @UX_STATE idle -> No active run
|
||||
// @UX_STATE running -> Polling + WebSocket active, progress bar updates in real-time
|
||||
// @UX_STATE completed/partial/failed/insert_failed/cancelled -> Terminal states
|
||||
// @UX_FEEDBACK WebSocket provides real-time batch completion events during translate phase
|
||||
// @UX_FEEDBACK Polling provides structured status updates (insert phase, final counts)
|
||||
// @UX_RECOVERY WebSocket disconnect -> continues with polling only
|
||||
// @RATIONALE Migrated from Svelte 4 writable+derived to Svelte 5 $state+$derived runes.
|
||||
// Backward-compatible .subscribe() retained for $translationRunStore syntax.
|
||||
// WebSocket and polling logic preserved exactly.
|
||||
|
||||
import { fetchRunStatus } from '$lib/api/translate';
|
||||
import { getTranslateRunWsUrl } from '$lib/api';
|
||||
|
||||
export type UxState = 'idle' | 'running' | 'inserting' | 'completed' | 'partial' | 'failed' | 'insert_failed' | 'cancelled';
|
||||
@@ -105,9 +94,6 @@ export const translationRunStore = {
|
||||
},
|
||||
};
|
||||
|
||||
let _pollingInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let _pollCount = 0;
|
||||
const MAX_POLLS = 300;
|
||||
let _onCompleteCallback: ((data: Record<string, unknown>) => void) | null = null;
|
||||
let _ws: WebSocket | null = null;
|
||||
|
||||
@@ -132,10 +118,6 @@ export function startTranslationRun(runId: string, options: StartRunOptions = {}
|
||||
_onCompleteCallback = options.onComplete || null;
|
||||
|
||||
_connectWebSocket(runId);
|
||||
|
||||
_pollCount = 0;
|
||||
_pollingInterval = setInterval(pollStatus, 2000);
|
||||
pollStatus();
|
||||
}
|
||||
|
||||
function _connectWebSocket(runId: string): void {
|
||||
@@ -209,10 +191,6 @@ export function stopTranslationRun(): void {
|
||||
_ws.close();
|
||||
_ws = null;
|
||||
}
|
||||
if (_pollingInterval) {
|
||||
clearInterval(_pollingInterval);
|
||||
_pollingInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function resetTranslationRun(): void {
|
||||
@@ -227,69 +205,4 @@ export function updateTranslationRunState(state: Partial<TranslationRunState>):
|
||||
_notify();
|
||||
}
|
||||
|
||||
async function pollStatus(): Promise<void> {
|
||||
_pollCount++;
|
||||
if (_pollCount > MAX_POLLS) {
|
||||
stopTranslationRun();
|
||||
_state = { ..._state, uxState: 'failed' };
|
||||
_notify();
|
||||
if (_onCompleteCallback) _onCompleteCallback({ status: 'TIMEOUT', error_message: 'Translation run timed out' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_state.runId) {
|
||||
stopTranslationRun();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = (await fetchRunStatus(_state.runId)) as Record<string, unknown>;
|
||||
if (!data) return;
|
||||
|
||||
const s = data?.status as string;
|
||||
const insertS = data?.insert_status as string;
|
||||
|
||||
let newUxState: UxState = _state.uxState;
|
||||
|
||||
if (s === 'PENDING' || s === 'RUNNING') {
|
||||
newUxState = (insertS === 'started' || insertS === 'pending' || insertS === 'running') ? 'inserting' : 'running';
|
||||
} else if (s === 'COMPLETED') {
|
||||
newUxState = (insertS === 'failed' || insertS === 'timeout') ? 'insert_failed'
|
||||
: (data.failed_records as number) > 0 ? 'partial' : 'completed';
|
||||
stopTranslationRun();
|
||||
} else if (s === 'FAILED') {
|
||||
newUxState = 'failed';
|
||||
stopTranslationRun();
|
||||
} else if (s === 'CANCELLED') {
|
||||
newUxState = 'cancelled';
|
||||
stopTranslationRun();
|
||||
}
|
||||
|
||||
const total = (data?.total_records as number) || 0;
|
||||
const pct = total > 0
|
||||
? Math.round((((data.successful_records as number) + (data.failed_records as number) + (data.skipped_records as number)) / total) * 100)
|
||||
: 0;
|
||||
|
||||
_state = {
|
||||
..._state,
|
||||
uxState: newUxState,
|
||||
status: data,
|
||||
totalRecords: total,
|
||||
successfulRecords: (data?.successful_records as number) || 0,
|
||||
failedRecords: (data?.failed_records as number) || 0,
|
||||
skippedRecords: (data?.skipped_records as number) || 0,
|
||||
cacheHits: (data?.cache_hits as number) || 0,
|
||||
progressPct: pct,
|
||||
insertStatus: (data?.insert_status as string) || null,
|
||||
batchCount: (data?.batch_count as number) || 0,
|
||||
};
|
||||
_notify();
|
||||
|
||||
if (newUxState !== 'running' && newUxState !== 'inserting') {
|
||||
if (_onCompleteCallback) _onCompleteCallback(data);
|
||||
}
|
||||
} catch (_err) {
|
||||
// Poll error — will retry on next interval
|
||||
}
|
||||
}
|
||||
// #endregion TranslationRunStore
|
||||
|
||||
Reference in New Issue
Block a user