Files
ruoyi-vue-pro/docs/education/migration/issues/EDU-005-flyway-takeover-decision.md

161 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# EDU-005 — Decide PostgreSQL/Flyway takeover strategy
- **Status:** done — forward-only takeover and EDU-006 version plan accepted
- **Type:** decision
- **Phase:** 1 / Phase 2 prerequisite
- **Blockers:** EDU-002
## Decision outcome
Education schema ownership moves exclusively to module-owned PostgreSQL Flyway migrations under `yudao-module-education/src/main/resources/db/migration/education/`. The existing root `sql/postgresql/education/` files are classified as manual bootstrap/design history, not Flyway history. The MySQL files are obsolete archival artifacts and must not remain in operational instructions.
`V4010__initialize_education_flyway.sql` and `V4020__create_native_catalog.sql` are uncommitted working-tree resources in this checkout, and the inspected local disposable PostgreSQL database has no `flyway_schema_history` table and no Education tables. This proves neither migration ran in that database, but it does not prove they never ran in another environment. To avoid assigning a second meaning to a potentially distributed version, EDU-006 must preserve both files byte-for-byte and allocate new work from `V4030`.
No migration or production schema change was executed by EDU-005.
## Evidence and classification
| Artifact | Verified state | Classification | Forward disposition |
|---|---|---|---|
| `V4010__initialize_education_flyway.sql` | Untracked module resource; contains only `SELECT 1`; packaged in current `target/classes` | Potentially distributed Flyway history; execution unverified | Freeze byte-for-byte; do not repurpose |
| `V4020__create_native_catalog.sql` | Untracked module resource; owns 11 native catalog tables; differs materially from manual `008` | Potentially distributed Flyway history; execution unverified | Freeze byte-for-byte; do not replace with manual `008` |
| `sql/postgresql/education/002``005`, `007`, `009` | Untracked manual scripts implementing the current Practice/report/wrong/favorite/unified-idempotency model in stages | Manual bootstrap/design history, not active Flyway | Consolidate the approved final state into higher Flyway versions; do not copy the obsolete intermediate tables as fresh schema |
| `sql/postgresql/education/008` | Manual native-catalog design predating/diverging from V4020 | Superseded manual design | V4020 remains the only intended Flyway owner of native catalog schema |
| `sql/postgresql/education/000``001` | Placeholder schema plus menu/tenant seed scripts | Manual bootstrap/seed history | Do not create a separate `education` schema; evaluate the still-used `education:capability` menu seed separately |
| `sql/mysql/education/**` | Historical MySQL schema, seeds, and rollback scripts | Obsolete archive | Remove from runbooks; retain only if explicitly labeled non-operational archive |
| `src/test/resources/sql/postgresql/create_tables.sql` | EDU-016 disposable test bridge; 140 PostgreSQL persistence tests passed against it | Temporary test fixture | Replace with Flyway-driven test setup after EDU-006 proves equivalent schema |
| Docker init mounts for manual Education SQL | Dirty Docker configuration mounts `000``009` directly | Obsolete delivery path | Remove Education manual mounts after Flyway takeover; the server owns migration execution |
## Approved schema owner map
### V4020 owner — unchanged
V4020 exclusively owns the native catalog tables:
- `education_region`, `education_school`, `education_major`, `education_subject`, `education_category`;
- `education_content_entry`, `education_content_node`;
- `education_question_collection`, `education_question`, `education_practice_blueprint`;
- `education_question_collection_question`.
EDU-006 does not fold Practice schema into V4020 and does not silently substitute manual `008`.
### EDU-006 new owner — Practice final state
The new Practice migration owns the final runtime shape of:
- `education_practice_session` and `education_practice_question`;
- `education_practice_report` and `education_practice_report_detail`;
- `education_wrong_question` and `education_wrong_question_idempotency`;
- `education_favorite`;
- `education_idempotency`.
The approved fresh schema does **not** create `education_answer_idempotency` or `education_submit_idempotency`. Current production services use `IdempotencyStoreMapper` and `education_idempotency`; the old DOs/Mappers are unused compatibility residue and must be removed or explicitly isolated during EDU-006.
The migration must include all columns already required by runtime code and the proven EDU-016 bridge, including `client_sequence`, `last_client_sequence`, `review_fingerprint`, protected answer snapshots, report content snapshots, JSONB fields, tenant IDs, logical-delete fields, and observed conflict/query indexes.
## Version plan for EDU-006
Version numbers are project-wide. With V4020 frozen, the next allocated version is:
1. **V4030 — Practice core-loop final schema and adoption**
- Create the final tables, columns, constraints, and indexes listed above.
- Be adoption-aware for databases that contain manually bootstrapped Practice tables.
- Validate existing column types and required uniqueness before treating existing objects as compatible; fail closed on incompatible shapes rather than silently accepting them.
- Backfill the unified idempotency table from legacy answer/submit tables when those tables exist.
- Preserve old idempotency tables during the initial adoption migration; do not make data destruction a prerequisite for application rollout.
2. **V4040 — deterministic Education capability seed, only if still approved**
- Seed menu IDs `6800`/`6801` idempotently if the existing `EducationCapabilityController` remains an exposed administrator capability.
- Keep role assignment outside the migration.
- If the capability endpoint/menu is retired before EDU-006, omit this migration rather than seeding dead UI.
3. **Later forward cleanup migration**
- Drop legacy `education_answer_idempotency` and `education_submit_idempotency` only after every adopted environment has verified backfill counts, the application no longer contains active references, and a separately reviewed forward cleanup is approved.
If repository-wide migration inventory changes before implementation, EDU-006 must re-run the version scan and use the next unused project-wide version instead of blindly taking V4030/V4040.
## Existing-environment takeover classes
`baseline-on-migrate=true` with baseline `4009` is an adoption aid, not proof that Education objects match Flyway.
1. **Empty or platform-only database, no Education tables**
- Use baseline `4009` only when the non-empty platform schema requires adoption.
- Run V4010, V4020, then V4030+ normally.
2. **Manual Practice tables exist, native catalog tables do not**
- Baseline `4009` may be used.
- V4020 creates catalog objects; V4030 validates/adopts Practice objects and performs required backfills.
3. **Manual catalog tables equivalent to V4020 already exist, no Flyway history**
- Do not run V4020 into colliding tables.
- First compare the actual schema with the frozen V4020 contract.
- For a verified equivalent environment, use a one-time environment-specific baseline at `4020`, then run V4030+. This records adoption, not execution of V4020, and must be documented per environment.
- If the schema is not equivalent, correct it through an explicit higher-version adoption path; do not falsify history or edit V4020.
4. **Flyway history already contains V4010 and/or V4020**
- Compare script/checksum/success with the frozen resources.
- Never edit an executed script. Any mismatch or failed row blocks rollout until an environment-specific repair decision is reviewed.
5. **Unknown shared environment**
- No migration rollout is authorized until its Education tables and `flyway_schema_history` are inventoried.
After all existing environments carry an explicit baseline/history record, changing `baseline-on-migrate` to `false` is a separate reviewed configuration ticket. `validate-on-migrate=true`, `clean-disabled=true`, and `out-of-order=false` remain mandatory.
## Data compatibility and backfill rules
- Copy legacy answer and submit idempotency rows into `education_idempotency` with deterministic operation values and `ON CONFLICT ... DO NOTHING` only after verifying duplicate-key/request-hash compatibility.
- Preserve original IDs only if required by references; otherwise allow identity allocation and verify semantic row counts by operation.
- Existing Practice tables must be compared with the final DO/Mapper contract, not merely checked for table-name existence.
- JSON snapshot fields use PostgreSQL `JSONB` where current runtime/test behavior expects JSONB normalization.
- Tenant-scoped uniqueness includes `tenant_id` where the business key is tenant-local. `education_practice_report` uses `(tenant_id, session_id)` as the final unique report key.
- Do not copy Supabase RLS. Framework tenant isolation remains primary; database constraints enforce integrity and idempotency.
- No destructive rollback SQL is delivered. Recovery is application rollback plus a higher-version forward correction.
## Documentation and operational corrections
EDU-006 must update operational documentation in the same slice:
- replace `yudao-module-education/README.md` MySQL apply/rollback commands with Flyway/PostgreSQL forward-only instructions;
- remove the manual Education SQL mounts from `script/docker/docker-compose.yml` so a fresh Docker database is not initialized outside Flyway before server startup;
- label `sql/postgresql/education/` and `sql/mysql/education/` as non-operational history or move them to an explicitly archival location without rewriting history;
- replace the EDU-016 temporary PostgreSQL schema bridge with Flyway-driven setup after equivalence is proven;
- state per environment whether Flyway was actually run, validated, or only packaged/compiled.
## Required EDU-006 verification
Static and build gates:
```bash
git diff --check
mvn -pl yudao-module-education -am -DskipTests clean package
find yudao-module-education/target/classes/db/migration/education -type f -print
mvn -pl yudao-server -am -DskipTests clean compile
```
Real disposable PostgreSQL gate:
1. initialize a disposable platform database or approved baseline fixture;
2. run Flyway migrate using the server's exact migration locations and PostgreSQL driver;
3. run Flyway validate;
4. inspect `flyway_schema_history` with version, script, checksum, and success;
5. inspect all approved tables, columns, constraints, indexes, and backfill counts;
6. run the EDU-016 PostgreSQL persistence suite against the migrated schema;
7. test at least the empty/platform-only path and one representative manually bootstrapped adoption path.
Only these real successful executions may be reported as migration success.
## Acceptance criteria
- [x] No published or potentially distributed migration is authorized for editing.
- [x] Every required table/index/constraint/seed has one intended Flyway owner and version range.
- [x] Manual SQL is not silently treated as executed history.
- [x] The plan includes migration packaging and real PostgreSQL execution evidence requirements.
- [x] Documentation correction scope is explicit.
## Verification performed by EDU-005
- Read project Flyway rules, local/dev Flyway configuration, server dependencies, all active migration locations, manual PostgreSQL/MySQL artifacts, core-loop DOs/Mappers, PostgreSQL test bridge, Docker initialization, Git history, and dirty-tree state.
- Inspected the reachable disposable `postgresdb` container. Target identity was database `postgres`, user `postgres`, schema `public`; it contained no `flyway_schema_history` relation and no Education tables. Container startup logs state that `/docker-entrypoint-initdb.d/*` was ignored because the volume was already initialized.
- Confirmed V4010/V4020 are currently packaged under `target/classes/db/migration/education/` from a prior build.
- Did not modify migration SQL, application code, server configuration, Docker configuration, or any database object.
- Did not run Flyway migrate/validate and does not claim a successful database migration.
## Risk and rollback
- **Risk:** High. Existing manually initialized databases may have partially overlapping or divergent table shapes, and a false baseline can hide incompatibility.
- **Rollback:** No rollback is required for this decision-only ticket. EDU-006 uses forward migrations, preserves legacy idempotency tables during first adoption, and supports application rollback without `flyway clean` or destructive down scripts.