78 lines
4.5 KiB
Markdown
78 lines
4.5 KiB
Markdown
# Backend Restart and Health Check Design
|
|
|
|
## Goal
|
|
|
|
Make backend deployment deterministic on Windows: replace any process listening on the configured backend port, prevent duplicate GUI deployments, keep the launched Java process alive independently of the deployment window, and report success only after the new backend is actually listening.
|
|
|
|
## Confirmed Behavior
|
|
|
|
- Before starting the backend, find every process listening on the configured backend port.
|
|
- Terminate each listener and its child process tree, including listeners not created by this tool.
|
|
- Log every PID that is terminated and fail the deployment if the port cannot be released.
|
|
- Do not allow another deployment to start while one is already running.
|
|
- Report exit code `0` only after the new process remains alive and the configured port accepts TCP connections.
|
|
- If the process exits early or startup times out, report failure and retain the backend log for diagnosis.
|
|
|
|
## Architecture
|
|
|
|
Keep process orchestration in `deploy_tool/deployer.py` and GUI state in `deploy_tool/gui.py`. Use only the Python standard library and Windows commands already available on the target machine; do not add a package dependency.
|
|
|
|
`deployer.py` will contain small helpers with focused responsibilities:
|
|
|
|
- Extract the configured server port from the Java command.
|
|
- Parse Windows `netstat -ano -p tcp` output to identify listening PIDs.
|
|
- Terminate a PID tree with `taskkill /PID <pid> /T /F`.
|
|
- Poll the port until it is released before launch.
|
|
- Poll the child process and port during startup.
|
|
- Build Windows creation flags that detach the Java process from the GUI console.
|
|
|
|
The existing deployment runner contract remains unchanged: each step returns an integer exit code. A background backend step may wait for readiness, but after readiness it returns while the detached Java process continues running.
|
|
|
|
## Deployment Flow
|
|
|
|
1. Resolve the backend JAR and configured port as today.
|
|
2. Query listeners for that port.
|
|
3. Log and terminate each listener process tree.
|
|
4. Wait up to 10 seconds for the port to become free. Failure to release the port stops deployment.
|
|
5. Append a clear deployment separator to `backend.log` and start Java with stdout/stderr redirected to that log.
|
|
6. On Windows, use `CREATE_NEW_PROCESS_GROUP` and `DETACHED_PROCESS` so closing the GUI does not close the backend console process.
|
|
7. For up to 60 seconds, poll both `process.poll()` and a TCP connection to `127.0.0.1:<port>`.
|
|
8. If the process exits before listening, return its non-zero code, or `1` if it exited with code `0` without becoming ready.
|
|
9. If the port begins accepting connections while the child is alive, log the PID and return `0`.
|
|
10. If startup times out, terminate the new process tree and return `1`.
|
|
|
|
## GUI Behavior
|
|
|
|
Both visible deployment buttons will be retained, but both refer to the same deployment-running state.
|
|
|
|
- After confirmation and before starting the worker thread, set `deployment_running = True` and disable both buttons.
|
|
- If deployment is already running, ignore another invocation and add an explanatory log message.
|
|
- When the `deploy_done` event is handled, clear the flag, restore both buttons, and display the real exit code.
|
|
|
|
This state transition occurs on the Tk main thread, so rapid clicks cannot enqueue multiple deployment workers.
|
|
|
|
## Error Handling and Logs
|
|
|
|
- Failure to enumerate listeners, terminate a listener, release the port, create the Java process, or reach readiness produces a non-zero deployment result.
|
|
- Log messages identify the affected port and PID without hiding the original backend log.
|
|
- Historical `backend.log` content is preserved; a timestamped separator distinguishes runs.
|
|
- The existing MyBatis warning is not treated as a startup failure because readiness is based on process state and TCP availability.
|
|
|
|
## Testing
|
|
|
|
Use `unittest` and dependency injection or pure helpers so tests do not terminate real processes or occupy real user ports.
|
|
|
|
Regression coverage will include:
|
|
|
|
- Parsing IPv4 and IPv6 listener rows while ignoring unrelated ports and states.
|
|
- Terminating all listener PIDs before spawning Java.
|
|
- Returning failure when the old listener cannot be stopped.
|
|
- Returning failure when Java exits before opening the port.
|
|
- Returning failure and cleaning up when startup times out.
|
|
- Returning success only when Java is alive and the configured port becomes reachable.
|
|
- Applying Windows detached-process flags.
|
|
- Ignoring a second GUI deployment while one is active.
|
|
- Disabling and restoring both GUI deployment buttons around a deployment.
|
|
|
|
The full existing test suite must continue to pass.
|