FabrikFabrik
FabrikQuery Builder

Execution

What happens when you hit Run — how Fabrik turns the canvas graph into an APIC REST URL, picks a strategy, and streams results back through the post-processor chain.

A query on the canvas is a graph. APIC speaks REST. Fabrik is the translator: it walks the graph, decides the cheapest way to ask APIC the question, sends the HTTP call, and runs the answer through your post-processors before showing it in the results panel.

This page is about what happens between Run and the first row showing up. Most of the time you don't need to think about it. When something surprises you — a query that should be instant taking four seconds, a DN filter that was ignored — knowing the pipeline makes the surprise tractable.

What Run actually does

Clicking Run kicks off a sequence that runs entirely in your session — no backend job, no queue, no Celery. Interactive execution is synchronous from the frontend's perspective.

  1. Validate the graph. An APIC connection must be selected and an Output node must exist. Missing either and the Run button is disabled (or you get a toast error).
  2. Detect pipeline edges. If the canvas contains any pipeline edges, the request is routed to the pipeline executor (see Pipelines) instead of the single-query path. Everything below describes the single-query path.
  3. Generate the query URL. The frontend generates a fallback URL from the canvas, then asks the backend to optimize. The backend walks the node chain, picks a strategy, and returns a REST path. The frontend uses the backend URL when it comes back; if the backend call fails for any reason, it falls back to its own generated URL. Both are stored on the query state as generatedQuery and actualQueryPath — they can differ.
  4. Execute against APIC. A single HTTP call through the backend's APIC client, which reuses the cached session token for this connection (auto-refreshing if it's about to expire).
  5. Post-process. The raw response runs through each Post-Processor node on the canvas, top-to-bottom by Y position, producing the final result set.
  6. Render. The results panel opens (if it wasn't already) and shows the rows.

The two-layer URL generation

Fabrik always generates the query URL twice — once in the browser, once on the backend. Two reasons:

  • Offline / degraded mode. A simple single-node query should still run even if the optimizer is down. The frontend generator handles trivial cases.
  • Correctness at scale. Multi-node chains with containment hierarchies need the backend's MIM-aware optimizer to produce the shortest URL. The frontend doesn't have the MIM graph handy.

The backend URL wins whenever it's returned. The actualQueryPath shown in the query info panel is the one APIC actually saw.

Strategies — how the backend picks a URL shape

The backend optimizer has two strategies it auto-selects between. Each one can produce a valid URL for a given graph; the optimizer picks the cheapest one that can handle the query.

StrategyEndpointWhen it winsTypical cost
MO (Managed Object)/api/mo/<dn>.jsonEvery class in the chain has an eq filter on its key attribute, so Fabrik can construct the DN directly.~300 ms
Class/api/class/<className>.jsonAny case where the full DN can't be built. Parent filters become wcard filters on the target class's dn.1.5–4 s

The optimizer picks whichever can handle the query at the lowest estimated cost. In practice:

  • "Bridge domains under tn-prod" — every step has a name filter. MO wins.
  • "Every bridge domain with status=active" — no DN anchor. Class wins.
  • "Bridge domains under every tenant that matches .*prod.*" — wildcard on tenant, exact on BD. Class with DN wildcard.

Partial-DN optimization. When every parent class has an eq filter but the target class doesn't, MO still wins by building the URL up to the parent and tacking on a subtree-class query. The optimizer handles this automatically.

Forcing a strategy

Power users can override the optimizer from the canvas toolbar — Force Class or Force MO buttons on the Class node's settings. Useful when you're debugging an optimizer decision or you've measured and the default is wrong for your specific fabric.

The forced strategy is still validated (can_handle must return true) — you can't force MO on a query that doesn't have enough filters.

APIC filter syntax Fabrik produces

Fabrik composes APIC's filter grammar automatically; you never type it. The grammar is:

  • eq(<Class>.<prop>,"<value>") — exact match, case-sensitive.
  • wcard(<Class>.<prop>,"<regex>") — regex match (not glob).
  • and(<filter>,<filter>,…) — conjunction.
  • or(<filter>,<filter>,…) — disjunction.
  • Numeric comparisons: gt, lt, ge, le.

Multiple Filter nodes fold into a single query-target-filter expression using and(). Pattern Builder groups become nested and(or(...), or(...)) shapes. You can inspect the final expression in the query info panel — it's copy-paste valid as an APIC URL parameter.

Pagination — or not

When Pagination is on (Output node), Fabrik appends page=0&page-size=<N> to the URL and APIC paginates at the source. The results panel gets a page-picker; each page change re-hits APIC for that slice.

When Pagination is off (default), APIC returns everything matching the query in one response. For small sets this is faster. For hundreds of thousands of rows it's going to hurt — turn pagination on.

Time Machine forces pagination off. Drift detection needs the complete set, so snapshotting a paginated query doesn't work. The Output node surfaces this as a mutual exclusion.

Cancelling a running query

While a query is in flight, the Run button switches to a red Cancel (X icon). Clicking it aborts cleanly:

  • The browser sends an AbortSignal to the fetch — the HTTP connection closes.
  • The backend's APIC client receives the abort and stops waiting.
  • The canvas state resets to idle. No partial results are kept.

Cancellation is immediate on the frontend. On the backend, if APIC has already started sending a response, the connection closes but APIC doesn't know to stop computing — the request is already off to the races. That's an APIC behaviour Fabrik can't change.

The query info panel

Open the query info panel (toolbar, button) to see what Fabrik actually did:

  • Strategy — MO, Class, NodeClass.
  • Estimated cost — rough category (fast / medium / slow) based on the strategy's cost estimate.
  • Actual query path — the exact REST URL APIC received.
  • Execution time — wall-clock ms from Run to results.
  • Optimization suggestions — hints the optimizer generates when it notices you could get a faster strategy by adding a filter. For example, adding an eq(fvBD.name,...) to unlock MO.

Suggestions are advisory — nothing acts on them automatically. They're there so you can tighten a slow query without guessing.

Caching and the preview button

Class nodes carry a preview button that runs a mini-query scoped to that class and everything upstream of it. Preview caches the raw result in memory. Subsequent Post-Processor changes downstream of that class re-run against the cached data instead of re-hitting APIC.

The cache is per-session and tied to the canvas state — changing the class's filters invalidates it. It exists to make iterative post-processor tuning fast; it's not a durability mechanism.

Execution history

Every successful or failed run on the canvas is recorded in the query's execution history. The toolbar three-dot menu → Execution History opens the list: timestamp, user, duration, row count, success/failure, and for failures the error message.

For saved queries, the run is also logged to the backend audit trail so admins can see who ran what and when. Unsaved canvas runs are local-only history — they don't create audit entries.

Errors you'll see

A few shapes of failure come up regularly.

  • "No APIC connection selected." The Start node isn't bound. Either pick one, or check whether the connection you expected was scoped private to another user.
  • "No suitable query strategy found." The graph doesn't have enough information for any strategy to produce a URL. Usually means a Class node has no filter anywhere in the chain and the target class is too broad to query without one.
  • "APIC query failed: 401 Unauthorized." The saved credentials don't work against APIC. Open the APIC connection and click Test — the real error surfaces there.
  • "APIC query failed: 414 URI Too Long." The filter expression exceeded APIC's URL length. Usually happens with filter_values pipeline mode and hundreds of upstream DNs. Split the upstream or switch injection modes.
  • "Query timed out." The backend's APIC client has a per-call timeout. For genuinely large queries, turn on pagination so APIC returns chunks instead of everything in one response.
  • "Post-processor 'X' failed." One of the post-processors in the chain threw. The error includes the processor name; open that node and check its config. The raw pre-processor data is still in the cache — you can inspect it.

A note on the backend

Everything above — strategy selection, URL assembly, APIC client calls — runs on the backend. The frontend never talks to APIC directly; it talks to Fabrik's backend, which proxies the REST call and returns the result. This is why:

  • You need an APIC connection saved in Fabrik, not just cached in the browser.
  • Encrypted credentials never leave the backend.
  • Audit logging captures everything, consistently, regardless of which user ran the query.

If you're debugging an execution issue, the backend logs (docker compose logs -f backend) show the chosen strategy, the exact APIC URL, the APIC response status, and any optimizer suggestions — usually enough to pinpoint what went wrong.


Execution is mechanical once you know the path. The next page — Results — is about what the panel shows after the dust settles: tables, JSON, charts, and pagination controls.