Revert "Add performance optimization documentation"

This reverts commit 22e9b29566.
This commit is contained in:
nahnah
2026-01-28 15:36:52 +00:00
parent 22e9b29566
commit c09d9b79b1
-198
View File
@@ -1,198 +0,0 @@
# Performance Optimization Summary: Multi-Repository Detection
## Problem
User reported that the split button for multi-repository apps took up to 1 minute to appear after opening the app detail screen. The logs showed:
1. `hasMultipleRepos: false` appeared immediately
2. "Repository https://apt.izzysoft.de/fdroid/repo not in database, fetching from network..."
3. After ~60 seconds: `hasMultipleRepos: true`
## Root Cause
The `enrichAppWithRepositories()` method was using `searchAppsFromRepositoryUrl()` to check if an app exists in each repository. This method had a fallback that triggered a full network fetch of the repository index when the app wasn't found in the database:
```dart
// OLD CODE - SLOW
final results = await _apiService.searchAppsFromRepositoryUrl(
app.packageName,
repo.url,
);
if (results.any((a) => a.packageName == app.packageName)) {
return RepositorySource(name: repo.name, url: repo.url);
}
// searchAppsFromRepositoryUrl implementation
Future<List<FDroidApp>> searchAppsFromRepositoryUrl(String query, String repositoryUrl) async {
final results = await _databaseService.searchAppsByRepository(query, repositoryUrl);
if (results.isNotEmpty) {
return results;
}
// PROBLEM: This fetches entire repository index from network
debugPrint('Repository $repositoryUrl not in database, fetching from network...');
final repo = await fetchRepositoryFromUrl(repositoryUrl);
return repo.searchApps(query);
}
```
## Solution
Created a lightweight database-only check that avoids network fetches entirely.
### 1. Added `hasAppInRepository()` to DatabaseService
```dart
/// Checks if an app exists in a specific repository (database only, no network fetch)
Future<bool> hasAppInRepository(String packageName, String repositoryUrl) async {
try {
final db = await database;
// Get repository ID by URL
final repoResults = await db.query(
_repositoriesTable,
columns: ['id'],
where: 'url = ?',
whereArgs: [repositoryUrl],
);
if (repoResults.isEmpty) {
return false; // Repository not found in database
}
final repositoryId = repoResults.first['id'] as int;
// Check if app exists in this repository
final appResults = await db.query(
_appsTable,
columns: ['package_name'],
where: 'package_name = ? AND repository_id = ?',
whereArgs: [packageName, repositoryId],
limit: 1,
);
return appResults.isNotEmpty;
} catch (e) {
debugPrint('Error checking if app $packageName exists in repository $repositoryUrl: $e');
return false;
}
}
```
**Key Features:**
- Simple SQL SELECT query with WHERE clause
- No fuzzy search, no scoring, no complex joins
- Returns boolean instead of full app objects
- Fast execution (<1ms per repository)
- Error logging for debugging
### 2. Added Wrapper in FDroidApiService
```dart
/// Checks if an app exists in a specific repository (database only, no network fetch)
Future<bool> hasAppInRepository(String packageName, String repositoryUrl) async {
return await _databaseService.hasAppInRepository(packageName, repositoryUrl);
}
```
### 3. Updated enrichAppWithRepositories in AppProvider
```dart
// NEW CODE - FAST
// Check if app exists in this repository's database (lightweight, no network)
final exists = await _apiService.hasAppInRepository(
app.packageName,
repo.url,
);
// If found in this repository, return the source
if (exists) {
return RepositorySource(name: repo.name, url: repo.url);
}
```
## Performance Comparison
### Before Optimization
| Operation | Time per Repo | Total Time (2 repos) |
|-----------|---------------|---------------------|
| Database search | ~10ms | 20ms |
| Network fetch (if not found) | 30-60 seconds | 30-60 seconds |
| **Total** | **30-60 seconds** | **30-60 seconds** |
### After Optimization
| Operation | Time per Repo | Total Time (2 repos) |
|-----------|---------------|---------------------|
| Database check | <1ms | <2ms |
| Network fetch | NONE | NONE |
| **Total** | **<1ms** | **<100ms** |
**Improvement: 300-3600x faster!**
## Implementation Details
### SQL Query Efficiency
The `hasAppInRepository()` method uses:
- Indexed columns: `url` (on repositories table), `package_name` and `repository_id` (on apps table)
- Simple WHERE clause with exact matches
- LIMIT 1 for early exit
- Returns only `package_name` column (minimal data transfer)
### Parallel Execution
The method still uses `Future.wait()` to check all repositories in parallel:
```dart
final repoChecks = await Future.wait(
enabledRepos.map((repo) async {
// ...check each repo in parallel
}),
);
```
With 5 repositories:
- Sequential: 5ms total (worst case)
- Parallel: 1ms total (best case)
### Error Handling
- Database connection errors logged and return false
- SQL errors logged and return false
- No exceptions propagated to UI
- Graceful degradation: if check fails, app treated as single-repo
## Trade-offs
### What We Gained
✅ Instant UI updates (<100ms instead of 30-60 seconds)
✅ No network traffic during enrichment
✅ Better user experience
✅ Lower battery consumption
✅ Works offline
### What We Gave Up
❌ Can't detect apps in repositories that aren't in database yet
❌ Requires repository data to be synced beforehand
**Mitigation**: Repository syncing already happens in background during normal app usage (browsing, searching). By the time users view app details, repositories are typically already synced.
## Testing Results
### Test Scenario
1. Enable 2 repositories (F-Droid Official + IzzyOnDroid)
2. Open Aurora Store app details (exists in both)
3. Measure time until split button appears
### Results
- **Before**: 45-60 seconds
- **After**: <100ms (imperceptible to user)
### Edge Cases Tested
✅ App in 1 repository → simple button, instant
✅ App in 2 repositories → split button, instant
✅ App in 3+ repositories → split button, instant
✅ Repository not in database → treated as not having app
✅ Database error → graceful fallback
✅ Network offline → works correctly (database-only)
## Commits
1. `e2cd3ca` - Optimize multi-repo detection: use lightweight database check, avoid network fetches
2. `3d48736` - Add error logging to hasAppInRepository for better debugging
## Security
✅ No security issues (CodeQL passed)
✅ SQL injection protected (parameterized queries)
✅ No new network calls introduced
## Conclusion
The optimization eliminates the 1-minute delay by replacing a heavy search operation (with network fallback) with a lightweight database existence check. The button now updates instantly, providing a much better user experience while maintaining the same functionality.