189 lines
7.9 KiB
Markdown
189 lines
7.9 KiB
Markdown
# Init Execute Async Resume Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Make `/api/v1/init/tasks/{taskId}/execute` return immediately after accepting work, then run init steps in the background and resume from the first non-`SUCCESS` step on retry.
|
|
|
|
**Architecture:** Keep the existing task table and step table as the source of truth. Split `InitService.executeTask` into an accept-and-dispatch path plus a background execution path, and use existing step statuses to determine where to resume. Add a lightweight in-process guard so one `taskId` cannot be started twice concurrently in the same application instance.
|
|
|
|
**Tech Stack:** Spring Boot service/controller layer, Java concurrency utilities, MyBatis-style repositories already present in the module, JUnit 5
|
|
|
|
---
|
|
|
|
### Task 1: Lock down current behavior with service tests
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java`
|
|
- Test: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java`
|
|
|
|
**Step 1: Write the failing tests**
|
|
|
|
Add tests that assert:
|
|
- `executeTask(taskId)` returns before a deliberately blocking executor finishes all steps
|
|
- retry skips steps already marked `SUCCESS` and resumes from the first `FAILED` step
|
|
- residual `RUNNING` steps are treated as the resume point on retry
|
|
- duplicate execute calls for the same running task do not launch a second execution
|
|
|
|
**Step 2: Run tests to verify they fail**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask test`
|
|
Expected: FAIL because execution is still synchronous and always starts from step 1.
|
|
|
|
**Step 3: Write minimal test scaffolding**
|
|
|
|
Extend the in-memory executor/repository helpers in the test file so they can:
|
|
- block on a latch or sleep
|
|
- count executed step codes
|
|
- fail a chosen step once and succeed on retry
|
|
|
|
Do not change production code in this step.
|
|
|
|
**Step 4: Run tests to verify RED is stable**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask test`
|
|
Expected: FAIL with assertion mismatches that prove the new behavior is not implemented yet.
|
|
|
|
**Step 5: Commit**
|
|
|
|
Skip commit in the current dirty workspace per user instruction.
|
|
|
|
### Task 2: Introduce async dispatch in `InitService`
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/service/InitService.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java`
|
|
|
|
**Step 1: Implement minimal async acceptance path**
|
|
|
|
Refactor `InitService` so `executeTask(taskId)`:
|
|
- loads task and steps
|
|
- returns a snapshot response quickly
|
|
- submits real work to a background executor
|
|
|
|
Use a small in-process dispatcher owned by `InitService`:
|
|
- `ExecutorService` or equivalent
|
|
- active-task map keyed by `taskId`
|
|
|
|
**Step 2: Keep the real execution logic in a dedicated method**
|
|
|
|
Create a private method such as `executeTaskInBackground(taskId)` that:
|
|
- reloads task and steps from repository
|
|
- updates statuses
|
|
- runs steps serially
|
|
- persists each step result immediately
|
|
- updates final task status
|
|
|
|
**Step 3: Run focused tests**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask test`
|
|
Expected: PASS
|
|
|
|
**Step 4: Refactor for readability**
|
|
|
|
Extract helpers only if needed:
|
|
- build response snapshot
|
|
- mark task running
|
|
- remove active-task entry in `finally`
|
|
|
|
Do not add extra features.
|
|
|
|
**Step 5: Commit**
|
|
|
|
Skip commit in the current dirty workspace per user instruction.
|
|
|
|
### Task 3: Implement resume-from-first-non-success semantics
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/service/InitService.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java`
|
|
|
|
**Step 1: Implement step selection logic**
|
|
|
|
Before running steps in the background:
|
|
- convert residual `RUNNING` steps to `FAILED`
|
|
- skip every step already marked `SUCCESS`
|
|
- start execution at the first non-`SUCCESS` step
|
|
|
|
Preserve current semantics after the resume point:
|
|
- mark current step `RUNNING`
|
|
- on success mark `SUCCESS`
|
|
- on failure mark `FAILED` and stop
|
|
|
|
**Step 2: Preserve response counters**
|
|
|
|
Ensure `successSteps` in `InitTaskExecuteResponse` reflects the current number of successful steps, including successes from earlier runs.
|
|
|
|
**Step 3: Run focused tests**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep test`
|
|
Expected: PASS
|
|
|
|
**Step 4: Run the earlier async tests again**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest#shouldAcceptTaskExecutionAsynchronously,InitServiceTest#shouldNotDispatchDuplicateBackgroundExecutionForSameTask,InitServiceTest#shouldResumeFromFirstFailedStepOnRetry,InitServiceTest#shouldResumeFromResidualRunningStep test`
|
|
Expected: PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
Skip commit in the current dirty workspace per user instruction.
|
|
|
|
### Task 4: Align controller and API-facing semantics
|
|
|
|
**Files:**
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/controller/InitInternalController.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/dto/InitTaskExecuteResponse.java`
|
|
- Modify: `/Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java`
|
|
|
|
**Step 1: Make the API comments and DTO semantics match async behavior**
|
|
|
|
Update JavaDoc/comments so they describe:
|
|
- execute endpoint only accepts and starts background work
|
|
- polling should use existing detail/step/log APIs
|
|
|
|
If needed, add a field only if tests prove it is necessary. Prefer reusing the current DTO unchanged.
|
|
|
|
**Step 2: Run focused verification**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest test`
|
|
Expected: PASS for all service tests, including the pre-existing create/query/execute cases.
|
|
|
|
**Step 3: Review the controller surface**
|
|
|
|
Check that:
|
|
- route path stays unchanged
|
|
- response wrapper stays unchanged
|
|
- no new endpoint is introduced unnecessarily
|
|
|
|
**Step 4: Commit**
|
|
|
|
Skip commit in the current dirty workspace per user instruction.
|
|
|
|
### Task 5: Final verification
|
|
|
|
**Files:**
|
|
- Verify only
|
|
|
|
**Step 1: Run init service tests**
|
|
|
|
Run: `mvn -q -Dtest=InitServiceTest test`
|
|
Expected: PASS
|
|
|
|
**Step 2: Run init executor regression tests touched by earlier behavior changes**
|
|
|
|
Run: `mvn -q -Dtest=ConfigurableInitStepExecutorTest#shouldInjectDatabaseCredentialsWhenExecutingStandardDbApplyCommand,ConfigurableInitStepExecutorTest#shouldExposeStandardInitScriptTemplates,ConfigurableInitStepExecutorTest#shouldStartStandardCmspCmtpWithConfiguredCommand test`
|
|
Expected: PASS
|
|
|
|
**Step 3: Run compile**
|
|
|
|
Run: `mvn -q -DskipTests compile`
|
|
Expected: PASS
|
|
|
|
**Step 4: Review focused diff**
|
|
|
|
Run: `git diff -- src/main/java/com/cisd/tms/modules/init/service/InitService.java src/main/java/com/cisd/tms/modules/init/controller/InitInternalController.java src/main/java/com/cisd/tms/modules/init/dto/InitTaskExecuteResponse.java src/test/java/com/cisd/tms/modules/init/service/InitServiceTest.java docs/plans/2026-03-10-init-execute-async-resume-design.md docs/plans/2026-03-10-init-execute-async-resume-plan.md`
|
|
Expected: only async-dispatch, resume logic, and related documentation changes.
|
|
|
|
**Step 5: Commit**
|
|
|
|
Skip commit in the current dirty workspace per user instruction.
|