forked from jsnbuchanan/crowd-funder-for-time-pwa
chore: add plan
This commit is contained in:
506
doc/activeDid-migration-plan.md
Normal file
506
doc/activeDid-migration-plan.md
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
# ActiveDid Migration Plan - Separate Table Architecture
|
||||||
|
|
||||||
|
**Author**: Matthew Raymer
|
||||||
|
**Date**: 2025-01-27T18:30Z
|
||||||
|
**Status**: 🎯 **PLANNING** - Active migration planning phase
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Move the `activeDid` field from the `settings` table to a dedicated
|
||||||
|
`active_identity` table to improve database architecture and separate
|
||||||
|
identity selection from user preferences.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
This document serves as the comprehensive planning and implementation
|
||||||
|
guide for the ActiveDid migration.
|
||||||
|
|
||||||
|
## Use/Run
|
||||||
|
|
||||||
|
Reference this document during implementation to ensure all migration
|
||||||
|
steps are followed correctly and all stakeholders are aligned on the
|
||||||
|
approach.
|
||||||
|
|
||||||
|
## Context & Scope
|
||||||
|
|
||||||
|
- **In scope**:
|
||||||
|
- Database schema modification for active_identity table
|
||||||
|
- Migration of existing activeDid data
|
||||||
|
- Updates to all platform services and mixins
|
||||||
|
- Type definition updates
|
||||||
|
- Testing across all platforms
|
||||||
|
- **Out of scope**:
|
||||||
|
- Changes to user interface for identity selection
|
||||||
|
- Modifications to identity creation logic
|
||||||
|
- Changes to authentication flow
|
||||||
|
|
||||||
|
## Environment & Preconditions
|
||||||
|
|
||||||
|
- **OS/Runtime**: All platforms (Web, Electron, iOS, Android)
|
||||||
|
- **Versions/Builds**: Current development branch, SQLite database
|
||||||
|
- **Services/Endpoints**: Local database, PlatformServiceMixin
|
||||||
|
- **Auth mode**: Existing authentication system unchanged
|
||||||
|
|
||||||
|
## Architecture / Process Overview
|
||||||
|
|
||||||
|
The migration follows a phased approach to minimize risk and ensure
|
||||||
|
data integrity:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Current State<br/>activeDid in settings] --> B[Phase 1: Schema Creation<br/>Add active_identity table]
|
||||||
|
B --> C[Phase 2: Data Migration<br/>Copy activeDid data]
|
||||||
|
C --> D[Phase 3: API Updates<br/>Update all access methods]
|
||||||
|
D --> E[Phase 4: Cleanup<br/>Remove activeDid from settings]
|
||||||
|
E --> F[Final State<br/>Separate active_identity table]
|
||||||
|
|
||||||
|
G[Rollback Plan<br/>Keep old field until verified] --> H[Data Validation<br/>Verify integrity at each step]
|
||||||
|
H --> I[Platform Testing<br/>Test all platforms]
|
||||||
|
I --> J[Production Deployment<br/>Gradual rollout]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interfaces & Contracts
|
||||||
|
|
||||||
|
### Database Schema Changes
|
||||||
|
|
||||||
|
| Table | Current Schema | New Schema | Migration Required |
|
||||||
|
|-------|----------------|------------|-------------------|
|
||||||
|
| `settings` | `activeDid TEXT` | Field removed | Yes - data migration |
|
||||||
|
| `active_identity` | Does not exist | New table with `activeDid TEXT` | Yes - table creation |
|
||||||
|
|
||||||
|
### API Contract Changes
|
||||||
|
|
||||||
|
| Method | Current Behavior | New Behavior | Breaking Change |
|
||||||
|
|---------|------------------|--------------|-----------------|
|
||||||
|
| `$accountSettings()` | Returns settings with activeDid | Returns settings without activeDid | No - backward compatible |
|
||||||
|
| `$saveSettings()` | Updates settings.activeDid | Updates active_identity.activeDid | Yes - requires updates |
|
||||||
|
| `$updateActiveDid()` | Updates internal tracking | Updates active_identity table | Yes - requires updates |
|
||||||
|
|
||||||
|
## Repro: End-to-End Procedure
|
||||||
|
|
||||||
|
### Phase 1: Schema Creation
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Create new active_identity table
|
||||||
|
CREATE TABLE active_identity (
|
||||||
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
|
activeDid TEXT NOT NULL,
|
||||||
|
lastUpdated TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Insert default record (will be updated during migration)
|
||||||
|
INSERT INTO active_identity (id, activeDid) VALUES (1, '');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Data Migration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Migration script to copy existing activeDid values
|
||||||
|
async function migrateActiveDidToSeparateTable(): Promise<void> {
|
||||||
|
// Get current activeDid from settings
|
||||||
|
const currentSettings = await retrieveSettingsForDefaultAccount();
|
||||||
|
const activeDid = currentSettings.activeDid;
|
||||||
|
|
||||||
|
if (activeDid) {
|
||||||
|
// Insert into new table
|
||||||
|
await dbExec(
|
||||||
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
|
[activeDid]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: API Updates
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Updated PlatformServiceMixin method
|
||||||
|
async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings> {
|
||||||
|
// Get settings without activeDid
|
||||||
|
const settings = await this._getSettingsWithoutActiveDid();
|
||||||
|
|
||||||
|
// Get activeDid from separate table
|
||||||
|
const activeIdentity = await this._getActiveIdentity();
|
||||||
|
|
||||||
|
return { ...settings, activeDid: activeIdentity.activeDid };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## What Works (Evidence)
|
||||||
|
|
||||||
|
- ✅ **Current activeDid storage** in settings table
|
||||||
|
- **Time**: 2025-01-27T18:30Z
|
||||||
|
- **Evidence**: `src/db/tables/settings.ts:25` - activeDid field exists
|
||||||
|
- **Verify at**: Current database schema and Settings type definition
|
||||||
|
|
||||||
|
- ✅ **PlatformServiceMixin integration** with activeDid
|
||||||
|
- **Time**: 2025-01-27T18:30Z
|
||||||
|
- **Evidence**: `src/utils/PlatformServiceMixin.ts:108` - activeDid tracking
|
||||||
|
- **Verify at**: Component usage across all platforms
|
||||||
|
|
||||||
|
- ✅ **Database migration infrastructure** exists
|
||||||
|
- **Time**: 2025-01-27T18:30Z
|
||||||
|
- **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-01-27T18:30Z
|
||||||
|
- **Evidence**: Database schema only shows settings table
|
||||||
|
- **Hypothesis**: Table needs to be created as part of migration
|
||||||
|
- **Next probe**: Create migration script for new table
|
||||||
|
|
||||||
|
- ❌ **Platform services hardcoded** to settings table
|
||||||
|
- **Time**: 2025-01-27T18:30Z
|
||||||
|
- **Evidence**: `src/services/platforms/*.ts` - direct settings table access
|
||||||
|
- **Hypothesis**: All platform services need updates
|
||||||
|
- **Next probe**: Audit all platform service files for activeDid usage
|
||||||
|
|
||||||
|
## Risks, Limits, Assumptions
|
||||||
|
|
||||||
|
- **Data Loss Risk**: Migration failure could lose activeDid values
|
||||||
|
- **Breaking Changes**: API updates required across all platform services
|
||||||
|
- **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
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
| Owner | Task | Exit Criteria | Target Date (UTC) |
|
||||||
|
|-------|------|---------------|-------------------|
|
||||||
|
| Development Team | Create migration script | Migration script tested and validated | 2025-01-28 |
|
||||||
|
| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-01-28 |
|
||||||
|
| Development Team | Update platform services | All services use new active_identity table | 2025-01-29 |
|
||||||
|
| Development Team | Update PlatformServiceMixin | Mixin methods updated and tested | 2025-01-29 |
|
||||||
|
| QA Team | Platform testing | All platforms tested and verified | 2025-01-30 |
|
||||||
|
| Development Team | Deploy migration | Production deployment successful | 2025-01-31 |
|
||||||
|
|
||||||
|
## 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, improves database normalization, enables future
|
||||||
|
identity management features
|
||||||
|
- *Common pitfalls*: Forgetting to update all platform services, not
|
||||||
|
testing rollback scenarios, missing data validation during migration
|
||||||
|
- *Next skill unlock*: Advanced database schema design and migration
|
||||||
|
planning
|
||||||
|
- *Teach-back*: Explain the four-phase migration approach and why each
|
||||||
|
phase is necessary
|
||||||
|
|
||||||
|
## Collaboration Hooks
|
||||||
|
|
||||||
|
- **Sign-off checklist**:
|
||||||
|
- [ ] Migration script tested on development database
|
||||||
|
- [ ] All platform services updated and tested
|
||||||
|
- [ ] Rollback plan 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
|
||||||
|
|
||||||
|
## Component & View Impact Analysis
|
||||||
|
|
||||||
|
### **High Impact Components**
|
||||||
|
|
||||||
|
1. **`IdentitySection.vue`** - Direct dependency on `activeDid`
|
||||||
|
- **Current**: Uses `activeDid` from component data
|
||||||
|
- **Impact**: Will need to update data binding and refresh logic
|
||||||
|
- **Risk**: **HIGH** - Core identity display component
|
||||||
|
|
||||||
|
**Current Implementation:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="text-sm text-slate-500">
|
||||||
|
<div class="font-bold">ID: </div>
|
||||||
|
<code class="truncate">{{ activeDid }}</code>
|
||||||
|
<!-- ... rest of template -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default class IdentitySection extends Vue {
|
||||||
|
activeDid = ""; // Direct component data
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required Changes:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts">
|
||||||
|
export default class IdentitySection extends Vue {
|
||||||
|
activeDid = ""; // Still needed for template binding
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// This will automatically work if $accountSettings() is updated
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add watcher for activeDid changes
|
||||||
|
async onActiveDidChanged(newDid: string | null) {
|
||||||
|
if (newDid) {
|
||||||
|
this.activeDid = newDid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **`DIDView.vue`** - Heavy activeDid usage
|
||||||
|
- **Current**: Initializes `activeDid` in `mounted()` lifecycle
|
||||||
|
- **Impact**: Must update initialization logic to use new table
|
||||||
|
- **Risk**: **HIGH** - Primary DID viewing component
|
||||||
|
|
||||||
|
**Current Implementation:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts">
|
||||||
|
export default class DIDView extends Vue {
|
||||||
|
activeDid = ""; // Component data
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
await this.initializeSettings();
|
||||||
|
await this.determineDIDToDisplay();
|
||||||
|
// ... rest of initialization
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeSettings() {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
this.apiServer = settings.apiServer || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async determineDIDToDisplay() {
|
||||||
|
const pathParam = window.location.pathname.substring("/did/".length);
|
||||||
|
let showDid = pathParam;
|
||||||
|
|
||||||
|
if (!showDid) {
|
||||||
|
// No DID provided in URL, use active DID
|
||||||
|
showDid = this.activeDid; // Uses component data
|
||||||
|
this.notifyDefaultToActiveDID();
|
||||||
|
}
|
||||||
|
// ... rest of logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required Changes:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts">
|
||||||
|
export default class DIDView extends Vue {
|
||||||
|
activeDid = ""; // Component data still needed
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
await this.initializeSettings();
|
||||||
|
await this.determineDIDToDisplay();
|
||||||
|
// ... rest of initialization
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeSettings() {
|
||||||
|
// This will automatically work if $accountSettings() is updated
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
this.apiServer = settings.apiServer || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add watcher for activeDid changes
|
||||||
|
async onActiveDidChanged(newDid: string | null) {
|
||||||
|
if (newDid && newDid !== this.activeDid) {
|
||||||
|
this.activeDid = newDid;
|
||||||
|
// Re-initialize if needed
|
||||||
|
await this.determineDIDToDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **`HomeView.vue`** - ActiveDid change detection
|
||||||
|
- **Current**: Has `onActiveDidChanged()` watcher method
|
||||||
|
- **Impact**: Watcher logic needs updates for new data source
|
||||||
|
- **Risk**: **MEDIUM** - Core navigation component
|
||||||
|
|
||||||
|
**Current Implementation:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts">
|
||||||
|
export default class HomeView extends Vue {
|
||||||
|
async onActiveDidChanged(newDid: string | null, oldDid: string | null) {
|
||||||
|
if (newDid !== oldDid) {
|
||||||
|
// Re-initialize identity with new settings (loads settings internally)
|
||||||
|
await this.initializeIdentity();
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
"[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeIdentity() {
|
||||||
|
// ... identity initialization logic
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
// ... rest of initialization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required Changes:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts">
|
||||||
|
export default class HomeView extends Vue {
|
||||||
|
async onActiveDidChanged(newDid: string | null, oldDid: string | null) {
|
||||||
|
if (newDid !== oldDid) {
|
||||||
|
// This will automatically work if $accountSettings() is updated
|
||||||
|
await this.initializeIdentity();
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
"[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeIdentity() {
|
||||||
|
// ... identity initialization logic
|
||||||
|
// This will automatically work if $accountSettings() is updated
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
// ... rest of initialization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Insight**: HomeView will require minimal changes since it already uses
|
||||||
|
the `$accountSettings()` method, which will be updated to handle the new
|
||||||
|
table structure transparently.
|
||||||
|
|
||||||
|
### **Medium Impact Components**
|
||||||
|
|
||||||
|
1. **`InviteOneAcceptView.vue`** - Identity fallback logic
|
||||||
|
- **Current**: Creates identity if no `activeDid` exists
|
||||||
|
- **Impact**: Fallback logic needs to check new table
|
||||||
|
- **Risk**: **MEDIUM** - Invite processing component
|
||||||
|
|
||||||
|
2. **`ClaimView.vue`** - Settings retrieval
|
||||||
|
- **Current**: Gets `activeDid` from `$accountSettings()`
|
||||||
|
- **Impact**: Will automatically work if API is updated
|
||||||
|
- **Risk**: **LOW** - Depends on API layer updates
|
||||||
|
|
||||||
|
3. **`ContactAmountsView.vue`** - Direct settings access
|
||||||
|
- **Current**: Accesses `activeDid` directly from settings
|
||||||
|
- **Impact**: Must update to use new API methods
|
||||||
|
- **Risk**: **MEDIUM** - Financial display component
|
||||||
|
|
||||||
|
### **Service Layer Impact**
|
||||||
|
|
||||||
|
1. **`WebPlatformService.ts`**
|
||||||
|
- **Current**: Direct SQL queries to settings table
|
||||||
|
- **Impact**: Must add `active_identity` table queries
|
||||||
|
- **Risk**: **HIGH** - Core web platform service
|
||||||
|
|
||||||
|
2. **`CapacitorPlatformService.ts`**
|
||||||
|
- **Current**: Similar direct SQL access
|
||||||
|
- **Impact**: Same updates as web service
|
||||||
|
- **Risk**: **HIGH** - Mobile platform service
|
||||||
|
|
||||||
|
3. **`PlatformServiceMixin.ts`**
|
||||||
|
- **Current**: Core methods like `$accountSettings()`, `$saveSettings()`
|
||||||
|
- **Impact**: Major refactoring required
|
||||||
|
- **Risk**: **CRITICAL** - Used by 50+ components
|
||||||
|
|
||||||
|
### **API Contract Changes**
|
||||||
|
|
||||||
|
1. **`$saveSettings()` method**
|
||||||
|
- **Current**: Updates `settings.activeDid`
|
||||||
|
- **New**: Updates `active_identity.activeDid`
|
||||||
|
- **Impact**: All components using this method
|
||||||
|
|
||||||
|
2. **`$updateActiveDid()` method**
|
||||||
|
- **Current**: Internal tracking only
|
||||||
|
- **New**: Database persistence required
|
||||||
|
- **Impact**: Identity switching logic
|
||||||
|
|
||||||
|
### **Testing Impact**
|
||||||
|
|
||||||
|
1. **Unit Tests**
|
||||||
|
- All platform service methods
|
||||||
|
- PlatformServiceMixin methods
|
||||||
|
- Database migration scripts
|
||||||
|
|
||||||
|
2. **Integration Tests**
|
||||||
|
- Component behavior with new data source
|
||||||
|
- Identity switching workflows
|
||||||
|
- Settings persistence
|
||||||
|
|
||||||
|
3. **Platform Tests**
|
||||||
|
- Web, Electron, iOS, Android
|
||||||
|
- Cross-platform data consistency
|
||||||
|
- Migration success on all platforms
|
||||||
|
|
||||||
|
### **Performance Impact**
|
||||||
|
|
||||||
|
1. **Additional Table Join**
|
||||||
|
- Settings queries now require active_identity table
|
||||||
|
- Potential performance impact on frequent operations
|
||||||
|
- Need for proper indexing
|
||||||
|
|
||||||
|
2. **Caching Considerations**
|
||||||
|
- ActiveDid changes trigger cache invalidation
|
||||||
|
- Component re-rendering on identity switches
|
||||||
|
- Memory usage for additional table data
|
||||||
|
|
||||||
|
### **Risk Assessment by Component Type**
|
||||||
|
|
||||||
|
- **Critical Risk**: PlatformServiceMixin, Platform Services
|
||||||
|
- **High Risk**: Identity-related components, views using `$accountSettings()`
|
||||||
|
- **Medium Risk**: Components with direct settings access, identity management
|
||||||
|
- **Low Risk**: Components using only basic settings, utility components
|
||||||
|
|
||||||
|
### **Migration Timeline Impact**
|
||||||
|
|
||||||
|
- **Phase 1**: Schema Creation (1-2 days) - No component impact
|
||||||
|
- **Phase 2**: Data Migration (1 day) - No component impact
|
||||||
|
- **Phase 3**: API Updates (3-5 days) - All components affected
|
||||||
|
- **Phase 4**: Cleanup (1-2 days) - No component impact
|
||||||
|
|
||||||
|
### **Update Priority Order**
|
||||||
|
|
||||||
|
1. **PlatformServiceMixin** - Core dependency for most components
|
||||||
|
2. **Platform Services** - Ensure data access layer works
|
||||||
|
3. **Identity Components** - Verify core functionality
|
||||||
|
4. **Settings-Dependent Views** - Update in dependency order
|
||||||
|
5. **Utility Components** - Final cleanup and testing
|
||||||
|
|
||||||
|
## Deferred for depth
|
||||||
|
|
||||||
|
- Advanced identity management features enabled by this change
|
||||||
|
- Performance optimization strategies for the new table structure
|
||||||
|
- Future schema evolution planning
|
||||||
|
- Advanced rollback and recovery procedures
|
||||||
Reference in New Issue
Block a user