perf(templates): resync and resolved-members read only the chain/subtree (ScadaBridge#89)
The last two whole-library template reads are gone. Both fed the inheritance-only TemplateInheritanceResolver, which walks one template's parent chain and never follows compositions. - TemplateService.ResyncInheritedMembersAsync (operator repair, CLI + editor) now reads #86's GetTemplateReconcileClosureAsync (derived subtree + the template's chain, tracked). When nothing derives from the template the closure is empty, so it falls back to the new tracked GetTemplateChainAsync - resyncing a leaf reads only its own chain. - ManagementActor.HandleGetResolvedTemplateMembers now reads GetTemplateChainAsync(id, trackChanges: false): the chain alone, untracked, with script bodies (the resolver projects script code, so #66's body-free analysis closure would not be equivalent). The chain walk is factored out of TemplateReconcileClosure into CollectChainAsync (one load per level, stops on parent cycles and dangling parents) and shared by both. Evidence: - TemplateChainReadTests (#86 fixture): for every template plus an unknown id, the closure-backed resync issues exactly the writes, audit row and result of the whole-graph resync with GetAllTemplatesAsync wired to throw; the resolver answers identically from the chain and the whole graph. 17 of these fail against the old service; 13 fail with the leaf chain fallback removed (mutation-checked). - TemplateChainRepositoryTests (SQLite, recorded SQL): a 4-deep chain loads 4 templates / 8 attributes instead of 46 / 92, one split-query set per level, untracked on request and tracked for the resync. - ResolvedTemplateMembersReadTests: the actor answers from the untracked chain read and never calls GetAllTemplatesAsync (fails on the old actor). Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+20
-2
@@ -161,8 +161,26 @@ public class TemplateEngineRepository : ITemplateEngineRepository
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
// The tracked, fully-included template shape shared by GetAllTemplatesAsync and
|
||||
// GetTemplateReconcileClosureAsync.
|
||||
/// <inheritdoc />
|
||||
// One fully-included query per chain level (Gitea #89). Chains are short (a handful of
|
||||
// levels), so this is a few small reads instead of the whole library.
|
||||
public Task<IReadOnlyList<Template>> GetTemplateChainAsync(int templateId, bool trackChanges, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return TemplateReconcileClosure.CollectChainAsync(
|
||||
templateId,
|
||||
async (ids, ct) =>
|
||||
{
|
||||
var idList = ids.ToList();
|
||||
var query = FullTemplateGraph().Where(t => idList.Contains(t.Id));
|
||||
if (!trackChanges)
|
||||
query = query.AsNoTracking();
|
||||
return await query.ToListAsync(ct);
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
// The tracked, fully-included template shape shared by GetAllTemplatesAsync,
|
||||
// GetTemplateReconcileClosureAsync and GetTemplateChainAsync.
|
||||
private IQueryable<Template> FullTemplateGraph()
|
||||
{
|
||||
return _context.Templates
|
||||
|
||||
Reference in New Issue
Block a user