ecluse:ecluse-core
Safe HaskellNone
LanguageGHC2021

Ecluse.Core.Server.Metadata

Description

Wiring a per-request Ecluse.Core.Registry.Metadata.MetadataClient for the serve path: the cross-cutting caching, metrics, and failure-logging policy wrapped around a registry's raw fetch primitive.

The read boundary's type lives in the registry layer (agnostic); a registry's raw fetch primitive lives with that registry (npm's in Ecluse.Core.Registry.Npm.Metadata). What lives here is the serve-path policy that is the same regardless of ecosystem: whether an origin is resolved through the shared metadata cache, recording the upstream-fetch metrics, and logging a failure once in the request's context. Keeping that policy in the serve layer is what lets the registry layer stay free of the cache and telemetry.

The two operations differ in how they resolve. The full-manifest op resolves the whole packument through the shared full-packument cache. The single-version op takes a hybrid path so a cold tarball gate need not pay a whole-packument decode to consult one version (see newMetadataClient): it consults a small (package, version) cache, then the warm full-packument cache read-only (so a packument GET followed by its tarball gate still collapses to one upstream call), and only on a cold miss leads its own selective fetch -- parsing just the requested version out of the full bytes -- into the (package, version) cache, never writing the whole packument back to the shared cache.

Synopsis

Caching policy

data ManifestCaching Source #

How a read handle resolves the full manifest for one origin.

The two origins of a packument merge differ exactly here: the private origin is the per-client authority and must not be shared, while the public origin is anonymous and shared across every client.

Constructors

Uncached

Resolve directly, uncached -- the per-client private origin, re-fetched every request so the upstream re-authorises each client's own forwarded credential.

Cached MetadataCache Source

Resolve through the shared metadata cache under the origin's Source key -- the anonymous public origin, so concurrent and subsequent reads collapse to one upstream call. Both operations of the resulting handle share this one entry.

Constructing a per-request read handle

newMetadataClient :: MetricsPort -> Upstream -> ManifestCaching -> (PackageName -> MetadataError -> IO ()) -> (PackageName -> [InvalidEntry] -> IO ()) -> (PackageName -> IO ()) -> (PackageName -> IO (Either MetadataError Manifest)) -> (PackageName -> Version -> IO (Either MetadataError (Maybe PackageDetails))) -> MetadataClient Source #

Build a per-request read handle from a registry's raw fetch primitives -- one that fetches and projects the full manifest, one that fetches and selectively projects a single version -- wiring them with the caching policy, the upstream-fetch metrics, and a request-context failure log.

The full-manifest op resolves the whole packument through the shared full-packument cache. The single-version op takes the hybrid path that delivers the cheap cold tarball gate while preserving the warm install one-call property:

  1. consult the small (package, version) cache -- a hit (a positive snapshot, or a cached determined absence) returns at once;
  2. else consult the warm full-packument cache read-only -- a hit selects the one version from the shared entry (so a packument GET followed by its tarball gate is still one upstream call), and does not populate the version cache;
  3. else (cold) lead the raw single-version fetch -- which fetches the full bytes but parses only the requested version -- through the (package, version) cache's single-flight, caching the resulting snapshot (or its determined absence) there, and never writing the whole packument back to the shared cache.

For the Uncached policy (the per-client private origin) there is no shared cache to consult, so the single-version op is the raw selective fetch, uncached, re-run each request.

The failure log is invoked once per real fetch (inside the cache's single-flight leader), in the caller's logging context, so a coalesced follower never re-logs a failure the leader already reported. The dropped-entry log (logInvalid) is invoked the same way (once per real full-manifest fetch, only when the projection dropped a malformed entry), so an operator sees a degraded-but-served document without it re-logging on every cache hit.

newNpmMetadataClient :: TracingPort -> MetricsPort -> Upstream -> ManifestCaching -> (PackageName -> MetadataError -> IO ()) -> (PackageName -> [InvalidEntry] -> IO ()) -> (PackageName -> IO ()) -> NpmClientConfig -> MetadataClient Source #

Build a per-request read handle for the npm protocol over one origin's fetch configuration: the npm full-manifest and single-version fetches as the raw primitives, with the serve-path caching, metrics, and the failure and dropped-entry logs wired by newMetadataClient.