MishkaCmsCore.Runtime.ComponentPrefetch (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Builds the components a page or a layout is about to draw, several at once, before it draws them.

A component that is not precompiled is built by the first render that names it (MishkaCmsCore.Runtime.LiveViewHelpers.component/1), and a render meets its components one after another, a child only once its parent is drawn. So the first visitor of a cold page waited for every one of them in a row. Compiling is CPU-bound, and the node has more than one core.

A page module and a layout module built by MishkaCmsCore.Runtime.Compilers.LiveViewCompiler and MishkaCmsCore.Runtime.Compilers.LayoutCompiler call prefetch/2 at the top of their render/1.

A built page does nothing

The module carries the components its own template names in a tag that says which copy it draws, as module names. When every one of them is loaded — or its last compile failed, or it is another site's — prefetch/2 answers :ok having read nothing but the loaded-code table: no file, no database, no message.

A cold one

When one is not, the module's own template is walked from what it keeps (MishkaCmsCore.Runtime.Compilers.CssClosure.reached_components/2, :references), through the component templates the boot writes, to every component it reaches, each in the copy the render draws. Those not loaded, not known to fail, and global or of the site this process renders for are built through MishkaCmsCore.Runtime.LiveViewHelpers.ensure_component_loaded/2 — the path the render takes, so the registry, the worker and MishkaCmsCore.Runtime.Handlers.CompileGate are the same ones, nothing is compiled twice, and a render or another request asking for one of them meanwhile waits for that compile. render/1 goes on once they are all done.

Its share of the node

One render builds at most slots/0 at once: config :mishka_cms_core, :component_prefetch_share of the gate's limit, rounded down and at least one — half when unset. The rest of the gate stays for every other request and site. 0 or false turns this off, and a render builds its components one after another, as it did.

What it never does

It takes no lock but a gate slot per compile, and reads no row itself: each compile reads its own in its worker, as a render's would. A component that does not compile fails as it does in a render — its failure is kept in MishkaCmsCore.Runtime.States.CompileFailureState, and the render draws the box it always draws in its place. A compile that crashes ends its task, not the page.

Events

  • runtime:compile:before_page_prefetch — before anything is built, with %{module: module, site: site_name, site_id: site_id, components: [%{name: name, site: site}]}, site the site's name for a component of its own and "Global" for a global one. A plugin answers the map with other components to build more or fewer; only those not built yet, of the site rendered for or global, are built.
  • runtime:compile:after_page_prefetch — once they are built, with %{module: module, site: site_name, site_id: site_id, built: [component], failed: [%{name: name, site: site, reason: reason}]}.

Telemetry

  • [:mishka_cms, :runtime, :component_prefetch, :start | :stop | :exception] — a span around a cold page's walk and build, with %{module: module, site: site_name, site_id: site_id}, and on :stop asked, built, failed and slots too. A built page emits nothing.

Summary

Types

A component, and the site a <.component> tag names to draw it.

Functions

Builds what owner is about to draw, unless every component in named is already built.

How many components one render builds at once: :component_prefetch_share of MishkaCmsCore.Runtime.Handlers.CompileGate.limit/0, at least one, and 0 when it is off.

Types

component()

@type component() :: %{name: String.t(), site: String.t()}

A component, and the site a <.component> tag names to draw it.

Functions

prefetch(owner, named)

@spec prefetch(module(), [{module(), String.t()}]) :: :ok

Builds what owner is about to draw, unless every component in named is already built.

owner is a page or layout module; named is the components its template names, as {module, site}, which the compiler writes into the call. Always :ok.

slots()

@spec slots() :: non_neg_integer()

How many components one render builds at once: :component_prefetch_share of MishkaCmsCore.Runtime.Handlers.CompileGate.limit/0, at least one, and 0 when it is off.