Files
pgsql-jellyfin/PHASE3_STRATEGY.md
T
wjones 86883cd5c6 Refactor PostgreSQL provider: multi-schema & async prep
- Refactor migrations and provider to use multiple PostgreSQL schemas, each matching a legacy SQLite database (activitylog, authentication, displaypreferences, library, users).
- All tables, foreign keys, and indexes are now schema-qualified; Down migration drops tables by schema.
- Provider ensures schemas exist before migrations; entities are mapped to correct schemas in OnModelCreating.
- Add support for max-pool-size, min-pool-size, and multiplexing connection options; update logging accordingly.
- VACUUM ANALYZE now runs per schema during scheduled optimization.
- TruncateAllTablesAsync now truncates tables with schema qualification.
- README updated with schema structure, new options, and multiplexing warnings.
- CacheDecorator now calls async repository methods using .GetAwaiter().GetResult(), with documentation.
- Lays groundwork for full async/await and multiplexing support in the database layer.
2026-02-23 09:38:22 -05:00

557 lines
13 KiB
Markdown

# 🔥 Phase 3 Strategy: BaseItemRepository Async Conversion
## ⚠️ CRITICAL: This is the Most Complex Repository
**Status**: Planning Phase
**Complexity**: ⭐⭐⭐⭐⭐ Very High
**Risk**: 🔴 HIGH
**Impact**: 🔴 CRITICAL
---
## 📊 Scope Analysis
### By the Numbers
- **Sync Operations**: 110
- **Public Methods**: 40+
- **Files to Modify**: 50-100
- **API Endpoints Affected**: 100+
- **Estimated Effort**: 4-6 weeks (full-time)
### Why This is Different
**BaseItemRepository is THE CORE of Jellyfin:**
- Used by nearly every feature
- Central to all library operations
- Used by all media scanning services
- Core of search and filtering
- Handles all item CRUD operations
---
## 🎯 Recommended Approach: Sub-Phases
**DO NOT** attempt to convert all at once. Break into 5 manageable sub-phases:
### **Sub-Phase 3a: Query Operations** (Week 1-2)
**Focus**: Read-only query methods
**Risk**: 🟡 MEDIUM
**Operations**: ~30
#### Methods to Convert:
- `GetItems(InternalItemsQuery query)`
- `GetItemList(InternalItemsQuery query)`
- `GetItemIdsList(InternalItemsQuery query)`
- Filter methods
- Sort methods
#### Why First:
- Most commonly used
- Read-only (safer)
- High impact on performance
- Good testing ground
---
### **Sub-Phase 3b: Item Retrieval** (Week 3-4)
**Focus**: Single item retrieval methods
**Risk**: 🟡 MEDIUM
**Operations**: ~25
#### Methods to Convert:
- `RetrieveItem(Guid id)`
- `GetCount(InternalItemsQuery query)`
- `GetItemById(Guid id)`
- Existence checks
#### Why Second:
- Still mostly read-only
- Less complex than writes
- Tests full retrieval path
---
### **Sub-Phase 3c: Write Operations** (Week 5)
**Focus**: Item creation and updates
**Risk**: 🔴 HIGH
**Operations**: ~20
#### Methods to Convert:
- `SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)`
- `UpdateInheritedValues(CancellationToken cancellationToken)`
- `UpdateImages(BaseItem item)`
#### Why Third:
- More complex logic
- Higher risk of data issues
- Needs extensive testing
- Build on query experience
---
### **Sub-Phase 3d: Delete Operations** (Week 6)
**Focus**: Item deletion
**Risk**: 🔴 HIGH
**Operations**: ~15
#### Methods to Convert:
- `DeleteItem(Guid id, CancellationToken cancellationToken)`
- Cascade deletes
- Cleanup operations
#### Why Fourth:
- Highest risk (data loss potential)
- Complex cascading logic
- Needs PeopleRepository (done ✅)
- Thorough testing critical
---
### **Sub-Phase 3e: Aggregations & Statistics** (Week 7)
**Focus**: Aggregation methods
**Risk**: 🟡 MEDIUM
**Operations**: ~20
#### Methods to Convert:
- Artist aggregations
- Album aggregations
- Statistics queries
- Genre aggregations
#### Why Last:
- Less critical
- Complex queries
- Lower risk
- Good finale
---
## 📋 Per Sub-Phase Checklist
### Preparation (1 day before starting sub-phase)
- [ ] Identify all methods in sub-phase
- [ ] Map all consumers of those methods
- [ ] Create detailed conversion plan
- [ ] Set up feature flag (if needed)
- [ ] Create test branch
### Implementation (3-5 days)
- [ ] Update interface with async methods
- [ ] Convert repository implementation
- [ ] Keep sync wrappers
- [ ] Add cancellation token support
- [ ] Proper error handling
### Consumer Updates (2-3 days)
- [ ] Update service layer
- [ ] Update API controllers
- [ ] Update background services
- [ ] Update scheduled tasks
### Testing (2-3 days)
- [ ] Unit tests
- [ ] Integration tests
- [ ] Performance testing
- [ ] Load testing
- [ ] Regression testing
### Review & Merge (1 day)
- [ ] Code review
- [ ] Address feedback
- [ ] Merge to main
- [ ] Monitor production (if deployed)
---
## 🔍 Detailed Method Analysis
### Sub-Phase 3a Example: GetItems()
**Current Signature**:
```csharp
IReadOnlyList<BaseItem> GetItems(InternalItemsQuery query)
```
**Target Signature**:
```csharp
// Sync wrapper (backward compat)
IReadOnlyList<BaseItem> GetItems(InternalItemsQuery query)
{
return GetItemsAsync(query, CancellationToken.None)
.GetAwaiter()
.GetResult();
}
// New async version
async Task<IReadOnlyList<BaseItem>> GetItemsAsync(
InternalItemsQuery query,
CancellationToken cancellationToken = default)
{
await using var context = _dbProvider.CreateDbContext();
var dbQuery = TranslateQuery(context.Items.AsNoTracking(), query);
var results = await dbQuery
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
return results.Select(Map).ToList();
}
```
**Consumers to Update** (examples):
- `LibraryManager.GetItems()`
- `BaseItem.GetChildren()`
- API Controllers (`ItemsController`, `FilterController`, etc.)
- Scheduled tasks
- Background services
**Estimated Impact**:
- 20-30 consumers
- 10-15 files to modify
---
## ⚠️ Critical Considerations
### 1. Performance Impact
**Concern**: BaseItemRepository is performance-critical
**Mitigation**:
- Extensive benchmarking before and after
- Monitor query performance
- Watch for N+1 queries
- Consider caching strategy
### 2. Data Consistency
**Concern**: Complex transaction logic
**Mitigation**:
- Transaction scope carefully managed
- Rollback on errors
- Comprehensive error handling
- Extensive testing
### 3. Breaking Changes
**Concern**: Used everywhere
**Mitigation**:
- Keep ALL sync methods
- Gradual consumer migration
- Feature flags for rollback
- Canary deployment strategy
### 4. Testing Coverage
**Concern**: Not all scenarios covered
**Mitigation**:
- Add missing tests BEFORE conversion
- Integration tests for all methods
- Load testing under realistic conditions
- Beta testing with select users
---
## 🧪 Testing Strategy
### Per Sub-Phase Testing
**Unit Tests**:
- All repository methods
- Edge cases
- Error conditions
- Cancellation scenarios
**Integration Tests**:
- Repository → Service → API
- Full request/response cycle
- Multiple concurrent requests
- Transaction rollback scenarios
**Performance Tests**:
- Benchmark each method
- Compare sync vs async performance
- Load testing (100+ concurrent users)
- Monitor connection pool usage
**Regression Tests**:
- Ensure no behavior changes
- Verify all existing features work
- Check edge cases still handled
---
## 📊 Risk Mitigation
### High-Risk Areas
1. **GetItems() - Most Used Method**
- Test extensively
- Monitor performance
- Gradual rollout
2. **SaveItems() - Data Modification**
- Extra validation
- Transaction logging
- Rollback testing
3. **DeleteItem() - Data Loss Risk**
- Backup strategy
- Soft delete consideration
- Audit logging
### Rollback Plan
**If issues detected**:
1. Feature flag to disable async
2. Revert to sync wrappers
3. Investigate issues
4. Fix and re-deploy
**Monitoring**:
- Error rates
- Response times
- Connection pool usage
- Database performance
---
## 🎯 Success Criteria Per Sub-Phase
### Technical
- [ ] All methods in sub-phase converted
- [ ] Build successful
- [ ] All tests passing
- [ ] Performance within 5% of baseline
- [ ] No memory leaks
### Functional
- [ ] All features working
- [ ] No user-reported issues
- [ ] API responses correct
- [ ] Error handling proper
### Non-Functional
- [ ] Documentation updated
- [ ] Code reviewed
- [ ] Monitoring in place
- [ ] Rollback tested
---
## 📅 Detailed Timeline
### Week 0: Preparation (Before Starting)
- [ ] Complete Phase 1 & 2 presentation
- [ ] Get stakeholder approval
- [ ] Allocate dedicated resources
- [ ] Set up monitoring
- [ ] Create performance baselines
### Week 1-2: Sub-Phase 3a (Query Operations)
- Day 1-2: Interface & implementation
- Day 3-5: Consumer updates
- Day 6-8: Testing
- Day 9-10: Review & merge
### Week 3-4: Sub-Phase 3b (Item Retrieval)
- Day 1-2: Interface & implementation
- Day 3-5: Consumer updates
- Day 6-8: Testing
- Day 9-10: Review & merge
### Week 5: Sub-Phase 3c (Write Operations)
- Day 1-2: Interface & implementation
- Day 3: Consumer updates
- Day 4-5: Extensive testing
### Week 6: Sub-Phase 3d (Delete Operations)
- Day 1-2: Interface & implementation
- Day 3: Consumer updates
- Day 4-5: Extensive testing
### Week 7: Sub-Phase 3e (Aggregations)
- Day 1-2: Interface & implementation
- Day 3: Consumer updates
- Day 4-5: Testing
### Week 8: Final Integration & Deployment
- Full regression testing
- Performance validation
- Production deployment
- Monitoring
---
## 🔧 Code Example: Complex Method
### SaveItems() - Before/After
**BEFORE (Synchronous)**:
```csharp
public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
{
using var context = _dbProvider.CreateDbContext();
using var transaction = context.Database.BeginTransaction();
foreach (var item in items)
{
// Validate item
ValidateItem(item);
// Save to database
var entity = Map(item);
context.Items.Add(entity);
// Update people
_peopleRepository.UpdatePeople(item.Id, item.People);
// Update images
UpdateImages(context, item);
}
context.SaveChanges();
transaction.Commit();
}
```
**AFTER (Asynchronous)**:
```csharp
// Sync wrapper for backward compatibility
public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
{
SaveItemsAsync(items, cancellationToken)
.GetAwaiter()
.GetResult();
}
// Async implementation
public async Task SaveItemsAsync(
IEnumerable<BaseItem> items,
CancellationToken cancellationToken = default)
{
await using var context = _dbProvider.CreateDbContext();
await using var transaction = await context.Database
.BeginTransactionAsync(cancellationToken)
.ConfigureAwait(false);
foreach (var item in items)
{
cancellationToken.ThrowIfCancellationRequested();
// Validate item
ValidateItem(item);
// Save to database
var entity = Map(item);
await context.Items.AddAsync(entity, cancellationToken)
.ConfigureAwait(false);
// Update people (using async PeopleRepository)
await _peopleRepository
.UpdatePeopleAsync(item.Id, item.People, cancellationToken)
.ConfigureAwait(false);
// Update images
await UpdateImagesAsync(context, item, cancellationToken)
.ConfigureAwait(false);
}
await context.SaveChangesAsync(cancellationToken)
.ConfigureAwait(false);
await transaction.CommitAsync(cancellationToken)
.ConfigureAwait(false);
}
```
---
## 💡 Recommendations
### DO ✅
1. **Break into sub-phases** - Don't do all at once
2. **Test extensively** - This is critical code
3. **Monitor performance** - Watch for regressions
4. **Keep sync wrappers** - Maintain backward compatibility
5. **Use feature flags** - Enable gradual rollout
6. **Document everything** - Future maintainers will thank you
7. **Get code reviews** - Multiple eyes on critical code
### DON'T ❌
1. **Rush it** - Take the time needed
2. **Skip tests** - You'll regret it
3. **Remove sync methods** - You'll break everything
4. **Deploy without monitoring** - You need visibility
5. **Ignore performance** - This is hot path code
6. **Work alone** - Get help and reviews
---
## 🎯 Recommended Next Steps
### Step 1: Present Phase 1 & 2 Results
**Audience**: Technical leadership, stakeholders
**Materials**: Use `STAKEHOLDER_PRESENTATION.md`
**Message**:
- 5/6 repositories complete (83%)
- 34 operations converted
- Zero issues, all builds passing
- Ready for Phase 3 with proper planning
### Step 2: Get Approval for Phase 3
**Request**:
- Dedicated developer(s) for 8 weeks
- QA support throughout
- Stakeholder check-ins every 2 weeks
- Approval to use feature flags
### Step 3: Preparation Week
**Tasks**:
- Analyze BaseItemRepository in detail
- Create performance baselines
- Identify all 110 operations
- Map all consumers
- Set up monitoring
### Step 4: Begin Sub-Phase 3a
**Focus**: Query operations (lowest risk)
**Timeline**: 2 weeks
**Deliverable**: Async query methods working
---
## 📚 Documentation Needed
For Phase 3, create:
- [ ] Detailed method inventory
- [ ] Consumer mapping document
- [ ] Performance baseline report
- [ ] Testing strategy document
- [ ] Deployment plan
- [ ] Rollback procedures
- [ ] Monitoring dashboard
---
## 🎉 Current Achievement
**You've completed 83% of the migration!**
- ✅ Phase 1: 4/4 repositories (100%)
- ✅ Phase 2: 1/1 repository (100%)
- ⏳ Phase 3: 0/1 repository (planning)
**That's 5 out of 6 repositories done!**
The foundation is solid. The pattern is proven. Phase 3 is well-planned. You're in excellent position to complete the final 17% of the work.
---
**Status**: Planning Complete
**Recommendation**: Present results, get approval, then begin Phase 3
**Confidence**: HIGH (with proper planning) ✅
**Risk**: Manageable (with sub-phases) 🟡
**Document Version**: 1.0
**Date**: 2025-01-15
**Next Action**: Stakeholder Presentation