BuildHtmlCache
Pennington.Infrastructure
Process-lifetime cache of fully rendered in-process HTTP responses, keyed by request path.
The static build crawls itself: the disk-write pass, the search index, and the llms.txt sidecar each self-fetch the same pages, so without sharing every page renders 2–3× through the full middleware pipeline. Installed behind HttpDispatcher via CachingHttpHandler, this collapses that to one render per URL — every consumer replays the first render.
Eviction rides the existing FileWatchDispatcher: as an IFileWatchAware with no scopes of its own, it is notified of every change another watcher already observes. On notification, it consults GetAffectedRoutes on every registered content service and evicts only the affected keys — wholesale only on a Wildcard report.
Constructors
BuildHtmlCache
#public BuildHtmlCache(IEnumerable<IContentService> contentServices)
Initializes the cache with the content services it consults for affected routes on file change.
Parameters
contentServicesIEnumerable<IContentService>
Methods
GetOrAddAsync
#public Task<CachedResponse> GetOrAddAsync(string key, Func<Task<CachedResponse>> factory)
Returns the cached response for key, invoking factory exactly once on the first request for that key. Concurrent first-requests coalesce onto the same render; a faulted render is evicted so a later request can retry.
Parameters
keystringfactoryFunc<Task<CachedResponse>>
Returns
Task<CachedResponse>OnFileChanged
#public FileWatchResponse OnFileChanged(FileChangeNotification change)
Called on the file-watcher thread for every watched change. Must be quick and thread-safe.
Parameters
changeFileChangeNotification
Returns
FileWatchResponsePennington.Infrastructure.BuildHtmlCache
namespace Pennington.Infrastructure;
/// Process-lifetime cache of fully rendered in-process HTTP responses, keyed by request path. The static build crawls itself: the disk-write pass, the search index, and the llms.txt sidecar each self-fetch the same pages, so without sharing every page renders 2–3× through the full middleware pipeline. Installed behind HttpDispatcher via CachingHttpHandler, this collapses that to one render per URL — every consumer replays the first render.Eviction rides the existing FileWatchDispatcher: as an IFileWatchAware with no scopes of its own, it is notified of every change another watcher already observes. On notification, it consults GetAffectedRoutes on every registered content service and evicts only the affected keys — wholesale only on a Wildcard report.
public class BuildHtmlCache
{
/// Initializes the cache with the content services it consults for affected routes on file change.
public BuildHtmlCache(IEnumerable<IContentService> contentServices)
;
/// Returns the cached response for key, invoking factory exactly once on the first request for that key. Concurrent first-requests coalesce onto the same render; a faulted render is evicted so a later request can retry.
public Task<CachedResponse> GetOrAddAsync(string key, Func<Task<CachedResponse>> factory)
;
/// Called on the file-watcher thread for every watched change. Must be quick and thread-safe.
public FileWatchResponse OnFileChanged(FileChangeNotification change)
;
}