perf: scale fixes for 1M+ photo libraries
Indexes: share_targets.target_id, duplicate_groups.status, GIN on stacks members + duplicate candidates JSONB, composite (owner_user_id, created_at DESC) on assets. N+1 elimination: batch metadata loading via find_by_assets(ids) using WHERE asset_id = ANY($1), used in timeline + sidecar export. Visibility: cache find_targets_for_user per request via OnceCell, extract filter_visible helper to reduce duplication. Streaming: FileStoragePort.open_file() returns (DataStream, u64), LocalFileStorage uses ReaderStream instead of loading full file. serve_file/serve_derivative use Body::from_stream(). Unbounded queries: sidecar full_export/import batched in 500-row chunks instead of u32::MAX. find_unresolved paginated with limit/offset. list_duplicates API accepts pagination params.
This commit is contained in:
@@ -550,6 +550,23 @@ impl AssetMetadataRepository for InMemoryAssetMetadataRepository {
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn find_by_assets(
|
||||
&self,
|
||||
asset_ids: &[SystemId],
|
||||
) -> Result<Vec<AssetMetadata>, DomainError> {
|
||||
let data = self.data.lock().await;
|
||||
let mut results = Vec::new();
|
||||
for id in asset_ids {
|
||||
let prefix = format!("{id}:");
|
||||
results.extend(
|
||||
data.iter()
|
||||
.filter(|(k, _)| k.starts_with(&prefix))
|
||||
.map(|(_, v)| v.clone()),
|
||||
);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn find_by_asset_and_source(
|
||||
&self,
|
||||
asset_id: &SystemId,
|
||||
@@ -785,13 +802,19 @@ impl DuplicateRepository for InMemoryDuplicateRepository {
|
||||
Ok(self.data.lock().await.get(&id.to_string()).cloned())
|
||||
}
|
||||
|
||||
async fn find_unresolved(&self) -> Result<Vec<DuplicateGroup>, DomainError> {
|
||||
async fn find_unresolved(
|
||||
&self,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
) -> Result<Vec<DuplicateGroup>, DomainError> {
|
||||
Ok(self
|
||||
.data
|
||||
.lock()
|
||||
.await
|
||||
.values()
|
||||
.filter(|g| g.status == DuplicateStatus::Unresolved)
|
||||
.skip(offset as usize)
|
||||
.take(limit as usize)
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user