fix(web): harden route transitions and monitor memory

This commit is contained in:
lingniu
2026-07-15 23:54:21 +08:00
parent 3fabcf181a
commit c29ccdf2da
17 changed files with 332 additions and 36 deletions

View File

@@ -26,6 +26,31 @@ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o ../
/opt/lingniu-vehicle-platform/env/platform.env
```
## Browser-safe release switch
The web application splits every major route into a hashed lazy-loaded asset. An already-open browser tab can therefore request the previous release's route asset after `current` has switched. Keep exactly one previous generation of original build assets in the new release before the atomic symlink switch:
```bash
CURRENT_WEB=/opt/lingniu-vehicle-platform/current/web
NEW_WEB=/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/web
# Record only this release's own build assets before adding compatibility files.
find "$NEW_WEB/assets" -type f -printf '%P\n' | sort > "$NEW_WEB/.release-assets"
if [ -f "$CURRENT_WEB/.release-assets" ]; then
while IFS= read -r asset; do
test -n "$asset" || continue
mkdir -p "$NEW_WEB/assets/$(dirname "$asset")"
cp -n "$CURRENT_WEB/assets/$asset" "$NEW_WEB/assets/$asset"
done < "$CURRENT_WEB/.release-assets"
else
# One-time compatibility for releases created before the manifest existed.
cp -an "$CURRENT_WEB/assets/." "$NEW_WEB/assets/"
fi
```
Never overwrite a new hashed file. The `.release-assets` manifest prevents compatibility files from accumulating recursively: the next release copies only the immediately previous build's original assets. The React route boundary also detects a failed dynamic import, reloads once with a short cooldown and keeps the application shell usable if recovery still fails. Verify one old route asset and the new main asset both return HTTP 200 after the switch.
## Environment
```text
@@ -71,7 +96,7 @@ ALERT_STREAM_LATENESS_SEC=120
`AMAP_WEB_JS_KEY` is served to the browser through `/app-config.js` so the same static build can be reused across environments. For production, set both `AMAP_SECURITY_JS_CODE` and `AMAP_SECURITY_SERVICE_HOST=/_AMapService`; the API service keeps the security code on the server and proxies AMap requests with `jscode` appended. Only omit `AMAP_SECURITY_SERVICE_HOST` for controlled debugging where exposing `AMAP_SECURITY_JS_CODE` to the browser is acceptable. `AMAP_API_KEY` is reserved for backend-only AMap service APIs such as geocoding, route planning, geofence, or trajectory service integration.
`PLATFORM_RELEASE` is surfaced by `/api/ops/health.runtime.platformRelease` so operators can confirm which ECS release is currently active after a deployment.
`PLATFORM_RELEASE` is surfaced by `/api/ops/health` at `data.runtime.platformRelease` so operators can confirm which ECS release is currently active after a deployment.
All three platform systemd units first load `/opt/lingniu-go-native/env/base.env` for the existing MySQL, Redis, TDengine and Kafka connection settings, then load `platform.env` for platform-specific overrides. `DATA_MODE=production` is mandatory on ECS: a missing or unreachable MySQL connection returns `DATA_STORE_UNAVAILABLE` instead of silently serving demonstration data. Use `DATA_MODE=mock` only for local development.

View File

@@ -0,0 +1,39 @@
# Frontend production-readiness audit
This document records verified risks, the production controls that address them, and the next evidence to collect. It is intentionally operational: a passing build alone is not proof that the browser application is production-ready.
## 2026-07-15: route continuity and monitor memory
### Resolved in this round
| Risk | User-visible failure | Control | Evidence gate |
| --- | --- | --- | --- |
| A stale lazy route asset disappears during release | Navigation leaves the content area blank | Preserve the previous build's original hashed assets for one generation; isolate every route with a recoverable error boundary and one-shot reload | Request an old route asset after the symlink switch; navigate through every primary route without a blank shell |
| A route is first fetched only after click | Slow transitions and a longer loading flash | Deduplicated route import cache plus pointer, focus and pointer-down preload | Route module unit tests and rendered navigation loop |
| Rapid map pans retain large query payloads | Heap growth and eventual interaction jank | Consume React Query `AbortSignal` and immediately garbage-collect inactive map viewport queries | Query-cache churn test keeps exactly one map payload after repeated viewport changes |
| Superseded monitor requests continue in the background | Wasted server traffic and stale responses | Pass request cancellation signals through the API client | API client signal test and browser network/console smoke |
| QR generation resolves after the dialog unmounts | State update after lifecycle end | Cancel the asynchronous state write on cleanup | Monitor component test |
React documents that `lazy` caches the import promise and propagates a rejected module load to the nearest Error Boundary; this is why a route-level boundary is required rather than a loading fallback alone: <https://react.dev/reference/react/lazy>. React's Suspense documentation also distinguishes a loading fallback from error handling: <https://react.dev/reference/react/Suspense>.
The navigation and progressive-loading direction follows mature observability systems: persistent global controls, collapsible sections and loading only the content needed for the current task. Elastic documents these dashboard interaction and panel-organization patterns here: <https://www.elastic.co/docs/explore-analyze/dashboards/using> and <https://www.elastic.co/docs/explore-analyze/dashboards/arrange-panels>.
### Remaining audit queue
1. Capture a Chrome heap profile across at least 50 `monitor -> history -> statistics -> tracks` cycles and inspect retained AMap objects and detached DOM nodes.
2. Bound or explicitly evict other high-cardinality query families, especially arbitrary history windows and track playback ranges.
3. Retire or migrate the legacy `App.tsx` integration suite so production V2 regressions and obsolete legacy UI expectations are reported separately.
4. Reduce the shared CSS and Semi UI payload; preserve ExcelJS as an action-only dynamic import and consider moving large workbook generation off the main thread.
5. Add an automated release smoke that reads the previous `.release-assets` manifest and asserts every listed compatibility asset returns HTTP 200.
## Release evidence template
- Commit and release identifier
- All non-legacy frontend tests
- Production V2 route tests
- TypeScript/Vite production build sizes
- Authenticated health response and `data.runtime.platformRelease`
- New main asset HTTP status
- Previous route asset HTTP status
- Browser route loop, blank-page check and console warnings/errors
- Desktop and mobile viewport screenshots