fix(manager-core): F-P-008 concurrent trigger hydration in list_for_app (wall-clock fix)
TriggerRepo::list_for_app selected parent rows then called
hydrate_one(...) per row serially — one SELECT against the kind-
specific details table per trigger. For N triggers on an app, that's
N+1 queries serialized on every dashboard `GET /apps/{id}/triggers`.
This commit shrinks the wall-clock cost via buffered concurrency
(`try_buffered(8)`): each row's details fetch still runs but they run
in parallel up to a small fan-out cap. Cuts dashboard load time from
sum(N latencies) to max(N latencies) / 8.
Collapsing to a single per-kind LEFT JOIN (the audit's preferred shape)
would cut the query count to ~7 — large enough to defer to v1.2 as
documented in the inline comment.
AUDIT.md anchor: F-P-008 (partial: wall-clock only; round-trip count
deferred).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -979,10 +979,21 @@ impl TriggerRepo for PostgresTriggerRepo {
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
|
||||
let mut out = Vec::with_capacity(parents.len());
|
||||
for p in parents {
|
||||
out.push(hydrate_one(&self.pool, p).await?);
|
||||
}
|
||||
// F-P-008 (partial): concurrent hydration so a dashboard with N
|
||||
// triggers waits for max(per-row-latency) instead of
|
||||
// sum(per-row-latency). Still N+1 queries against the details
|
||||
// tables — collapsing to a single per-kind LEFT JOIN is a
|
||||
// larger SQL refactor tracked as v1.2.
|
||||
use futures::stream::{self, TryStreamExt};
|
||||
let pool = self.pool.clone();
|
||||
let out = stream::iter(parents.into_iter().map(Ok))
|
||||
.map_ok(|p| {
|
||||
let pool = pool.clone();
|
||||
async move { hydrate_one(&pool, p).await }
|
||||
})
|
||||
.try_buffered(8)
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user