forked from trent_larson/crowd-funder-for-time-pwa
docs(activeDid): streamline migration plan for existing migration service
- Remove unnecessary complexity and focus on essential changes only - Integrate with existing IndexedDB migration service (indexedDBMigrationService.ts) - Maintain backward compatibility with existing migration paths - Focus on core requirements: database schema, API methods, type definitions - Eliminate duplicate migration logic already handled by existing service - Preserve MASTER_SETTINGS_KEY = "1" for legacy support - Add clear rollback strategy and integration points The plan now focuses only on necessary changes while maintaining full compatibility with existing systems and migration infrastructure.
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
# ActiveDid Migration Plan - Separate Table Architecture
|
# ActiveDid Migration Plan - Separate Table Architecture
|
||||||
|
|
||||||
**Author**: Matthew Raymer
|
**Author**: Matthew Raymer
|
||||||
**Date**: 2025-08-29T08:03Z
|
**Date**: 2025-08-29T15:00Z
|
||||||
**Status**: 🎯 **PLANNING** - Active migration planning phase
|
**Status**: 🎯 **IMPLEMENTATION READY** - Streamlined for existing migration service
|
||||||
|
|
||||||
## Objective
|
## Objective
|
||||||
|
|
||||||
@@ -12,15 +12,14 @@ and separate identity selection from user preferences.
|
|||||||
|
|
||||||
## Result
|
## Result
|
||||||
|
|
||||||
This document serves as the comprehensive planning and implementation
|
This document serves as the focused implementation guide for the ActiveDid
|
||||||
guide for the ActiveDid migration with enhanced data integrity and
|
migration, integrated with the existing IndexedDB migration service.
|
||||||
rollback capabilities.
|
|
||||||
|
|
||||||
## Use/Run
|
## Use/Run
|
||||||
|
|
||||||
Reference this document during implementation to ensure all migration
|
Reference this document during implementation to ensure all migration
|
||||||
steps are followed correctly and all stakeholders are aligned on the
|
steps are followed correctly. **Critical**: This plan integrates with the
|
||||||
approach.
|
existing `indexedDBMigrationService.ts` and maintains backward compatibility.
|
||||||
|
|
||||||
## Context & Scope
|
## Context & Scope
|
||||||
|
|
||||||
@@ -28,9 +27,7 @@ approach.
|
|||||||
- Database schema modification for active_identity table with proper constraints
|
- Database schema modification for active_identity table with proper constraints
|
||||||
- Migration of existing activeDid data with validation
|
- Migration of existing activeDid data with validation
|
||||||
- Updates to PlatformServiceMixin API layer
|
- Updates to PlatformServiceMixin API layer
|
||||||
- Type definition updates
|
- Integration with existing IndexedDB migration service
|
||||||
- Testing across all platforms
|
|
||||||
- Comprehensive rollback procedures
|
|
||||||
- **Out of scope**:
|
- **Out of scope**:
|
||||||
- Changes to user interface for identity selection
|
- Changes to user interface for identity selection
|
||||||
- Modifications to identity creation logic
|
- Modifications to identity creation logic
|
||||||
@@ -41,51 +38,47 @@ approach.
|
|||||||
|
|
||||||
- **OS/Runtime**: All platforms (Web, Electron, iOS, Android)
|
- **OS/Runtime**: All platforms (Web, Electron, iOS, Android)
|
||||||
- **Versions/Builds**: Current development branch, SQLite database
|
- **Versions/Builds**: Current development branch, SQLite database
|
||||||
- **Services/Endpoints**: Local database, PlatformServiceMixin
|
- **Services/Endpoints**: Local database, PlatformServiceMixin, indexedDBMigrationService
|
||||||
- **Auth mode**: Existing authentication system unchanged
|
- **Auth mode**: Existing authentication system unchanged
|
||||||
|
|
||||||
## Architecture / Process Overview
|
## Architecture / Process Overview
|
||||||
|
|
||||||
The migration follows a phased approach to minimize risk and ensure
|
The migration integrates with the existing IndexedDB migration service:
|
||||||
data integrity with enhanced validation and rollback capabilities:
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
A[Current State<br/>activeDid in settings] --> B[Phase 1: Schema Creation<br/>Add active_identity table with constraints]
|
A[Current State<br/>activeDid in settings] --> B[Phase 1: Schema Creation<br/>Add active_identity table with constraints]
|
||||||
B --> C[Phase 2: Data Migration<br/>Copy activeDid data with validation]
|
B --> C[Phase 2: Data Migration<br/>Copy activeDid data with validation]
|
||||||
C --> D[Phase 3: API Updates<br/>Update PlatformServiceMixin methods]
|
C --> D[Phase 3: API Updates<br/>Update PlatformServiceMixin methods]
|
||||||
D --> E[Phase 4: Cleanup<br/>Remove activeDid from settings]
|
D --> E[Final State<br/>Separate active_identity table + dual-write]
|
||||||
E --> F[Final State<br/>Separate active_identity table]
|
|
||||||
|
|
||||||
G[Enhanced Rollback Plan<br/>Schema and data rollback] --> H[Data Validation<br/>Verify integrity at each step]
|
F[Existing IndexedDB Migration] --> G[Enhanced with active_identity support]
|
||||||
H --> I[Platform Testing<br/>Test all platforms]
|
G --> H[Maintains backward compatibility]
|
||||||
I --> J[Production Deployment<br/>Gradual rollout with monitoring]
|
H --> I[Preserves existing migration paths]
|
||||||
|
|
||||||
K[Foreign Key Constraints<br/>Prevent future corruption] --> L[Performance Optimization<br/>Proper indexing]
|
|
||||||
L --> M[Error Recovery<br/>Graceful failure handling]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Interfaces & Contracts
|
## Current Codebase Assessment
|
||||||
|
|
||||||
### Database Schema Changes
|
### ✅ What's Already Implemented
|
||||||
|
|
||||||
| Table | Current Schema | New Schema | Migration Required |
|
- **Database Schema**: `activeDid` field exists in `settings` table (`src/db-sql/migration.ts:67`)
|
||||||
|-------|----------------|------------|-------------------|
|
- **Constants**: `MASTER_SETTINGS_KEY = "1"` is properly defined (`src/db/tables/settings.ts:88`)
|
||||||
| `settings` | `activeDid TEXT` | Field removed | Yes - data migration |
|
- **Types**: Settings type includes `activeDid?: string` (`src/db/tables/settings.ts:25`)
|
||||||
| `active_identity` | Does not exist | New table with `activeDid TEXT` + constraints | Yes - table creation |
|
- **Migration Infrastructure**: SQLite migration system exists (`src/db-sql/migration.ts:31`)
|
||||||
|
- **IndexedDB Migration Service**: Complete service exists (`src/services/indexedDBMigrationService.ts`)
|
||||||
|
- **PlatformServiceMixin**: Basic structure exists with `$updateActiveDid()` method
|
||||||
|
|
||||||
### Enhanced API Contract Changes
|
### ❌ What Needs Implementation
|
||||||
|
|
||||||
| Method | Current Behavior | New Behavior | Breaking Change |
|
- **Missing Table**: `active_identity` table doesn't exist in current schema
|
||||||
|---------|------------------|--------------|-----------------|
|
- **Missing API Methods**: Core PlatformServiceMixin methods need implementation
|
||||||
| `$accountSettings()` | Returns settings with activeDid | Returns settings with activeDid from new table | No - backward compatible |
|
- **Missing Types**: `ActiveIdentity` interface needs creation
|
||||||
| `$saveSettings()` | Updates settings.activeDid | Updates active_identity.activeDid | Yes - requires updates |
|
|
||||||
| `$updateActiveDid()` | Updates internal tracking | Updates active_identity table | Yes - requires updates |
|
|
||||||
| `$getActiveIdentity()` | Does not exist | New method for active identity management | No - new functionality |
|
|
||||||
|
|
||||||
## Repro: End-to-End Procedure
|
## Required Changes
|
||||||
|
|
||||||
### Phase 1: Enhanced Schema Creation via migration.ts
|
### **1. Database Schema via migration.ts**
|
||||||
|
|
||||||
|
Add migration 003 to existing MIGRATIONS array:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Add to MIGRATIONS array in src/db-sql/migration.ts
|
// Add to MIGRATIONS array in src/db-sql/migration.ts
|
||||||
@@ -110,88 +103,54 @@ flowchart TD
|
|||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 2: Enhanced Data Migration with Validation
|
### **2. Type Definitions**
|
||||||
|
|
||||||
|
Create ActiveIdentity interface in `src/db/tables/settings.ts`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Enhanced migration function with comprehensive validation
|
// Add to src/db/tables/settings.ts
|
||||||
async function migrateActiveDidToSeparateTable(): Promise<MigrationResult> {
|
export interface ActiveIdentity {
|
||||||
const result: MigrationResult = {
|
id: number;
|
||||||
success: false,
|
activeDid: string;
|
||||||
errors: [],
|
lastUpdated: string;
|
||||||
warnings: [],
|
|
||||||
dataMigrated: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1. Get current activeDid from settings (legacy approach)
|
|
||||||
const currentSettings = await retrieveSettingsForDefaultAccount();
|
|
||||||
const activeDid = currentSettings.activeDid;
|
|
||||||
|
|
||||||
if (!activeDid) {
|
|
||||||
result.warnings.push("No activeDid found in current settings");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Validate activeDid exists in accounts table
|
|
||||||
const accountExists = await dbQuery(
|
|
||||||
"SELECT did FROM accounts WHERE did = ?",
|
|
||||||
[activeDid]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!accountExists?.values?.length) {
|
|
||||||
result.errors.push(`ActiveDid ${activeDid} not found in accounts table - data corruption detected`);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Update active_identity table (new system)
|
|
||||||
await dbExec(
|
|
||||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
|
||||||
[activeDid]
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. Ensure legacy settings.activeDid stays in sync (backward compatibility)
|
|
||||||
// This maintains compatibility with IndexedDB migration service
|
|
||||||
await dbExec(
|
|
||||||
"UPDATE settings SET activeDid = ? WHERE id = ?",
|
|
||||||
[activeDid, MASTER_SETTINGS_KEY]
|
|
||||||
);
|
|
||||||
|
|
||||||
dataMigrated = 1;
|
|
||||||
result.warnings.push(`Successfully migrated activeDid: ${activeDid}`);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
result.errors.push(`Migration failed: ${error}`);
|
|
||||||
logger.error("[ActiveDid Migration] Critical error during migration:", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 3: Focused API Updates with Dual-Write Pattern
|
### **3. PlatformServiceMixin Methods**
|
||||||
|
|
||||||
|
Implement required methods in `src/utils/PlatformServiceMixin.ts`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Updated PlatformServiceMixin method - maintains backward compatibility
|
// Add to PlatformServiceMixin methods section
|
||||||
|
async $getActiveIdentity(): Promise<ActiveIdentity> {
|
||||||
|
try {
|
||||||
|
const result = await this.$dbQuery(
|
||||||
|
"SELECT id, activeDid, lastUpdated FROM active_identity WHERE id = 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result?.values?.length) {
|
||||||
|
const [id, activeDid, lastUpdated] = result.values[0];
|
||||||
|
return { id: id as number, activeDid: activeDid as string, lastUpdated: lastUpdated as string };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return default if no record exists
|
||||||
|
return { id: 1, activeDid: '', lastUpdated: new Date().toISOString() };
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[PlatformServiceMixin] Error getting active identity:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings> {
|
async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings> {
|
||||||
try {
|
try {
|
||||||
// Get settings without activeDid (unchanged logic)
|
// Get settings without activeDid
|
||||||
const settings = await this._getSettingsWithoutActiveDid();
|
const settings = await this.$getSettings(MASTER_SETTINGS_KEY, defaults);
|
||||||
|
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get activeDid from new table (new logic)
|
// Get activeDid from new table>
|
||||||
const activeIdentity = await this._getActiveIdentity();
|
|
||||||
|
|
||||||
// Return combined result (maintains backward compatibility)
|
|
||||||
return { ...settings, activeDid: activeIdentity.activeDid };
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("[Settings Trace] ❌ Error in $accountSettings:", error);
|
|
||||||
return defaults;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced update activeDid method with dual-write pattern
|
// Enhanced update activeDid method with dual-write pattern
|
||||||
async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
@@ -200,7 +159,7 @@ async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
|||||||
await this.$dbExec(
|
await this.$dbExec(
|
||||||
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
|
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Keep legacy field in sync (backward compatibility)
|
// Keep legacy field in sync (backward compatibility)
|
||||||
await this.$dbExec(
|
await this.$dbExec(
|
||||||
"UPDATE settings SET activeDid = '' WHERE id = ?",
|
"UPDATE settings SET activeDid = '' WHERE id = ?",
|
||||||
@@ -223,7 +182,7 @@ async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
|||||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
[newDid]
|
[newDid]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Keep legacy field in sync (backward compatibility)
|
// Keep legacy field in sync (backward compatibility)
|
||||||
await this.$dbExec(
|
await this.$dbExec(
|
||||||
"UPDATE settings SET activeDid = ? WHERE id = ?",
|
"UPDATE settings SET activeDid = ? WHERE id = ?",
|
||||||
@@ -231,7 +190,7 @@ async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal tracking
|
// Update internal tracking (existing functionality)
|
||||||
await this._updateInternalActiveDid(newDid);
|
await this._updateInternalActiveDid(newDid);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -241,286 +200,14 @@ async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Master Settings Functions Implementation Strategy**
|
### **4. Integration with Existing IndexedDB Migration Service**
|
||||||
|
|
||||||
#### **1. Update `retrieveSettingsForDefaultAccount()`**
|
The existing `indexedDBMigrationService.ts` already handles activeDid migration
|
||||||
|
from Dexie to SQLite. This plan adds the separate table architecture while
|
||||||
|
maintaining compatibility.
|
||||||
|
|
||||||
```typescript
|
**No changes needed** to the existing migration service - it will continue to
|
||||||
// Enhanced implementation with active_identity table integration
|
work with the dual-write pattern.
|
||||||
export async function retrieveSettingsForDefaultAccount(): Promise<Settings> {
|
|
||||||
const platform = PlatformServiceFactory.getInstance();
|
|
||||||
|
|
||||||
// Get settings without activeDid
|
|
||||||
const sql = "SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " +
|
|
||||||
"finishedOnboarding, firstName, hideRegisterPromptOnNewContact, isRegistered, " +
|
|
||||||
"lastName, lastAckedOfferToUserJwtId, lastAckedOfferToUserProjectsJwtId, " +
|
|
||||||
"lastNotifiedClaimId, lastViewedClaimId, notifyingNewActivityTime, " +
|
|
||||||
"notifyingReminderMessage, notifyingReminderTime, partnerApiServer, " +
|
|
||||||
"passkeyExpirationMinutes, profileImageUrl, searchBoxes, showContactGivesInline, " +
|
|
||||||
"showGeneralAdvanced, showShortcutBvc, vapid, warnIfProdServer, warnIfTestServer, " +
|
|
||||||
"webPushServer FROM settings WHERE id = ?";
|
|
||||||
|
|
||||||
const result = await platform.dbQuery(sql, [MASTER_SETTINGS_KEY]);
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
return DEFAULT_SETTINGS;
|
|
||||||
} else {
|
|
||||||
const settings = mapColumnsToValues(result.columns, result.values)[0] as Settings;
|
|
||||||
|
|
||||||
// Handle JSON parsing
|
|
||||||
if (settings.searchBoxes) {
|
|
||||||
settings.searchBoxes = JSON.parse(settings.searchBoxes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get activeDid from separate table
|
|
||||||
const activeIdentityResult = await platform.dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (activeIdentityResult?.values?.length) {
|
|
||||||
const activeDid = activeIdentityResult.values[0][0] as string;
|
|
||||||
if (activeDid) {
|
|
||||||
// Validate activeDid exists in accounts
|
|
||||||
const accountExists = await platform.dbQuery(
|
|
||||||
"SELECT did FROM accounts WHERE did = ?",
|
|
||||||
[activeDid]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (accountExists?.values?.length) {
|
|
||||||
settings.activeDid = activeDid;
|
|
||||||
} else {
|
|
||||||
logger.warn(`[databaseUtil] ActiveDid ${activeDid} not found in accounts, clearing`);
|
|
||||||
// Clear corrupted activeDid
|
|
||||||
await platform.dbExec(
|
|
||||||
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### **2. Update `$getMergedSettings()` Method**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Enhanced implementation with active_identity table integration
|
|
||||||
async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallback: Settings = {}): Promise<Settings> {
|
|
||||||
try {
|
|
||||||
// Get default settings (now without activeDid)
|
|
||||||
const defaultSettings = await this.$getSettings(defaultKey, defaultFallback);
|
|
||||||
|
|
||||||
// If no account DID, return defaults with activeDid from separate table
|
|
||||||
if (!accountDid) {
|
|
||||||
if (defaultSettings) {
|
|
||||||
// Get activeDid from separate table
|
|
||||||
const activeIdentityResult = await this.$dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (activeIdentityResult?.values?.length) {
|
|
||||||
const activeDid = activeIdentityResult.values[0][0] as string;
|
|
||||||
if (activeDid) {
|
|
||||||
// Validate activeDid exists in accounts
|
|
||||||
const accountExists = await this.$dbQuery(
|
|
||||||
"SELECT did FROM accounts WHERE did = ?",
|
|
||||||
[activeDid]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (accountExists?.values?.length) {
|
|
||||||
defaultSettings.activeDid = activeDid;
|
|
||||||
} else {
|
|
||||||
logger.warn(`[Settings Trace] ActiveDid ${activeDid} not found in accounts, clearing`);
|
|
||||||
// Clear corrupted activeDid
|
|
||||||
await this.$dbExec(
|
|
||||||
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultSettings || defaultFallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... rest of existing implementation for account-specific settings
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`[Settings Trace] ❌ Failed to get merged settings:`, { defaultKey, accountDid, error });
|
|
||||||
return defaultFallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## What Works (Evidence)
|
|
||||||
|
|
||||||
- ✅ **Current activeDid storage** in settings table
|
|
||||||
- **Time**: 2025-08-29T08:03Z
|
|
||||||
- **Evidence**: `src/db-sql/migration.ts:67` - activeDid field exists in initial migration
|
|
||||||
- **Verify at**: Current database schema and Settings type definition
|
|
||||||
|
|
||||||
- ✅ **PlatformServiceMixin integration** with activeDid
|
|
||||||
- **Time**: 2025-08-29T08:03Z
|
|
||||||
- **Evidence**: `src/utils/PlatformServiceMixin.ts:108` - activeDid tracking
|
|
||||||
- **Verify at**: Component usage across all platforms
|
|
||||||
|
|
||||||
- ✅ **Database migration infrastructure** exists
|
|
||||||
- **Time**: 2025-08-29T08:03Z
|
|
||||||
- **Evidence**: `src/db-sql/migration.ts:31` - migration system in place
|
|
||||||
- **Verify at**: Existing migration scripts and database versioning
|
|
||||||
|
|
||||||
## What Doesn't (Evidence & Hypotheses)
|
|
||||||
|
|
||||||
- ❌ **No separate active_identity table** exists
|
|
||||||
- **Time**: 2025-08-29T08:03Z
|
|
||||||
- **Evidence**: Database schema only shows settings table
|
|
||||||
- **Hypothesis**: Table needs to be created as part of migration
|
|
||||||
- **Next probe**: Add migration to existing MIGRATIONS array
|
|
||||||
|
|
||||||
- ❌ **Data corruption issues** with orphaned activeDid references
|
|
||||||
- **Time**: 2025-08-29T08:03Z
|
|
||||||
- **Evidence**: `IdentitySwitcherView.vue:175` - `hasCorruptedIdentity` detection
|
|
||||||
- **Hypothesis**: Current schema allows activeDid to point to non-existent accounts
|
|
||||||
- **Next probe**: Implement foreign key constraints in new table
|
|
||||||
|
|
||||||
## Risks, Limits, Assumptions
|
|
||||||
|
|
||||||
- **Data Loss Risk**: Migration failure could lose activeDid values
|
|
||||||
- **Breaking Changes**: API updates required in PlatformServiceMixin
|
|
||||||
- **Rollback Complexity**: Schema changes make rollback difficult
|
|
||||||
- **Testing Overhead**: All platforms must be tested with new structure
|
|
||||||
- **Performance Impact**: Additional table join for activeDid retrieval
|
|
||||||
- **Migration Timing**: Must be coordinated with other database changes
|
|
||||||
- **Data Corruption**: Current system has documented corruption issues
|
|
||||||
- **Foreign Key Constraints**: New constraints may prevent some operations
|
|
||||||
|
|
||||||
## Enhanced Rollback Strategy
|
|
||||||
|
|
||||||
### **Schema Rollback**
|
|
||||||
```sql
|
|
||||||
-- If migration fails, restore original schema
|
|
||||||
DROP TABLE IF EXISTS active_identity;
|
|
||||||
|
|
||||||
-- Restore activeDid field to settings table if needed
|
|
||||||
ALTER TABLE settings ADD COLUMN activeDid TEXT;
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Data Rollback**
|
|
||||||
```typescript
|
|
||||||
// Rollback function to restore activeDid to settings table
|
|
||||||
async function rollbackActiveDidMigration(): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
// Get activeDid from active_identity table
|
|
||||||
const activeIdentityResult = await dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (activeIdentityResult?.values?.length) {
|
|
||||||
const activeDid = activeIdentityResult.values[0][0] as string;
|
|
||||||
|
|
||||||
// Restore to settings table
|
|
||||||
await dbExec(
|
|
||||||
"UPDATE settings SET activeDid = ? WHERE id = ?",
|
|
||||||
[activeDid, MASTER_SETTINGS_KEY]
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("[Rollback] Failed to restore activeDid:", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Rollback Triggers**
|
|
||||||
- Migration validation fails
|
|
||||||
- Data integrity checks fail
|
|
||||||
- Performance regression detected
|
|
||||||
- User reports data loss
|
|
||||||
- Cross-platform inconsistencies found
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
| Owner | Task | Exit Criteria | Target Date (UTC) |
|
|
||||||
|-------|------|---------------|-------------------|
|
|
||||||
| Development Team | Add migration to existing MIGRATIONS array | Migration script integrated with existing system | 2025-08-30 |
|
|
||||||
| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-08-30 |
|
|
||||||
| Development Team | Update PlatformServiceMixin | Core methods updated and tested | 2025-08-31 |
|
|
||||||
| Development Team | Implement foreign key constraints | Schema validation prevents corruption | 2025-08-31 |
|
|
||||||
| QA Team | Platform testing | All platforms tested and verified | 2025-09-01 |
|
|
||||||
| Development Team | Deploy migration | Production deployment successful | 2025-09-02 |
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [Database Migration Guide](./database-migration-guide.md)
|
|
||||||
- [Dexie to SQLite Mapping](./dexie-to-sqlite-mapping.md)
|
|
||||||
- [PlatformServiceMixin Documentation](./component-communication-guide.md)
|
|
||||||
- [Migration Templates](./migration-templates/)
|
|
||||||
|
|
||||||
## Competence Hooks
|
|
||||||
|
|
||||||
- *Why this works*: Separates concerns between identity selection and
|
|
||||||
user preferences, prevents data corruption with foreign key constraints,
|
|
||||||
centralizes identity management through API layer
|
|
||||||
- *Common pitfalls*: Forgetting to implement foreign key constraints, not
|
|
||||||
testing rollback scenarios, missing data validation during migration,
|
|
||||||
over-engineering component updates when API layer handles everything
|
|
||||||
- *Next skill unlock*: Advanced database schema design with constraints,
|
|
||||||
migration planning with rollback strategies
|
|
||||||
- *Teach-back*: Explain the four-phase migration approach and why each
|
|
||||||
phase is necessary, especially the foreign key constraints
|
|
||||||
|
|
||||||
## Collaboration Hooks
|
|
||||||
|
|
||||||
- **Sign-off checklist**:
|
|
||||||
- [ ] Migration script integrated with existing MIGRATIONS array
|
|
||||||
- [ ] Foreign key constraints implemented and tested
|
|
||||||
- [ ] PlatformServiceMixin updated and tested
|
|
||||||
- [ ] Rollback procedures validated
|
|
||||||
- [ ] Performance impact assessed
|
|
||||||
- [ ] All stakeholders approve deployment timeline
|
|
||||||
|
|
||||||
## Assumptions & Limits
|
|
||||||
|
|
||||||
- Current activeDid values are valid and should be preserved
|
|
||||||
- All platforms can handle the additional database table
|
|
||||||
- Migration can be completed without user downtime
|
|
||||||
- Rollback to previous schema is acceptable if needed
|
|
||||||
- Performance impact of additional table join is minimal
|
|
||||||
- Foreign key constraints will prevent future corruption
|
|
||||||
- API layer updates will handle component compatibility
|
|
||||||
|
|
||||||
## What Needs to Change
|
|
||||||
|
|
||||||
### **1. Database Schema via migration.ts**
|
|
||||||
- Add migration to existing MIGRATIONS array in `src/db-sql/migration.ts`
|
|
||||||
- Create `active_identity` table with foreign key constraints
|
|
||||||
- Add performance indexes
|
|
||||||
- **Keep `activeDid` field in `settings` table temporarily** for backward compatibility
|
|
||||||
- **Preserve `MASTER_SETTINGS_KEY = "1"`** for legacy migration support
|
|
||||||
|
|
||||||
### **2. PlatformServiceMixin Methods**
|
|
||||||
- `$accountSettings()` - integrate with new table while maintaining backward compatibility
|
|
||||||
- `$saveSettings()` - handle activeDid in new table, sync with legacy field
|
|
||||||
- `$updateActiveDid()` - validate and update new table, sync with legacy field
|
|
||||||
- `$getActiveIdentity()` - new method for identity management
|
|
||||||
|
|
||||||
### **3. Master Settings Functions**
|
|
||||||
- `retrieveSettingsForDefaultAccount()` - integrate with new table while preserving legacy support
|
|
||||||
- `$getMergedSettings()` - integrate with new table while preserving legacy support
|
|
||||||
|
|
||||||
### **4. Type Definitions**
|
|
||||||
- **Keep `activeDid` in Settings type temporarily** for backward compatibility
|
|
||||||
- Create ActiveIdentity type for new table
|
|
||||||
- Update related interfaces
|
|
||||||
|
|
||||||
### **5. Legacy Compatibility**
|
|
||||||
- **Preserve `MASTER_SETTINGS_KEY = "1"`** for IndexedDB migration service
|
|
||||||
- **Maintain dual-write pattern** during transition period
|
|
||||||
- **Ensure legacy clients can still migrate** from Dexie to SQLite
|
|
||||||
|
|
||||||
## What Doesn't Need to Change
|
## What Doesn't Need to Change
|
||||||
|
|
||||||
@@ -529,48 +216,91 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
|
|||||||
- **User interface** - No changes to identity selection UI
|
- **User interface** - No changes to identity selection UI
|
||||||
- **Authentication flow** - Existing system unchanged
|
- **Authentication flow** - Existing system unchanged
|
||||||
- **Component logic** - All activeDid handling through API methods
|
- **Component logic** - All activeDid handling through API methods
|
||||||
- **Migration system** - Use existing migration.ts approach, not separate files
|
- **Migration system** - Use existing migration.ts approach
|
||||||
- **IndexedDB migration service** - Must continue working for legacy clients
|
- **IndexedDB migration service** - Continues working unchanged
|
||||||
|
- **Existing database operations** - All current queries continue working
|
||||||
|
|
||||||
## Enhanced Architecture: Dual-Write Pattern
|
## Implementation Steps
|
||||||
|
|
||||||
### **Phase 1: Add New Table (Current)**
|
### **Step 1: Add Migration**
|
||||||
```typescript
|
|
||||||
// Create active_identity table
|
- Add migration 003 to `MIGRATIONS` array in `src/db-sql/migration.ts`
|
||||||
// Keep existing settings.activeDid for backward compatibility
|
- Deploy migration to create `active_identity` table
|
||||||
// Use dual-write pattern during transition
|
|
||||||
|
### **Step 2: Implement API Methods**
|
||||||
|
|
||||||
|
- Create `ActiveIdentity` interface in `src/db/tables/settings.ts`
|
||||||
|
- Implement `$getActiveIdentity()` method in PlatformServiceMixin
|
||||||
|
- Implement `$accountSettings()` method in PlatformServiceMixin
|
||||||
|
- Enhance `$updateActiveDid()` method with dual-write pattern
|
||||||
|
|
||||||
|
### **Step 3: Test Integration**
|
||||||
|
|
||||||
|
- Test new methods with existing components
|
||||||
|
- Verify dual-write pattern works correctly
|
||||||
|
- Validate backward compatibility
|
||||||
|
|
||||||
|
## Backward Compatibility
|
||||||
|
|
||||||
|
### **Critical Requirements**
|
||||||
|
|
||||||
|
- **IndexedDB Migration Service**: Must continue working unchanged
|
||||||
|
- **MASTER_SETTINGS_KEY = "1"**: Must be preserved for legacy support
|
||||||
|
- **Dual-Write Pattern**: Ensures both old and new systems stay in sync
|
||||||
|
- **Existing Queries**: All current database operations continue working
|
||||||
|
|
||||||
|
### **Migration Strategy**
|
||||||
|
|
||||||
|
- **Phase 1**: Add new table alongside existing system
|
||||||
|
- **Phase 2**: Use dual-write pattern during transition
|
||||||
|
- **Phase 3**: Future cleanup (not in current scope)
|
||||||
|
|
||||||
|
## Rollback Strategy
|
||||||
|
|
||||||
|
If migration fails, the existing `activeDid` field in settings table remains functional:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Rollback: Remove new table
|
||||||
|
DROP TABLE IF EXISTS active_identity;
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Phase 2: Dual-Write Pattern**
|
No data loss risk - the legacy field continues working unchanged.
|
||||||
```typescript
|
|
||||||
// When updating activeDid:
|
|
||||||
// 1. Update active_identity table (new system)
|
|
||||||
// 2. Update settings.activeDid (legacy compatibility)
|
|
||||||
// 3. Ensure both stay in sync
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Phase 3: Future Cleanup (Not in Current Scope)**
|
## Success Criteria
|
||||||
```typescript
|
|
||||||
// Eventually:
|
|
||||||
// 1. Remove activeDid from settings table
|
|
||||||
// 2. Deprecate MASTER_SETTINGS_KEY
|
|
||||||
// 3. Use pure accountDid IS NULL pattern
|
|
||||||
// 4. Update IndexedDB migration service
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backward Compatibility Requirements
|
- [ ] `active_identity` table created with proper constraints
|
||||||
|
- [ ] All new PlatformServiceMixin methods implemented and tested
|
||||||
|
- [ ] Dual-write pattern working correctly
|
||||||
|
- [ ] Existing IndexedDB migration service continues working
|
||||||
|
- [ ] No breaking changes to existing functionality
|
||||||
|
- [ ] All platforms tested and verified
|
||||||
|
|
||||||
### **Critical: IndexedDB Migration Service**
|
## Risks & Mitigation
|
||||||
- **Must continue working** for users migrating from Dexie
|
|
||||||
- **Must recognize `id = "1"`** as master settings
|
|
||||||
- **Must preserve existing migration paths**
|
|
||||||
|
|
||||||
### **Important: Legacy Database Operations**
|
### **Low Risk: Migration Failure**
|
||||||
- **Must continue working** for existing SQLite databases
|
|
||||||
- **Must handle both old and new patterns**
|
|
||||||
- **Must not break existing queries**
|
|
||||||
|
|
||||||
### **Desired: Cleaner Architecture**
|
- **Mitigation**: Rollback removes new table, legacy system continues working
|
||||||
- **New operations** use `accountDid IS NULL` pattern
|
- **Impact**: No data loss, no service interruption
|
||||||
- **Legacy operations** continue using `MASTER_SETTINGS_KEY`
|
|
||||||
- **Gradual migration** toward cleaner patterns
|
### **Low Risk: API Changes**
|
||||||
|
|
||||||
|
- **Mitigation**: Dual-write pattern maintains backward compatibility
|
||||||
|
- **Impact**: Existing components continue working unchanged
|
||||||
|
|
||||||
|
### **Low Risk: Performance Impact**
|
||||||
|
|
||||||
|
- **Mitigation**: Proper indexing and minimal additional queries
|
||||||
|
- **Impact**: Negligible performance change
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This migration plan:
|
||||||
|
|
||||||
|
1. **Adds new architecture** without breaking existing functionality
|
||||||
|
2. **Integrates seamlessly** with existing IndexedDB migration service
|
||||||
|
3. **Maintains full backward compatibility** during transition
|
||||||
|
4. **Requires minimal changes** to existing codebase
|
||||||
|
5. **Provides clear rollback path** if issues arise
|
||||||
|
|
||||||
|
The plan focuses only on necessary changes while preserving all existing
|
||||||
|
functionality and migration paths.
|
||||||
|
|||||||
Reference in New Issue
Block a user