Add logic to initialize at least one user if the startup wizard is not completed, both during application startup and in the startup user API. After user creation, reload the user entity with all related navigation properties to ensure the in-memory user object is fully populated. Also update build and publish metadata files.
13 KiB
🔥 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:
IReadOnlyList<BaseItem> GetItems(InternalItemsQuery query)
Target Signature:
// 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
-
GetItems() - Most Used Method
- Test extensively
- Monitor performance
- Gradual rollout
-
SaveItems() - Data Modification
- Extra validation
- Transaction logging
- Rollback testing
-
DeleteItem() - Data Loss Risk
- Backup strategy
- Soft delete consideration
- Audit logging
Rollback Plan
If issues detected:
- Feature flag to disable async
- Revert to sync wrappers
- Investigate issues
- 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):
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):
// 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 ✅
- Break into sub-phases - Don't do all at once
- Test extensively - This is critical code
- Monitor performance - Watch for regressions
- Keep sync wrappers - Maintain backward compatibility
- Use feature flags - Enable gradual rollout
- Document everything - Future maintainers will thank you
- Get code reviews - Multiple eyes on critical code
DON'T ❌
- Rush it - Take the time needed
- Skip tests - You'll regret it
- Remove sync methods - You'll break everything
- Deploy without monitoring - You need visibility
- Ignore performance - This is hot path code
- 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