Skip to content

Commit 3f732be

Browse files
committed
docs: add documentation on how to handle migration uplifts and reordering
1 parent 0f9865e commit 3f732be

File tree

1 file changed

+147
-35
lines changed

1 file changed

+147
-35
lines changed

docs/developer/db-migration-checklist.md

Lines changed: 147 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,69 +10,181 @@ The developer implementing the migration is responsible for completing these ste
1010

1111
### 1. Schema and Versioning:
1212

13-
- [ ] **Bump the Database Version:** The `DB_VERSION` constant in `legacy/storage/src/main/java/com/fsck/k9/storage/StoreSchemaDefinition.java` has been incremented by exactly **1**.
14-
- [ ] **Update the database schema definition:** Any changes to the database schema have been reflected in the `dbCreateDatabaseFromScratch` within the `StoreSchemaDefinition.java` file.
15-
- [ ] **Create a New Migration Class:** A new `MigrationToXX` class within the `legacy/storage/src/main/java/com/fsck/k9/storage/migrations` folder has been created. Where XX is the new database version number.
16-
- [ ] **Migration Logic Implemented:** The new migration class contains the necessary SQL statements to transition the database from the previous version to the new version.
17-
- [ ] **Register the Migration:** The new migration class has been registered in the `Migrations.kt` file in the `legacy/storage/src/main/java/com/fsck/k9/storage/migrations` folder.
13+
- **Bump the Database Version:** The `DB_VERSION` constant in `legacy/storage/src/main/java/com/fsck/k9/storage/StoreSchemaDefinition.java` has been incremented by exactly **1**.
14+
- **Update the database schema definition:** Any changes to the database schema have been reflected in the `dbCreateDatabaseFromScratch` within the `StoreSchemaDefinition.java` file.
15+
- **Create a New Migration Class:** A new `MigrationToXX.kt` class file within the `legacy/storage/src/main/java/com/fsck/k9/storage/migrations` folder has been created, where `XX` is the new database version number.
16+
- **Migration Logic Implemented:** The new migration class contains the necessary SQL statements to transition the database from the previous version to the new version.
17+
- **Register the Migration:** The new migration class has been registered in the `Migrations.kt` file in the `legacy/storage/src/main/java/com/fsck/k9/storage/migrations` folder.
1818

1919
### 2. Migration Logic:
2020

21-
- [ ] **Data Integrity:** The migration logic has been designed to preserve existing data and ensure data integrity.
22-
- [ ] **Idempotency:** The migration can be safely re-run without causing issues or data corruption.
23-
- [ ] **Error Handling:** Appropriate error handling has been implemented to manage potential issues during the migration process.
24-
- [ ] **No Network Calls:** The migration does not make any network calls. If network calls are absolutely necessary, they are handled gracefully and do not fail the migration.
25-
- [ ] **Self-contained Logic:** The migration logic is self-contained within the migration class and does not depend on application logic outside of it.
26-
- [ ] **Performance Considerations:** Long running migrations will block the app startup (a dedicated loading screen will be shown). Consider breaking them into smaller steps if necessary.
27-
- [ ] **Documentation:** The migration class includes comments explaining the purpose of the migration and any non-obvious logic.
21+
- **Data Integrity:** The migration logic has been designed to preserve existing data and ensure data integrity.
22+
- **Idempotency:** The migration can be safely re-run without causing issues or data corruption.
23+
- **Error Handling:** Appropriate error handling has been implemented to manage potential issues during the migration process.
24+
- **No Network Calls:** The migration does not make any network calls. If network calls are absolutely necessary, they are handled gracefully and do not fail the migration.
25+
- **Self-contained Logic:** The migration logic is self-contained within the migration class and does not depend on application logic outside of it.
26+
- **Performance Considerations:** Long running migrations will block the app startup (a dedicated loading screen will be shown). Consider breaking them into smaller steps if necessary.
27+
- **Documentation:** The migration class includes comments explaining the purpose of the migration and any non-obvious logic.
2828

2929
### 3. Testing:
3030

31-
- [ ] **Unit Tests:** Unit tests for the migration have been written to cover various scenarios, including edge cases.
32-
- [ ] **Test Schema Changes:** The test validates that the schema is correct after the migration runs. It should check for:
31+
- **Unit Tests:** Unit tests for the migration have been written to cover various scenarios, including edge cases.
32+
- **Test Schema Changes:** The test validates that the schema is correct after the migration runs. It should check for:
3333
- New tables exist.
3434
- New columns exist in the correct tables.
3535
- Correct column types, nullability, and default values.
36-
- [ ] **Test Data Migration:** The test validates that existing data is correctly migrated. This includes:
36+
- **Test Data Migration:** The test validates that existing data is correctly migrated. This includes:
3737
- Data in existing tables remains intact.
3838
- Data transformations (if any) are correctly applied.
3939

4040
### 4. Holistic testing:
4141

42-
- [ ] Run migration **Beta -> Beta** and verify no issues.
43-
- [ ] Run migration **Beta -> Release** and verify no issues.
44-
- [ ] Run migration **Release -> Release** and verify no issues.
42+
- **Fresh Install:** The app installs and runs correctly on a fresh install.
43+
- **Upgrade from Production:** The app upgrades correctly from the latest production version.
44+
- **Upgrade from Beta:** The app upgrades correctly from the latest beta version.
4545

4646
## Phase 2: Review
4747

4848
The reviewer is responsible for validating these steps during the code review process.
4949

5050
### 1. Code and Logic Review:
5151

52-
- [ ] **Verify Version Bump:** Confirm that the database version has been incremented correctly by 1.
53-
- [ ] **Schema Definition Update:** Ensure that the database schema definition has been updated to reflect the new schema.
54-
- [ ] **Review Migration Class:** Ensure the new migration class is correctly named and placed in the appropriate directory.
55-
- [ ] **Validate Migration Logic:** Review the SQL statements in the migrate() method for correctness and safety.
56-
- [ ] **Check for Data Integrity:** Ensure that the migration logic preserves existing data and does not introduce data loss unless explicitly intended.
57-
- [ ] **Performance Review:** Assess the migration logic for potential performance bottlenecks, especially on large datasets.
58-
- [ ] **Review Documentation:** Check that the migration class is well-commented, explaining the purpose and any complex logic.
52+
- **Verify Version Bump:** Confirm that the database version has been incremented correctly by 1.
53+
- **Schema Definition Update:** Ensure that the database schema definition has been updated to reflect the new schema.
54+
- **Review Migration Class:** Ensure the new migration class is correctly named and placed in the appropriate directory.
55+
- **Validate Migration Logic:** Review the SQL statements in the migrate() method for correctness and safety.
56+
- **Check for Data Integrity:** Ensure that the migration logic preserves existing data and does not introduce data loss unless explicitly intended.
57+
- **Performance Review:** Assess the migration logic for potential performance bottlenecks, especially on large datasets.
58+
- **Review Documentation:** Check that the migration class is well-commented, explaining the purpose and any complex logic.
5959

6060
### 2. Testing Review:
6161

62-
- [ ] **Confirm Unit Tests:** Ensure that unit tests for the migration have been written and cover various scenarios.
63-
- [ ] **Review Test Coverage:** Validate that the tests adequately cover the schema changes and data migration paths, including edge cases.
62+
- **Confirm Unit Tests:** Ensure that unit tests for the migration have been written and cover various scenarios.
63+
- **Review Test Coverage:** Validate that the tests adequately cover the schema changes and data migration paths, including edge cases.
64+
- **Review Reordering Scenarios:** If the PR involves reordering, confirm that the developer has followed the renumbering protocol and re-verified their changes.
6465

6566
### 3. Holistic check:
6667

67-
- [ ] Run migration **Beta -> Beta** and verify no issues.
68-
- [ ] Run migration **Beta -> Release** and verify no issues.
69-
- [ ] Run migration **Release -> Release** and verify no issues.
68+
- **Fresh Install:** The app installs and runs correctly on a fresh install.
69+
- **Upgrade from Production:** The app upgrades correctly from the latest production version.
70+
- **Upgrade from Beta:** The app upgrades correctly from the latest beta version.
7071

7172
## What to watch out for:
7273

73-
- [ ] **Data Loss:** Ensure that no unintended data loss occurs during the migration.
74-
- [ ] **Network Calls:** Avoid making network calls during the migration process. If necessary, ensure they are handled gracefully and do not fail the migration.
75-
- [ ] **Write migrations that are self-contained and do not depend on application logic outside the migration class.**
76-
- [ ] **Long-running Migrations:** Be cautious of migrations that may take a long time to complete, especially on large datasets. Consider breaking them into smaller steps if necessary.
77-
- [ ] **Blocking the Main Thread:** Migrations run on the main thread and will block the UI. Keep migrations as fast as possible.
74+
- **Data Loss:** Ensure that no unintended data loss occurs during the migration.
75+
- **Network Calls:** Avoid making network calls during the migration process. If necessary, ensure they are handled gracefully and do not fail the migration.
76+
- **Merge Conflicts on Uplift:** Be prepared for merge conflicts in `StoreSchemaDefinition.java` and `Migrations.kt` when rebasing. When resolving them, ensure you are correctly renumbering your migration and not overwriting someone else's.
77+
- **Uplifting Hotfixes:** When uplifting a hotfix with a migration to a public branch (`beta`, `release`), its version number must be higher than the highest version across **all** branches (`main`, `beta`, `release`). See "Scenario B" for the detailed "jump over" strategy. Never rewrite the migration history of a public branch.
78+
- **Write migrations that are self-contained and do not depend on application logic outside the migration class.**
79+
- **Long-running Migrations:** Be cautious of migrations that may take a long time to complete, especially on large datasets. Consider breaking them into smaller steps if necessary.
80+
- **Blocking the Main Thread:** Migrations run on the main thread and will block the UI. Keep migrations as fast as possible.
81+
82+
## Handling Rebases and Uplifts
83+
84+
When working with migrations, you'll often encounter situations where the order changes. Below are two common scenarios
85+
and how to handle them.
86+
87+
### Scenario A: Rebasing a Feature Branch
88+
89+
This happens when you are about to merge your branch, but another migration has been merged into `main` in the meantime.
90+
91+
#### Step 1: Renumber Your Migration
92+
93+
- **Rebase Your Branch:** Before merging, always rebase your branch on the latest version of `main`.
94+
- **Resolve Conflicts:** If another migration has taken your version number, you'll need to renumber your migration to the next available version number.
95+
- **Rename and Update Files:**
96+
- Rename your `MigrationToXX.kt` file to `MigrationToYY.kt`, where `YY` is the new, higher version number.
97+
- Update the class name inside the file to match (e.g., `class MigrationToYY(...)`).
98+
- Update the `DB_VERSION` in `StoreSchemaDefinition.java` to `YY`.
99+
- Update the registration in `Migrations.kt` to use your new `MigrationToYY` class.
100+
101+
#### Step 2: Verify Your Renumbered Migration
102+
103+
Renumbering is changing the context of your migration, and you must verify its correctness again.
104+
105+
- **Verify Schema Definition:** Ensure that the `dbCreateDatabaseFromScratch` method in `StoreSchemaDefinition.java` reflects the correct final schema after all migrations, including your renumbered one.
106+
- **Verify Migration Logic:** Your migration will now run *after* a different one. Review your SQL logic to ensure it's still valid. For example, if you are modifying a table that the new intermediate migration also touched, you must ensure your changes don't cause conflicts.
107+
- **Re-run All Tests:** Thoroughly re-run all unit and integration tests to ensure your migration still works as expected in the new order.
108+
109+
### Scenario B: Uplifting a Hotfix to a Public Branch (`beta` or `release`)
110+
111+
This scenario is complex and requires extreme care. It occurs when a hotfix with a migration needs to be applied to
112+
`release` and/or `beta`, while newer migrations may already exist on `beta`.
113+
114+
The goal is to release a hotfix without breaking any user's upgrade path, regardless of which track (release/beta) they
115+
are on or switch to later.
116+
117+
#### What you must preserve
118+
119+
- **Always Upgrade:** A user's database version must only ever increase. No downgrades are allowed.
120+
- **Public History is Immutable:** Never rewrite the public migration history once a migration has shipped in a public build (`beta`, `release`), you **must not** renumber, remove, or alter it.
121+
- **Minimize Hotfix Migrations:** Avoid migrations in hotfixes if possible. If unavoidable, keep them minimal and fully self-contained.
122+
123+
#### A "How to uplift" guide
124+
125+
Warning: This is a delicate process and requires careful attention to detail.
126+
127+
1. Audit current versions:
128+
- `R` = latest version shipped on `release`
129+
- `B` = latest version shipped on `beta`
130+
- `M` = current version on `main` (where your hotfix branch is based)
131+
2. Pick the hotfix version:
132+
- Set the hotfix migration version `H` to `max(R, B, M) + 1` (pick +2/+3 if multiple hotfixes are needed).
133+
3. Create the hotfix migration on the target branch (`beta` or `release`):
134+
- Create `MigrationToH.kt` with your migration logic.
135+
- Update `DB_VERSION` in `StoreSchemaDefinition.java` to `H`.
136+
- Register the migration in `Migrations.kt`.
137+
- Update the definition in `dbCreateDatabaseFromScratch` to the post `H` schema.
138+
4. Patch `beta` first, then `main` immediately after:
139+
- Set `DB_VERSION` in `StoreSchemaDefinition.java` on `beta`, `main` to `H`.
140+
- Register `MigrationToH` in `Migrations.kt` on both branches. Reuse the same migration.
141+
- Verify `dbCreateDatabaseFromScratch` on both branches reflects the post-`H` schema.
142+
5. Reconcile any unreleased migrations below `H`:
143+
- For both `beta` and `main`, ensure any unreleased migrations with version `< H` are updated against the new schema after `H`.
144+
- This may involve reintroducing them as new migrations with versions `H+1`, `H+2`, etc., on both branches.
145+
- Verify `dbCreateDatabaseFromScratch` again to ensure it reflects the final schema after all migrations.
146+
147+
#### Example
148+
149+
Scenario: Uplift a hotfix migration `MigrationTo9.kt` from `main` to `beta`, while preserving existing migrations.
150+
151+
Initial state before uplift:
152+
153+
- `release` branch is at version `R=5`. The last shipped migration is `MigrationTo5.kt`
154+
- `beta` branch is at version `B=7`. This includes `MigrationTo6.kt` and `MigrationTo7.kt`.
155+
- Let's assume a beta build with version `7` has not yet been shipped. But a version `6` has been shipped to beta users.
156+
- `main` branch is at version `M=10`, it contains migrations up to `MigrationTo10.kt`.
157+
- A critical bug requires a hotfix. The necessary migration logic currently exists on main as `MigrationTo9.kt`.
158+
159+
Goal: Release the logic from `MigrationTo9.kt` as a hotfix to beta users without disrupting the unreleased `MigrationTo7.kt`.
160+
161+
Steps:
162+
163+
1. Pick hotfix version -> `H=11`.
164+
2. Create the Hotfix on a temporary branch based on `beta`:
165+
- On your new branch, create `MigrationTo11.kt`. Copy the logic from main's `MigrationTo9.kt` into this new file.
166+
- Review the logic, since it will now run after `MigrationTo7.kt`.
167+
- Update `DB_VERSION` in `StoreSchemaDefinition.java` to `11`.
168+
- Register `MigrationTo11.kt` in `Migrations.kt`.
169+
- Update `dbCreateDatabaseFromScratch` to include the schema changes from `MigrationTo7.kt` and `MigrationTo11.kt`.
170+
- Merge the hotfix to `beta`, which is now on version `11`.
171+
3. Reconcile `main`:
172+
- The `main` branch has a more complex history (`MigrationTo8.kt`, `MigrationTo9.kt`, `MigrationTo10.kt`) that must be re-evaluated now that version `11` has been introduced.
173+
- The original `MigrationTo9.kt` on main is now redundant and its version number is obsolete.
174+
- Create a PR for `main` to:
175+
1. Remove obsolete `MigrationTo9.kt`.
176+
2. Add the same `MigrationTo11.kt` file that was used on the `beta` branch and register it in `Migrations.kt`.
177+
3. Renumber and re-introduce other migrations (`MigrationTo8.kt` as `MigrationTo12.kt`, `MigrationTo10.kt` as `MigrationTo13.kt`) to ensure continuity.
178+
4. Set the final `DB_VERSION` in `StoreSchemaDefinition.java` to `13`.
179+
5. Update `dbCreateDatabaseFromScratch` to reflect the final schema after all migrations.
180+
181+
Result:
182+
183+
- `release` branch remains at version `5`.
184+
- `beta` ships version `11`.
185+
- `main` branch is now at version `13`.
186+
- All user upgrade paths are preserved:
187+
- Release users on version 5 will not be affected by this hotfix. They will only upgrade when a new version is published to the release track.
188+
- Beta users on the pre-hotfix version `6` will upgrade directly to version `11`, correctly applying migrations `7` and `11`.
189+
- The migration history is now linear and consistent across all branches, preventing future conflicts.
78190

0 commit comments

Comments
 (0)