# 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.