<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Theodore P.</title>
    <description>The latest articles on DEV Community by Theodore P. (@theodore_p_9749548f7dd03).</description>
    <link>https://dev.to/theodore_p_9749548f7dd03</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2192744%2F3caf6e4b-c4fb-4987-8fe8-6eb07542622f.jpg</url>
      <title>DEV Community: Theodore P.</title>
      <link>https://dev.to/theodore_p_9749548f7dd03</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/theodore_p_9749548f7dd03"/>
    <language>en</language>
    <item>
      <title>Verification-first architecture for language-model repair pipelines.</title>
      <dc:creator>Theodore P.</dc:creator>
      <pubDate>Thu, 27 Aug 2026 15:00:41 +0000</pubDate>
      <link>https://dev.to/theodore_p_9749548f7dd03/building-a-verification-first-repair-harness-2d11</link>
      <guid>https://dev.to/theodore_p_9749548f7dd03/building-a-verification-first-repair-harness-2d11</guid>
      <description>&lt;h1&gt;
  
  
  The Harness Is All You Need
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Verification-first architecture for language-model repair pipelines.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A method for putting a language model inside a maintenance system without letting it decide anything.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;A large class of engineering maintenance work has the same shape. A small machine-readable rule, which locates or transforms a value inside an input the system does not control, silently stops producing correct output because the input changed. The rule is short, the failures are many, and a competent engineer can repair any single case in minutes once the evidence is in front of them. The cost is not difficulty. It is volume and triage.&lt;/p&gt;

&lt;p&gt;This is an attractive target for a language model and a dangerous one. The model can propose a repair from evidence, but a wrong repair is worse than no repair, because it produces plausible output that no alarm fires on. The naive pipeline, which collects failures, shows them to a model and applies the answers, fails in three independent ways at once, and every one of those failures is invisible in aggregate metrics.&lt;/p&gt;

&lt;p&gt;This document describes a harness that makes the approach work. It is derived from a production deployment that maintains roughly 1,400 such rules against inputs from as many independent external sources, covering about 1.6 million monitored items, and it has been checked against two further domains. The vocabulary is deliberately abstract so that the method can be mapped onto any problem with the same shape.&lt;/p&gt;

&lt;p&gt;The central claim is measurable and, in that deployment, measured. &lt;strong&gt;The quality of such a system is governed by the oracle and by the evidence given to the generator, not by the capability of the generator.&lt;/strong&gt; Tripling model size produced no usable improvement. Constraining the prompt and narrowing the evidence roughly doubled precision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;When this method applies&lt;/li&gt;
&lt;li&gt;Terminology&lt;/li&gt;
&lt;li&gt;Five invariants&lt;/li&gt;
&lt;li&gt;Reference architecture&lt;/li&gt;
&lt;li&gt;Stage A. Attribution&lt;/li&gt;
&lt;li&gt;Stage B. Compression&lt;/li&gt;
&lt;li&gt;Stage C. Generation&lt;/li&gt;
&lt;li&gt;Stage D. The oracle&lt;/li&gt;
&lt;li&gt;Stage E. The human gate&lt;/li&gt;
&lt;li&gt;Designing the outcome taxonomy&lt;/li&gt;
&lt;li&gt;Evaluation protocol&lt;/li&gt;
&lt;li&gt;Diagnosing failure: model or evidence?&lt;/li&gt;
&lt;li&gt;Cost model&lt;/li&gt;
&lt;li&gt;Anti-patterns&lt;/li&gt;
&lt;li&gt;Design checklist&lt;/li&gt;
&lt;li&gt;Instantiating the method in a new domain&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. When this method applies
&lt;/h2&gt;

&lt;p&gt;The method fits a problem when six conditions hold together. If any of them fails, a different design is appropriate, and section 16 says which.&lt;/p&gt;

&lt;p&gt;The first condition is that &lt;strong&gt;the rule is small and structured&lt;/strong&gt;. It must be short enough to generate in full and, more importantly, short enough to verify exhaustively rather than by sampling.&lt;/p&gt;

&lt;p&gt;The second is that &lt;strong&gt;correctness is machine-checkable against evidence you already hold&lt;/strong&gt;. This is the load-bearing condition. If you cannot decide, mechanically, whether a proposed repair is correct using evidence already in your possession, you do not have this problem. You have a research problem. Do not build a harness; build the oracle first, then reconsider whether the harness is still needed.&lt;/p&gt;

&lt;p&gt;The third is that &lt;strong&gt;failures are numerous and repetitive&lt;/strong&gt;. Below some volume a human repairs them faster than you can build the machinery, and the machinery will be obsolete before it pays for itself.&lt;/p&gt;

&lt;p&gt;The fourth is that &lt;strong&gt;a wrong repair is silent&lt;/strong&gt;: it produces output, just wrong output. This is what separates the problem from code generation. A generated function that is wrong usually throws, fails a test, or refuses to compile. A wrong rule returns a value. The pipeline stays green and the data quietly rots. Every design decision in this document follows from taking that seriously.&lt;/p&gt;

&lt;p&gt;The fifth is that &lt;strong&gt;the evidence for a repair is local&lt;/strong&gt;, meaning one or two examples suffice. If a repair requires reasoning across the whole system, the generator has the wrong shape and no harness will fix that.&lt;/p&gt;

&lt;p&gt;The sixth is that &lt;strong&gt;a human can adjudicate a proposal in under a minute&lt;/strong&gt; given the right display. The gate is deliberate, but it must not become the bottleneck, and whether it does is a property of your interface rather than of your reviewers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; In our deployment the oracle predated the model by a year and could be replayed over historical evidence at will. That single fact is why the project was tractable at all: every design question could be settled by re-running the oracle over inputs already on disk, and no question required a new data collection.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Terminology
&lt;/h2&gt;

&lt;p&gt;The vocabulary below is used consistently throughout. It is worth fixing before the design discussion begins, because most disagreements about systems of this kind turn out to be disagreements about which component is being described.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;rule&lt;/strong&gt; is the small structured object being repaired. A &lt;strong&gt;source&lt;/strong&gt; is the external system whose inputs the rule is applied to, and which changes without warning or coordination. An &lt;strong&gt;instance&lt;/strong&gt; is one failing case, meaning a rule together with the evidence of its failure, and &lt;strong&gt;evidence&lt;/strong&gt; is the stored input on which the rule produced wrong output or none at all.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;generator&lt;/strong&gt; is the model, in its single narrow role: evidence in, candidate rule out. The &lt;strong&gt;oracle&lt;/strong&gt; is deterministic code that decides whether a candidate is acceptable, and a &lt;strong&gt;verdict&lt;/strong&gt; is the oracle's typed judgement on one candidate. A &lt;strong&gt;gate&lt;/strong&gt; is a particular kind of oracle check, one that can only demote a verdict and never promote it. A &lt;strong&gt;trial&lt;/strong&gt; is empirical evaluation of a candidate against a held-out population of real instances. The &lt;strong&gt;adjudicator&lt;/strong&gt; is the human who approves or rejects, and is the only writer to production. The &lt;strong&gt;harness&lt;/strong&gt; is everything except the generator.&lt;/p&gt;

&lt;p&gt;The asymmetry in that list is deliberate and should be stated explicitly. The generator has one job and no authority. Everything that decides anything is either deterministic code or a person.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; We introduced the word "gate" only after the second production incident, when it became clear that the team was arguing about whether a check should be able to rescue a candidate. Naming the demote-only property made the answer obvious and closed the argument permanently.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Five invariants
&lt;/h2&gt;

&lt;p&gt;State these in your design document and test them. They are not style preferences. Each one was learned by violating it.&lt;/p&gt;

&lt;h3&gt;
  
  
  I1. Deterministic before generative
&lt;/h3&gt;

&lt;p&gt;Every filtering, grouping, classification and verification step is ordinary code. The generator is invoked only on instances that deterministic code could not dispose of, and only for the one judgement that genuinely requires reading unstructured evidence.&lt;/p&gt;

&lt;p&gt;The consequence is that the generator sees a tiny fraction of the input. This is what makes the economics work, and it is also what makes the results interpretable: when quality moves, you know which stage moved it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; In one production run, 435,702 failing cases entered the pipeline and 137 pieces of evidence reached the model. That is roughly one in three thousand. Everything else was disposed of by rules that cost nothing to run.&lt;/p&gt;

&lt;h3&gt;
  
  
  I2. The oracle runs the production code path
&lt;/h3&gt;

&lt;p&gt;Verify a candidate by executing the same code that will consume it in production, with the same parser, the same normalisation and the same coercion. Never a reimplementation, never an approximation, and never "the model says it matches".&lt;/p&gt;

&lt;p&gt;The consequence is that a candidate which passes verification passes because production would pass. A reimplemented oracle drifts from production silently, and its drift is indistinguishable from a model failure, which contaminates every subsequent measurement.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Our generation loop needed a fast in-process check for speed, so a second, lighter implementation was unavoidable. Rather than assume the two agreed, we measured: 3.2% of candidates were decided differently by the two parsers. Small enough to tolerate, and large enough that assuming zero would have been a mistake we could not have detected later.&lt;/p&gt;

&lt;h3&gt;
  
  
  I3. Every claim is downgraded to a measurement
&lt;/h3&gt;

&lt;p&gt;Do not let a qualitative judgement survive into the pipeline. "The candidate looks right" becomes a verdict from the oracle. The verdict becomes a hit rate over a held-out population. The hit rate becomes a comparison against what production currently achieves on the same evidence.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Three candidates in one review batch carried an identical verdict. Under trial, the first was a genuine repair, moving from zero successes in fifty to fifty in fifty. The second was pure churn: thirty-three successes in thirty-three both before and after, with byte-identical output. The third had no testable population at all, so no evidence existed in either direction. Three correct decisions, three different actions, one verdict. Without the trial layer all three would have been treated alike.&lt;/p&gt;

&lt;h3&gt;
  
  
  I4. Precision over recall, deliberately
&lt;/h3&gt;

&lt;p&gt;In a human-gated system the scarcest resource is the adjudicator's trust. A queue at high precision gets reviewed. A queue at low precision gets ignored, and once ignored it is dead regardless of what it contains.&lt;/p&gt;

&lt;p&gt;The consequence is that most tuning should make the generator answer less often. Design the contract so that "no answer" is an explicitly correct response, and say so in the prompt rather than hoping it is inferred.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Across our whole sequence of prompt changes, the count of genuinely good candidates barely moved, staying between eight and eleven on a fixed evaluation set, while precision rose from 40% to 91%. The entire gain was the model learning to decline. Nothing we did made it better at finding answers; everything we did made it better at not inventing them.&lt;/p&gt;

&lt;h3&gt;
  
  
  I5. Decisions are durable
&lt;/h3&gt;

&lt;p&gt;An approval is never silently overwritten by a later run. A rejection is never resurrected: the same candidate must not reappear as though it were new, and a genuinely different candidate for the same slot should arrive annotated with what was already rejected there.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Before we added rejection memory, a monthly cycle re-proposed the same rejected candidates every month. Reviewers learned within two cycles that the queue contained things they had already dismissed, and review throughput fell accordingly. The fix was a few lines of persistence and it recovered more reviewer time than any model change we ever made.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Reference architecture
&lt;/h2&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    IN["Failing instances"] --&amp;gt; A
    A["A. ATTRIBUTION&amp;lt;br/&amp;gt;metadata only, no evidence decoded"] --&amp;gt; Aq{"Can repairing the&amp;lt;br/&amp;gt;rule fix this?"}
    Aq --&amp;gt;|no| ROUTE["Routed to the owning&amp;lt;br/&amp;gt;discipline, with the reason"]
    Aq --&amp;gt;|yes| B
    B["B. COMPRESSION&amp;lt;br/&amp;gt;group by structural equivalence"] --&amp;gt; Bq{"Is this evidence&amp;lt;br/&amp;gt;a valid basis&amp;lt;br/&amp;gt;for a repair?"}
    Bq --&amp;gt;|no| EXCL["Excluded, reason recorded"]
    Bq --&amp;gt;|yes| C
    C["C. GENERATION&amp;lt;br/&amp;gt;focused evidence to candidate"] --&amp;gt; CC["critique loop&amp;lt;br/&amp;gt;(mechanical, one round)"]
    CC --&amp;gt; D
    D["D. ORACLE&amp;lt;br/&amp;gt;production code path + gates"] --&amp;gt; Dq{"verdict"}
    Dq --&amp;gt;|not acceptable| FILE["Filed with a typed verdict"]
    Dq --&amp;gt;|acceptable| Q["Review queue"]
    Q --&amp;gt; E["E. ADJUDICATION&amp;lt;br/&amp;gt;evidence displayed, trial on demand"]
    E --&amp;gt;|approve| PROD["Production rule"]
    E --&amp;gt;|reject| MEM["Durable rejection"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;em&gt;Figure 1. The five stages, and the single path into production.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two properties of this structure matter more than the individual stages. The first is that the funnel is monotone in cost: each stage costs more per instance than the one before it, so each stage must remove work rather than add it. If a stage does not reduce the population by roughly an order of magnitude, it is not earning its place and should be merged into its neighbour. The second is that exactly one arrow reaches production, and a person is standing on it. Everything else writes to a queue, a file, or a log.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Stage A. Attribution
&lt;/h2&gt;

&lt;p&gt;The purpose of this stage is to partition failures by which discipline can fix them, using metadata alone, with no evidence decoded and no model invoked. It is the highest-value stage relative to its cost, and it is almost always skipped by teams who begin from "we have a lot of failures, let us use a language model".&lt;/p&gt;

&lt;p&gt;Four rules govern its design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buckets must be mutually exclusive and exhaustive.&lt;/strong&gt; Compute them with a single first-match-wins rule chain rather than with independent conditions. Independent conditions allow one failure to count in two buckets and allow some failures to count in none, and both errors make every downstream number wrong. Assert that the buckets sum to the total, and fail loudly when they do not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Order the chain by ownership rather than by frequency.&lt;/strong&gt; The question the chain answers is "who fixes this?", so the most specific owner should be tested first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each bucket names a team, not a symptom.&lt;/strong&gt; "Rejected" is not a bucket. "Access control, owned by the anti-abuse team" is a bucket, because it tells you where the work goes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bucket with no owner is a bug in the taxonomy.&lt;/strong&gt; Add an explicit catch-all and watch it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Across one estate, 78% of failures attributed to something other than the rule: access denials, entities that had been deleted at the source, and infrastructure faults. In a single run, 29,852 failures traced to one unreachable gateway, which was a five-minute configuration fix. Had those cases reached the model, it would have produced confident and useless repairs for a problem no repair could touch. Separately, when we replaced four independent bucket conditions with one exclusive chain, 4,307 previously uncounted failures appeared in the catch-all. They had been invisible for months, not because anyone hid them, but because no condition matched them and nothing asserted that the buckets summed to the total.&lt;/p&gt;

&lt;p&gt;One blind spot must be documented rather than solved here. A failure can look healthy at the metadata level and be semantically dead, for example an input that announces success and contains only a notice that the entity no longer exists. Metadata cannot see that. Stage B must.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Stage B. Compression
&lt;/h2&gt;

&lt;p&gt;The purpose of this stage is to collapse N failing instances into K distinct modes, where K is the number of genuinely different repairs required. Four rules govern it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Group by structure, never by content.&lt;/strong&gt; Two instances belong together when the shape of the evidence matches: the vocabulary of identifiers, the schema, the template. Content differs within one mode by definition, so keying on content makes every instance its own mode and the stage accomplishes nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard-split before you cluster softly.&lt;/strong&gt; Partition first on cheap categorical facts, then apply similarity only within each partition. This prevents a similarity threshold from merging categories that are qualitatively different but superficially close.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take one representative per mode, and carry a sibling.&lt;/strong&gt; The representative is what the generator sees. The sibling, a second instance of the same mode, is what makes the resulting candidate testable for generality rather than memorisation. This rule is not optional. A candidate validated on exactly one example is indistinguishable from a candidate that encodes that example, and requiring it to hold on a second instance is the cheapest generality test available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classify the representative's validity, and exclude with a recorded reason.&lt;/strong&gt; Not every piece of evidence is a legitimate basis for repair. Evidence drawn from the wrong kind of entity produces a rule that is correct for that evidence and wrong forever afterwards.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; One run compressed 26,481 failing instances to 114 representatives, in 88 seconds of ordinary computation. Most sources had exactly one failure mode; a minority had two or three genuinely distinct variants, and that minority is precisely the population a naive one-instance-per-source deduplication would have mis-repaired without ever revealing that it had. In the same run, 424 of 561 candidate groups were excluded because their evidence was the wrong kind: listings rather than single entities, notices rather than content. Our highest-ranked source by raw failure count was one of them. It had thousands of failures, and no repair existed, because 99.9% of its evidence did not contain the value at all. Ranking by raw failure count points at the loudest source. Ranking by attributable, valid failures points at the fixable one.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Stage C. Generation
&lt;/h2&gt;

&lt;p&gt;The purpose of this stage is narrow by construction: given focused evidence and the current rule, propose a candidate or decline.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 Evidence selection is the highest-leverage variable
&lt;/h3&gt;

&lt;p&gt;Do not hand the model the whole input. Anchor on the strongest signal for the value you want, expand to a bounded context around that anchor, and attach only the structured metadata that could plausibly carry the answer.&lt;/p&gt;

&lt;p&gt;This is not primarily a cost optimisation. The excluded region is where the wrong answers come from: repeated blocks, adjacent entities, alternate renderings of the same value. Removing that region removes a class of error rather than trading it for another.&lt;/p&gt;

&lt;p&gt;The corresponding hazard is real and must be handled explicitly, because a focused view that omits the answer is worse than an unfocused one. Fall back to the full input whenever the anchor is not found with confidence, and measure how often the answer lies outside the focused region.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Our focused view is roughly 5% of the input. When we finally measured the hazard rather than assuming it away, we found that for one of the two fields we repair, the correct value lay outside the focused region on about one instance in ten. On those instances the model mostly declined, which cost recall and protected precision. That measurement converted a suspicion into a stated bound, and it also identified the single highest-value improvement available to us, which is widening the anchor for that field rather than changing anything about the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 The contract
&lt;/h3&gt;

&lt;p&gt;The prompt is a contract rather than a request, and it must state five things.&lt;/p&gt;

&lt;p&gt;It must state &lt;strong&gt;the task as repair, not discovery&lt;/strong&gt;. "This rule stopped working, here is the evidence, what should it be" outperforms "find the value", because the framing keeps the model anchored to the semantic slot the original rule meant rather than to any plausible-looking value in the input.&lt;/p&gt;

&lt;p&gt;It must state &lt;strong&gt;the current rule and the observed failure&lt;/strong&gt;, which are context the model cannot infer.&lt;/p&gt;

&lt;p&gt;It must state &lt;strong&gt;hard constraints as prohibitions with reasons&lt;/strong&gt;: uniqueness, generality across instances, and forbidden constructs such as positional indexes, instance identifiers and state-dependent conditions. Give the reason for each. A constraint with a stated failure mode is followed more reliably than a bare rule.&lt;/p&gt;

&lt;p&gt;It must state &lt;strong&gt;that declining is a correct answer&lt;/strong&gt;, explicitly, with an example of when. This single clause carries a large share of the precision gain described in I4.&lt;/p&gt;

&lt;p&gt;It must specify &lt;strong&gt;a machine-parseable output shape&lt;/strong&gt;, with a place for the model to record what it saw and why. That rationale is not for the pipeline. It is for the adjudicator.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Two constraints in our contract exist because of specific incidents. The prohibition on positional constructs was added after a candidate that counted siblings returned an unrelated value the moment an optional element appeared. The prohibition on state-dependent conditions was added after a candidate that keyed on a status marker worked perfectly while the entity stayed in that state and returned nothing, silently, the moment it changed. Neither would have been caught by a check on the evidence in front of us, because on that evidence both were correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 Determinism
&lt;/h3&gt;

&lt;p&gt;Fix the sampling temperature at zero and pin the seed. Without this you cannot attribute a metric change to a design change, and every comparison is contaminated by sampling noise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Before pinning, two runs of an identical configuration disagreed on 4 of 46 slots. That is enough to move a headline number by two points, which is the same magnitude as several of the design changes we were trying to evaluate. Residual nondeterminism from batched inference remained even after pinning; we measured it once, recorded it, and stopped worrying about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.4 The critique loop, exactly one round
&lt;/h3&gt;

&lt;p&gt;After the first answer, run the cheap mechanical checks. If any fail, return them to the model together with the evidence of what its candidate actually did, not a bare "invalid" but a statement of the form "it matched these three things, whose contents are X, Y and Z". Allow exactly one revision.&lt;/p&gt;

&lt;p&gt;The reason for exactly one is that iterating against the same evidence teaches the model to satisfy that evidence, which is overfitting with extra steps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; In our measurements the second round mostly produces withdrawal to a null answer rather than a better candidate. Under I4 withdrawal is a win, so the round pays for itself without ever needing to produce a repair. When we later gave a much larger model the same feedback on the same cases, it did not do better, which is the subject of section 12.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.5 A note on reasoning budgets
&lt;/h3&gt;

&lt;p&gt;If your generator supports an explicit reasoning phase, be aware of a specific failure mode. When reasoning and answer draw from one output budget, the reasoning expands to consume the entire budget and the model returns nothing.&lt;/p&gt;

&lt;p&gt;The fix is a separate hard limit on the reasoning phase, leaving the answer budget intact. The general lesson outlives this one setting: verify that "more of a good thing" is monotone before assuming it, particularly where two behaviours draw from a shared pool.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; We enabled reasoning with a generous budget and got 19 empty answers out of 23. Assuming the budget was too small, we raised it, and the failure got worse rather than better, because the reasoning simply expanded to fill whatever it was given. Bounding the reasoning phase separately at a few hundred tokens produced the best configuration we have measured, better than no reasoning at all, and it also stopped the model proposing state-dependent constructs without being told to.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Stage D. The oracle
&lt;/h2&gt;

&lt;p&gt;The oracle is the component that makes the system trustworthy, and it deserves more design attention than the prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 Layers
&lt;/h3&gt;

&lt;p&gt;Run the checks in increasing order of cost and stop at the first that rejects.&lt;/p&gt;

&lt;p&gt;Begin with &lt;strong&gt;syntax&lt;/strong&gt;, which asks whether the candidate parses or compiles at all. Then &lt;strong&gt;uniqueness&lt;/strong&gt;, which asks whether it resolves to exactly one thing. Then &lt;strong&gt;value&lt;/strong&gt;, which asks whether the production consumer accepts what the candidate yields. Then &lt;strong&gt;generality&lt;/strong&gt;, which asks whether it also holds on the sibling instance and on other evidence from the same source. Then &lt;strong&gt;form&lt;/strong&gt;, which asks whether it violates a stated constraint such as an embedded instance identifier or a positional index, and which is nearly free because it is a property of the candidate's text rather than of its execution.&lt;/p&gt;

&lt;p&gt;Two further checks are more expensive and are described separately below, because they are the ones most systems omit.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 Gates demote, never promote
&lt;/h3&gt;

&lt;p&gt;A gate can only lower a verdict, never raise one. This asymmetry is what lets you add gates over time without re-validating everything that came before: a new gate can only make the queue more conservative, so its introduction can never invalidate a past approval.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 The two gates people forget
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The regression gate&lt;/strong&gt; requires something most pipelines do not keep, namely evidence of success. Store, for each source, the most recent input on which the rule worked, together with the value production derived from it. Without that store, every candidate is evaluated only where the incumbent fails, which is the single place where the incumbent is guaranteed to lose, and the system will ship candidates that repair the broken minority while breaking the working majority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The null-hypothesis gate&lt;/strong&gt; asks whether the incumbent already works on the failing evidence. If it does, this rule is not the cause of the failure, something else is, and the candidate should be demoted with a pointer to the real question.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; The success store was the single highest-value addition we made after the first production run. It is inexpensive, being one bounded, compressed input per monitored item with a two-week lifetime, and it immediately caught thirteen candidates in one run that would otherwise have shipped. It also supplies the population for the trial in stage E, so one storage decision paid for two features. The null-hypothesis gate, added at the same time, reclassified a large fraction of a subsequent run's queue: those cases were failing on the other field, and every minute spent reviewing them as repairs was wasted.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.4 Merging, not overwriting
&lt;/h3&gt;

&lt;p&gt;If a source can have several modes, several candidates will arrive for the same slot. Do not key your queue by source alone.&lt;/p&gt;

&lt;p&gt;The correct behaviour is to verify everything first and then merge per slot. Identical candidates merge with their impact summed. Genuinely different candidates either compose, when the consumer supports ordered alternatives, in which case compose them and verify the composition as a unit, or are published as one winner with the alternatives attached and visible to the adjudicator.&lt;/p&gt;

&lt;p&gt;A corollary applies as soon as more than one entry point feeds the queue. The queue key must include the entry point, or one stream's run will silently retire the other stream's work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; We keyed the queue by source and slot. One source had four modes and therefore four candidate repairs, which overwrote one another in arrival order. The survivor came from the smallest mode, representing 18 failing cases, while three larger repairs covering 252 cases vanished without any record that they had existed. After the fix, the same source published a composed candidate whose honest impact was 189 cases, and which the trial confirmed at fifty successes in fifty. Separately, when we later added a second entry point, its first run retired the first entry point's pending work, because both wrote the same keys. The review queue emptied overnight. Both incidents were schema decisions made in week one and paid for in week three.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Stage E. The human gate
&lt;/h2&gt;

&lt;p&gt;The adjudicator is part of the system, and the display is part of the design. The card should show evidence rather than conclusions.&lt;/p&gt;

&lt;p&gt;It should show the current rule and the candidate side by side, with what each yields on the same evidence. It should show the counts behind the verdict, meaning how many things matched and how many sibling instances the candidate held on. It should show the impact, summed honestly across merged modes rather than reported from whichever mode happened to survive. It should show the reason string for any demotion, phrased so a reviewer can argue with it. It should link to the raw evidence and state that evidence's classification. Finally, it should show the model's self-reported confidence in visually de-emphasised form, because it is the only unverified number present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the candidate editable before approval.&lt;/strong&gt; A large share of near-miss candidates need one qualifier added. An editable field converts them from rejections into repairs in seconds, and it is the cheapest recall you will ever buy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approval must be minimal and targeted.&lt;/strong&gt; Write exactly one field, to the location the consumer actually reads, and record on the approval record the exact path written.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; We shipped a version in which approval wrote to a location the consumer never read for one whole class of sources. The review showed green, the record said approved, and production behaviour did not change at all. Nothing failed, nothing alerted, and the only reason we found it was that a later audit compared what had been approved against what production was doing. Since then, every approval records its own write path, and the audit is a one-line query.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.1 The trial
&lt;/h3&gt;

&lt;p&gt;The strongest instrument available to the adjudicator runs both the incumbent and the candidate over a sample of real instances drawn from both populations, meaning instances that currently fail and instances that currently succeed, and reports per-instance results.&lt;/p&gt;

&lt;p&gt;Compute a verdict from that comparison and persist it, so it survives the session. Five outcomes suffice: &lt;em&gt;gain&lt;/em&gt;, where the candidate wins on failing evidence and loses nothing; &lt;em&gt;no gain&lt;/em&gt;, where behaviour is identical to the incumbent and the change is therefore churn; &lt;em&gt;loss&lt;/em&gt;; &lt;em&gt;regression&lt;/em&gt;; and &lt;em&gt;unverifiable&lt;/em&gt;, where no testable population exists.&lt;/p&gt;

&lt;p&gt;The trial is the only stage that samples instances nobody selected, which is exactly why it catches what every earlier stage missed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Our trial samples fifty failing and fifty working instances per candidate. It is also the mechanism that rescues a class of candidate the oracle demotes: when the regression gate reports that a candidate returns a &lt;em&gt;different&lt;/em&gt; value on working inputs rather than no value, that is sometimes precisely the intent, because the incumbent had been reading an adjacent value all along. The only approved repair of that shape in our system was demoted by the oracle, rescued by the trial, and confirmed correct in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Designing the outcome taxonomy
&lt;/h2&gt;

&lt;p&gt;Verdicts are the interface between the harness and the human, so design them as a typed enumeration with an action attached to each. Four rules apply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every verdict names an action.&lt;/strong&gt; If two verdicts imply the same action, merge them. If one verdict implies "it depends", split it until it does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distinguish "produces nothing" from "produces something different".&lt;/strong&gt; These look alike in a diff and are opposites in meaning. The first is a break. The second is frequently the entire point of the repair.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Include a verdict for "not a repair at all".&lt;/strong&gt; Some instances are telling you that the entity is gone, the reference is stale, or the access is broken. A taxonomy without that bucket forces them into a repair verdict, where they are reviewed forever and never resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A verdict says the rule is valid, not that the action is wise.&lt;/strong&gt; A candidate can be perfectly correct and still derive a value from evidence you should not be processing at all.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Two candidates in our queue were marked acceptable by every check and were correct in the strict sense. One faithfully returned a sentence stating that the entity no longer existed; the other faithfully returned a notice that the caller was not authorised to see it. Both were flawless rules applied to evidence we should never have collected. The display now carries the evidence classification next to the verdict, because no verdict can express this on its own. Our regression verdict still conflates the two meanings named above and separates them only in its reason string; in hindsight they should have been two verdicts, and that is the change we would make first.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Evaluation protocol
&lt;/h2&gt;

&lt;p&gt;Most reported numbers for systems of this kind are not comparable across time, because the checks change as the system improves. Fix that before anything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.1 Re-score history with today's oracle
&lt;/h3&gt;

&lt;p&gt;Keep every run's raw generator output. Score all runs, always, with the current oracle. A run from month one and a run from month three then differ only in what changed by design.&lt;/p&gt;

&lt;p&gt;This has a consequence people find uncomfortable and should not avoid. A stricter oracle makes past runs look worse, and it makes the current run look worse than last month's. That is the metric working correctly. A benchmark that only ever improves is measuring your optimism.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Adding the regression gate dropped a headline precision figure from 51% to 49% on comparable populations, because the gate rejected candidates the previous run had happily called acceptable. We published the drop with the explanation attached. The alternative, quoting the older number because it was more flattering, would have meant reporting a quality improvement that consisted entirely of a weaker test.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.2 Define precision to exclude declining
&lt;/h3&gt;

&lt;p&gt;Precision is acceptable candidates divided by candidates proposed. An instance the generator declined is not counted against it, because declining on evidence with no valid answer is correct behaviour, and penalising it would optimise directly against I4.&lt;/p&gt;

&lt;p&gt;Report the decline rate separately. It is a real quantity, since it bounds recall, but it is not an error rate and must not be folded into one.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.3 Two populations, reported separately
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;laboratory&lt;/strong&gt; population is a fixed, hand-picked set of twenty to fifty instances, used to attribute the effect of one change at a time. It is not representative and must never be quoted as system performance. Its job is causal attribution, and for that job a small fixed set is exactly right.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;field&lt;/strong&gt; population is unselected production traffic. This is the honest number, and it will be lower.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Our laboratory figure is 91%, on twenty-three instances chosen because a repair was known to exist in each. Our field figures are 76% where the evidence is healthy and 49% where the evidence is already failing. All three are true, all three are quoted in different documents, and each one answers a different question. Quoting the laboratory number as system performance would have been the single most misleading thing we could have done, and it would have been entirely defensible from the raw data.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.4 Stratify by difficulty, and say why the strata differ
&lt;/h3&gt;

&lt;p&gt;Do not average across populations of different difficulty. Say what makes them differ, in terms of the evidence rather than in terms of the outcome.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Our two entry points differ structurally. One draws evidence where the rule already fails, which is hard, because a substantial share of that evidence has no valid answer at all: the entity is gone, or the value was never present. The other draws evidence where the system is healthy, which is easier, because the value is present and a reference value exists for comparison. The gap between 49% and 76% is almost entirely that difference, and averaging the two would produce a number describing no workload we actually run.&lt;/p&gt;

&lt;h3&gt;
  
  
  11.5 One change at a time, with the question written down
&lt;/h3&gt;

&lt;p&gt;For each experiment record five things: the question it tests, the single change made, the population it ran on, the result, and the decision taken. A sequence of such records is the most useful artifact your project will produce. It stops the team re-running a dead end six months later, and it lets a newcomer see that the gains came from constraints rather than from scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Diagnosing failure: model or evidence?
&lt;/h2&gt;

&lt;p&gt;When precision plateaus, the instinct is to reach for a larger generator. Test that instinct, because it is cheap to test and it is usually wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.1 First, categorise every failure mechanically
&lt;/h3&gt;

&lt;p&gt;Take a full run's rejected candidates and bucket them by why the oracle rejected them, then compute the agreement rate on the accepted ones.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Across 442 rejections in one run, just under a third, 31%, resolved to nothing at all: a guessed chain of identifiers matching nothing in the evidence. A further 37% resolved to several things, meaning the candidate was not scoped to the intended entity and would silently read a neighbouring one. Both are generator failures, and both are aggravated, because the model had already been told in its revision round exactly what its candidate matched. A further 13% violated a form constraint stated in the prompt, typically a positional index or an embedded identifier. Another 9% resolved to exactly one thing whose content was not a value at all but a label, an identifier or an unrelated attribute. A residual 2% did not parse.&lt;/p&gt;

&lt;p&gt;The remaining 3% were ours: a defect in our own numeric coercion, which read a comma-decimal amount as an integer a hundred times larger for sources using European conventions. No model change would have fixed those, and without the categorisation we would have attributed them to the model and tuned the prompt against our own bug.&lt;/p&gt;

&lt;p&gt;Two conclusions followed immediately. First, 68% of the failures were candidates the generator's own in-loop check had already flagged, and the model returned them regardless. Those never reach a human, so queue quality is unaffected, but they establish that the ceiling is bounded by the model's compliance rather than by its ability to find answers. Second, where candidates were accepted, they reproduced the value production itself derived from the same evidence in 98.5% of cases, which established that the evidence was sound and the failures were not a data-quality problem wearing a disguise.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.2 Then run the escalation experiment
&lt;/h3&gt;

&lt;p&gt;There is a cheap and decisive test of the "bigger model" hypothesis. Take only the candidates the oracle rejected. Send each to a larger or differently-trained generator, with the full context: the same evidence, the first generator's answer, and the oracle's explanation of exactly what that answer did. Score with the same oracle, and report four outcomes: repaired, withdrawn, still failing, and damaged, where damaged means slots that were correct before and are wrong now.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; We ran this with a model roughly three times the size of our production one, trained specifically on code. On healthy evidence, 129 failing slots were sent; 28 of them, or 22%, were repaired; 4 were withdrawn; 97 remained wrong; and 24 slots that had been working were damaged. On failing evidence, 34 slots were sent and none at all were repaired: 14 were withdrawn, 20 remained wrong, and 5 working slots were damaged. The larger model made the same two mistakes as the smaller one, resolving to nothing and resolving to several things, while looking at an explicit list of what its predecessor's candidate had matched. Twelve minutes of computation and two dollars settled a question the team had been arguing about for weeks.&lt;/p&gt;

&lt;p&gt;The inference is that the limiting factor was the evidence window rather than the parameter count. The information needed to disambiguate lay outside the focused region we were sending. That is a retrieval and context problem, and it is fixed by widening and structuring the evidence, not by scaling the generator.&lt;/p&gt;

&lt;h3&gt;
  
  
  12.3 Escalate per-slot, never per-instance
&lt;/h3&gt;

&lt;p&gt;If you do escalate, apply the second generator only to the specific slot that failed, and merge its answer back into the existing record.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; Letting the second model rewrite whole instances made the system worse, moving it from 50% to 46% on the same population, entirely through collateral damage to slots that had been correct and were not in question. Restricting it to the failing slot and merging moved the same population from 75% to 80%. The difference between those two outcomes is not the model. It is the scope it was allowed to touch.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Cost model
&lt;/h2&gt;

&lt;p&gt;At realistic volumes, cost is dominated by fixed setup rather than by inference, and the intuition that inference is the expensive part leads directly to the wrong optimisations.&lt;/p&gt;

&lt;p&gt;Attribution and compression are effectively free, running in seconds to minutes with no model involved. Generator provisioning is the dominant fixed cost. Inference itself costs cents and a few seconds per instance. The oracle is free. Adjudication is the real cost, and it is measured in human minutes rather than in currency.&lt;/p&gt;

&lt;p&gt;Five consequences follow. &lt;strong&gt;Batch aggressively&lt;/strong&gt;, because two runs of a hundred instances cost far more than one run of two hundred. &lt;strong&gt;Parallelise by sharding the input&lt;/strong&gt; rather than by scaling the machine, since several modest workers on several shards beat one large worker. &lt;strong&gt;Keep the environment warm&lt;/strong&gt; across iterations during development, where the setup cost would otherwise be paid on every experiment. &lt;strong&gt;Make teardown independent of your session&lt;/strong&gt;, because a crashed terminal must never leave a meter running: track resources by an external identifier, provide a one-command teardown, and, because local state files lie, verify against the provider's own interface that nothing is still running. Finally, &lt;strong&gt;optimise the adjudicator's minute rather than the inference cent&lt;/strong&gt;, because at our volumes review time exceeds compute cost by orders of magnitude, and every display improvement that removes a click is worth more than a model change that removes a fraction of a cent.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; On a typical run of ours, environment setup and model loading consumed 21 of 46 billed minutes. The inference for the whole run cost less than a dollar. Separately, a state file recording a single rented machine led us to believe a teardown had succeeded when three machines were still running, one of them expensive; only an audit against the provider's own listing revealed it. Both lessons are cheap to learn from someone else's writing and expensive to learn from an invoice.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Anti-patterns
&lt;/h2&gt;

&lt;p&gt;Each of the following was either done by us or seriously proposed, and each carries a specific cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Going straight to the model&lt;/strong&gt;, skipping attribution and compression, is the fastest thing to build and the most expensive to own. The model spends its budget on problems no repair can fix, and because nothing upstream partitions the input, the results cannot be attributed to any cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One instance per source&lt;/strong&gt; looks like obvious deduplication and silently hides every source with more than one failure mode. You repair the smallest mode and declare the source fixed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reimplementing the consumer inside the oracle&lt;/strong&gt; is faster to write and produces an oracle that drifts from production. The drift then masquerades as model error, and you tune the prompt to chase a bug in your own verifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verifying only on failing evidence&lt;/strong&gt; is tempting because that is the evidence you collected. It guarantees the incumbent loses every comparison, and you will ship regressions with confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-applying high-confidence candidates&lt;/strong&gt; removes the bottleneck by removing the only component that can see semantic wrongness. Because wrong repairs are silent, you will not notice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trusting self-reported confidence&lt;/strong&gt; is tempting because the number is right there in the output. It is uncalibrated, and it correlates with fluency rather than with correctness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iterating the critique loop until it passes&lt;/strong&gt; looks like convergence and is overfitting to the single instance inside the loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keying the queue by source alone&lt;/strong&gt; is the simplest schema and causes multi-mode repairs to overwrite one another, and parallel entry points to destroy each other's work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reaching for a bigger model at the plateau&lt;/strong&gt; is the culturally default move, and in our measurement it repaired 22% of one population and none of the other while damaging slots that already worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reporting a single precision number&lt;/strong&gt; makes for a cleaner narrative and averages populations of different difficulty, producing a figure that describes no real workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalising values into a fixed vocabulary before storing them&lt;/strong&gt; produces tidier data and destroys the evidence. A substring match once mapped a negative statement onto its positive counterpart, inverting the meaning; because only the mapped value was retained, the error was undetectable after the fact. Store what the source said, and normalise on read.&lt;/p&gt;




&lt;h2&gt;
  
  
  15. Design checklist
&lt;/h2&gt;

&lt;p&gt;The three lists below are ordered as they arise. Each item is a condition to be satisfied rather than a task to be ticked, and a design that cannot satisfy one should record why in the same place it records the rest of its decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before the first model call.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The oracle exists, runs the production code path, and can be replayed over historical evidence.&lt;/li&gt;
&lt;li&gt;Attribution buckets are mutually exclusive, exhaustive, named after their owning discipline, and asserted to sum to the total.&lt;/li&gt;
&lt;li&gt;Compression groups by structure and carries a sibling instance for every mode.&lt;/li&gt;
&lt;li&gt;Evidence validity is classified, and invalid evidence is excluded with a recorded reason.&lt;/li&gt;
&lt;li&gt;A store of successful evidence exists, holding the value production derived from each stored input.&lt;/li&gt;
&lt;li&gt;The queue key includes the source, the slot and the entry point.&lt;/li&gt;
&lt;li&gt;Verdicts are typed, each carries an action, and gates can only demote.&lt;/li&gt;
&lt;li&gt;The generator's contract states its constraints, the reason for each, and that declining is a correct answer.&lt;/li&gt;
&lt;li&gt;Sampling is deterministic and seeded.&lt;/li&gt;
&lt;li&gt;The critique loop is bounded at one round.&lt;/li&gt;
&lt;li&gt;Approval writes a single field, to the location the consumer reads, and records that path on the approval.&lt;/li&gt;
&lt;li&gt;Teardown of paid resources works from a cold start and is verified against the provider rather than against local state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Before quoting a number.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every run has been re-scored with the current oracle.&lt;/li&gt;
&lt;li&gt;Laboratory and field populations are reported separately and labelled as such.&lt;/li&gt;
&lt;li&gt;Populations of different difficulty are reported separately, with the structural reason for the difference stated.&lt;/li&gt;
&lt;li&gt;The decline rate is reported alongside precision rather than folded into it.&lt;/li&gt;
&lt;li&gt;The experiment log records, for each change, the question it tested and the decision it produced.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When precision plateaus.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rejections have been categorised mechanically by oracle reason before any model change is considered.&lt;/li&gt;
&lt;li&gt;The agreement rate on accepted candidates has been computed, to rule out an evidence problem.&lt;/li&gt;
&lt;li&gt;Escalation has been tested on rejected slots only, scored with the same oracle.&lt;/li&gt;
&lt;li&gt;Damage to previously-correct slots has been counted, not only repairs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  16. Instantiating the method in a new domain
&lt;/h2&gt;

&lt;p&gt;Map your problem onto the terminology of section 2, then work through the checklist. Three domains illustrate how little the method changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules that locate a value inside a semi-structured input.&lt;/strong&gt; The rule is a path or pattern; the source is an external system that emits inputs on its own schedule; the evidence is a stored input on which location failed. The oracle is the production parser together with the value coercion that follows it. Uniqueness means the candidate resolves to exactly one element, and generality means it holds on a second input of the same shape. The regression gate replays stored successful inputs and requires their known values back. The null-hypothesis gate asks whether the current rule already succeeds on the failing evidence. The trial runs both rules over fifty failing and fifty working inputs, and the adjudicator sees the value each one derives, side by side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field mappings in schema or configuration migration.&lt;/strong&gt; The rule is a mapping or a configuration key; the source is an upstream system; the evidence is a rejected record. The oracle is the real ingestion validator rather than a copy of its rules. Uniqueness means the candidate maps to exactly one destination field, and generality means it holds on a second record of the same shape. The regression gate replays previously-valid records and requires that they still validate. The null-hypothesis gate asks whether the current mapping already accepts the record. The trial runs both mappings over a held-out batch, and the adjudicator sees which records each accepts and rejects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assertions and wait conditions in flaky-test repair.&lt;/strong&gt; The rule is an assertion or a wait condition; the source is a test suite; the evidence is a failing run's trace and timing. The oracle is the real runner executing the real suite. Uniqueness means the condition resolves to one target, and generality means the behaviour holds across N repeated runs rather than one lucky one. The regression gate requires that previously-passing tests still pass. The null-hypothesis gate asks whether the test is failing for a reason unrelated to timing. The trial runs both versions fifty times, and the adjudicator sees the pass rate and the timing distribution of each.&lt;/p&gt;

&lt;p&gt;Four situations call for something other than this method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When no mechanical oracle exists&lt;/strong&gt; and condition C2 fails, build the oracle. If the oracle turns out to be the hard part, then the model was never your bottleneck and a harness will not help you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When instances are few and individually valuable&lt;/strong&gt; and condition C3 fails, use an interactive agent with tool access on individual cases. It costs an order of magnitude more per case and reads context a batch pipeline cannot, which makes it right for a handful of hard sources and wrong for thousands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When repairs require global reasoning&lt;/strong&gt; and condition C5 fails, restructure the problem until the unit of repair is local, or accept that what you have is design work rather than maintenance work and staff it accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When wrong repairs are loud&lt;/strong&gt; and condition C4 fails, you may be able to auto-apply with a rollback and skip the human gate entirely. Verify first that the failure really is loud in production rather than merely loud in a test environment, because the two are routinely confused and only one of them protects you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field note.&lt;/em&gt; We run the first domain in bulk and reserve an interactive agent for a handful of sources whose inputs carry their values inside embedded state rather than in the structure itself. The agent costs roughly forty times more per case and is the only tool we have that reads those inputs correctly. The boundary between the two is volume, not difficulty, and it is worth stating in the design document so that nobody has to rediscover it under deadline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The generator is the least important component of the system. What determines whether such a system produces trustworthy repairs is seven things: &lt;strong&gt;attribution&lt;/strong&gt;, which refuses to work on failures no repair can fix, and which accounted for 78% of ours; &lt;strong&gt;compression&lt;/strong&gt;, which yields one representative per genuine mode with a sibling for generality; &lt;strong&gt;evidence selection&lt;/strong&gt;, a small and well-chosen window, because the excluded region is where the errors live; &lt;strong&gt;the oracle&lt;/strong&gt;, running the production code path through layered checks and gates that only demote; &lt;strong&gt;a store of success&lt;/strong&gt;, without which regression cannot be detected and will therefore be shipped; &lt;strong&gt;typed verdicts and durable decisions&lt;/strong&gt;, so that reviewer effort accumulates rather than resetting; and &lt;strong&gt;honest evaluation&lt;/strong&gt;, meaning re-scored history, separated populations, and a metric that is permitted to get worse.&lt;/p&gt;

&lt;p&gt;Build those seven and a small general-purpose model is sufficient. Skip them and no model is.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Derived from a production deployment maintaining roughly 1,400 rules over inputs from as many independent external sources, covering approximately 1.6 million monitored items. All quantitative claims are measurements from that deployment. They are offered as evidence for the design rules, not as benchmarks to reproduce.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>llm</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your scraper is identified before it sends a single request</title>
      <dc:creator>Theodore P.</dc:creator>
      <pubDate>Sat, 22 Aug 2026 20:30:26 +0000</pubDate>
      <link>https://dev.to/theodore_p_9749548f7dd03/your-scraper-is-identified-before-it-sends-a-single-request-7n7</link>
      <guid>https://dev.to/theodore_p_9749548f7dd03/your-scraper-is-identified-before-it-sends-a-single-request-7n7</guid>
      <description>&lt;p&gt;I spent a long time believing that a blocked scraper was a headers problem. Wrong User-Agent, missing Accept-Language, something in that family. So I would copy a browser's headers field by field, hit the target again, and get the same 403. Then I would add a proxy. Same 403. Then I would add a delay. Same 403, just slower.&lt;/p&gt;

&lt;p&gt;The thing I was missing is that the server had already classified my client before my request headers arrived. It did it in the first packet of the TLS handshake, and no amount of header polishing reaches that packet.&lt;/p&gt;

&lt;p&gt;This post is about three things that, once I understood them, reorganized how I build fetchers: the handshake that identifies you, the line between what an HTTP client can present and what it structurally cannot, and the fact that a challenge page usually arrives wearing a 200.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The handshake is a fingerprint
&lt;/h2&gt;

&lt;p&gt;Before any HTTP request, your client opens a TLS connection. The first message it sends is the ClientHello. It is plaintext, it is not secret, and it is an announcement: here are the cipher suites I support, in my preferred order; here are the extensions I understand, in this order; here are the elliptic curves I accept; here are the signature algorithms; here is the ALPN list of protocols I am willing to speak afterwards.&lt;/p&gt;

&lt;p&gt;Your code chose none of this. The TLS library under your HTTP library chose it. Python's &lt;code&gt;ssl&lt;/code&gt; module makes one set of choices. Go's &lt;code&gt;crypto/tls&lt;/code&gt; makes another. Chrome's BoringSSL makes a third. The choices are stable per stack and different across stacks, which is exactly the property a fingerprint needs.&lt;/p&gt;

&lt;p&gt;The defense hashes the ordered set into a short identifier. The older scheme is JA3, the newer and more structured one is JA4. Then it looks the identifier up in a table. Real Chrome builds are in the table. Real Firefox builds are in the table. &lt;code&gt;python-requests&lt;/code&gt; is also in the table, filed under automation.&lt;/p&gt;

&lt;p&gt;Here is what the two announcements look like side by side, abbreviated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;real Chrome ClientHello:
  ciphers:   [GREASE, 0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, ...]
  ext order: [GREASE, server_name, ..., GREASE, padding]
  curves:    [GREASE, X25519, secp256r1, secp384r1]
  -&amp;gt; JA4 matches a known Chrome build

language-default ClientHello:
  ciphers:   [0xc02c, 0xc030, 0x009f, ...]
  ext order: [server_name, ...]
  -&amp;gt; JA4 matches "known automation"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details in there caught me out for a long time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Order is part of the signal.&lt;/strong&gt; Two clients can support the identical set of ciphers and still fingerprint differently because they list them in a different sequence. Enabling the same algorithms as Chrome is insufficient. You have to offer them in Chrome's order, with Chrome's extensions in Chrome's positions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browsers are deliberately messy.&lt;/strong&gt; Those &lt;code&gt;GREASE&lt;/code&gt; entries are meaningless values that browsers insert at randomized positions to keep the TLS ecosystem tolerant of unknown extensions. A naive imitation that omits them, or places them predictably, stands out precisely by being too tidy. A convincing handshake has to be messy in the exact way a real browser is messy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix: impersonate, do not reconstruct
&lt;/h3&gt;

&lt;p&gt;You do not rebuild Chrome's cipher order by hand. You use a library that links against a patched TLS stack and ships presets, each one a faithful copy of a specific browser version's ClientHello. In Python that library is &lt;code&gt;curl_cffi&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;curl_cffi.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;impersonate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chrome&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# cipher order, extension order, curves, ALPN, GREASE padding
# all reproduced from the named preset, not Python's ssl defaults
&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One value names the browser. Everything below it follows.&lt;/p&gt;

&lt;p&gt;Two disciplines come with this, and both are where it breaks in the field.&lt;/p&gt;

&lt;p&gt;First, the impersonation target has to agree with the identity you claim elsewhere. If the handshake is Chrome 120's and your User-Agent says Chrome 131, the defense sees two identities that cannot both be true. That mismatch is more incriminating than an honest Python fingerprint would have been, because an honest library is merely automation while a contradiction is an attempt.&lt;/p&gt;

&lt;p&gt;Second, presets age. A preset that reproduces a three-year-old Chrome build is itself anomalous once almost no real user runs that build. Keeping the impersonation target on versions real users actually run is maintenance, not setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the handshake cannot do
&lt;/h3&gt;

&lt;p&gt;A perfect Chrome handshake from a data-center IP is still a perfect Chrome handshake from a data-center IP, and the address earns its own suspicion. The handshake also runs none of the JavaScript probes that come later if a challenge engages. It is the first thing the server reads and the cheapest thing to get right, and that is the whole of its job.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The twenty and the eighty
&lt;/h2&gt;

&lt;p&gt;This is the part that would have saved me the most time.&lt;/p&gt;

&lt;p&gt;Think about what actually leaves your machine when an HTTP client makes a request. The TLS handshake. The HTTP/2 settings and frame ordering above it. The request headers, including the whole family of client hints. The source IP. These travel as bytes, and an HTTP client can present all of them with complete fidelity, because presenting them is nothing more than writing the right bytes.&lt;/p&gt;

&lt;p&gt;Now think about what a defense's challenge script probes once it is running inside a browser. Screen dimensions and pixel density. A WebGL scene rendered and hashed, which depends on the exact GPU and driver combination. Text drawn to a canvas and measured, which exercises the font stack. A short computation pushed through the audio pipeline, where the rounding varies by CPU and OS. Hardware concurrency. Device memory. And the motion of the pointer, graded for humanity.&lt;/p&gt;

&lt;p&gt;Not one of those has a representation in an HTTP request. There is no header for the hash of a rendered scene, because the hash is the output of code running against a graphics stack that your HTTP client does not have. You can send a client hint claiming a screen width, but a hint is a claim and the script makes a measurement, and a defense that cares cross-checks the two.&lt;/p&gt;

&lt;p&gt;Roughly a fifth of the total fingerprint surface travels on the wire. The other four fifths exist only inside a running browser. Faking a WebGL hash over HTTP is a category error. It is the result of a computation your tool cannot perform.&lt;/p&gt;

&lt;p&gt;That line through the middle of the problem decides the architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A target that reads only the wire surfaces is served by an HTTP client, fast and cheap. Effort goes into making the wire presentation coherent.&lt;/li&gt;
&lt;li&gt;A target that probes the runtime or grades behavior cannot be served by any HTTP client. The choice narrows to running a real browser or leaving the target alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every system of any size I have seen ends up with both tools and a rule for choosing between them. Start cheap. Escalate only when the cheap path is refused. Keep the expensive tier idle until a target proves it needs it.&lt;/p&gt;

&lt;p&gt;There is one bridge across the line, and it is the most valuable pattern I know for the harder defenses: a browser can do the runtime work once, earn the trust token, and hand that token to the HTTP client to spend on a burst of fast requests. It works because a token, unlike a rendered-scene hash, is just bytes, and bytes travel. It comes with three conditions (same exit IP as the solve, client identity coherent with the solving browser, burst finished before the token expires) and each one is a place the naive version dies silently. That pattern gets its own chapter in the book; here I only want the line itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The challenge arrives as a 200
&lt;/h2&gt;

&lt;p&gt;Everything above assumes you can tell when you were challenged. You mostly cannot, unless you build the detector on purpose.&lt;/p&gt;

&lt;p&gt;A defense rarely returns an honest error. It returns a page with a normal status code and a body of plausible size that is actually a shell: a script tag, an interstitial skeleton, and no content. A system that cannot distinguish the shell from the real page stores shells as data, counts challenges as successes, and burns its identities on the wrong signals.&lt;/p&gt;

&lt;p&gt;Two detectors, layered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content markers.&lt;/strong&gt; Every challenge script declares characteristic variables and identifiers that real content never contains. Keep a set per defense and scan every response body against it. False positives are rare. The cost is that markers drift as vendors revise their scripts, so a sudden rise in undetected shells is usually the first sign your marker set went stale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A size floor.&lt;/strong&gt; The stricter defenses serve a deliberately economical challenge, a few KB with a terse marker a generic scan might miss. Real content has a weight that a shell falls far below. A body under the floor is treated as a shell regardless of status code.&lt;/p&gt;

&lt;p&gt;Here is a real trace of one work item crossing the line mid-crawl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client GET /product/48812  -&amp;gt; HTTP/2 200, body 9.4 KB
  marker scan: "var chlgeId = ''" present
  size:        9.4 KB &amp;lt; 15 KB floor
  -&amp;gt; challenge wearing a 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important decision is what happens next. The naive reaction is to retry with a fresh identity. That earns the same shell, because the target is probing the runtime and no HTTP identity answers a runtime probe. The challenge is information about which tool the target requires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;burn persona
re-queue { url: /product/48812, force_js: True, priority: 0 }
browser tier: warm, solve, serve -&amp;gt; 200, body 148 KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which status codes mean what, in the retry policy I ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BURN_AND_RETRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;frozenset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;   &lt;span class="c1"&gt;# re-queue AND burn the identity
&lt;/span&gt;&lt;span class="n"&gt;RETRY_ONLY&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;frozenset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;   &lt;span class="c1"&gt;# transient: re-queue, keep identity
# 503 lives with the burn set: Akamai challenge shells return 503.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And one more detector for the case where the address itself has been condemned rather than the identity. A block page looks different from a challenge, and the correct response reaches below the persona to the exit IP, because fresh identities from a condemned address only get spent uselessly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_is_ip_blocked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;          &lt;span class="c1"&gt;# real content never trips this
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adresse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adres&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blocked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access denied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;too many requests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gesperrt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bloqué&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bloqueado&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;geblokkeerd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What this changed in practice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I stopped treating headers as the first fix and started treating the handshake as the first fix. &lt;code&gt;impersonate="chrome"&lt;/code&gt; and a User-Agent that names the same Chrome version, before anything else.&lt;/li&gt;
&lt;li&gt;I stopped asking "how do I make requests pass this target" and started asking "which side of the line is this target on." If it probes the runtime, the answer is a browser, and the interesting engineering becomes how rarely I can afford to use it.&lt;/li&gt;
&lt;li&gt;I stopped counting 200s as successes. A 200 is a status code. Content is a marker scan plus a size floor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is secret; it is all visible in the bytes if you look. The expensive part was learning which bytes to look at and in what order, and that is what the months went on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The book
&lt;/h2&gt;

&lt;p&gt;These three sections are condensed from chapters III, IV, and XIV of a book I wrote called &lt;strong&gt;The Quiet Fetch: Engineering Web Acquisition Against Modern Bot Defenses, One Layer at a Time&lt;/strong&gt;. It is 25 chapters and four appendices, about 110 pages, and it walks the whole stack in the order a request meets it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the six fingerprint surfaces and the two kinds of defense&lt;/li&gt;
&lt;li&gt;the wire layer: TLS, HTTP/2 settings as a fingerprint, headers and client hints, referer and timing tells&lt;/li&gt;
&lt;li&gt;personas as a coherent unit, pools, cooldown, and burn&lt;/li&gt;
&lt;li&gt;Cloudflare and Akamai up close, why Akamai is harder, the cookie-handoff warmup, challenge detection&lt;/li&gt;
&lt;li&gt;the browser tier: Slots and profiles, coupling the browser identity to the client identity&lt;/li&gt;
&lt;li&gt;fleet and scale: splitting the pipeline into three services, queues and pacing, scaling on backlog, the proxy grid, retries and breakers and self-healing selectors&lt;/li&gt;
&lt;li&gt;recon and operating: mapping a surface, auditing your own origin for leaks, observability, and the honest ceiling where the right answer is to stop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every chapter ends with a Field Verdict box: the mechanism, the fix, and the conditions under which it fails. The code is production code from fetchers I run, with the business stripped out.&lt;/p&gt;

&lt;p&gt;If the three things above were useful, the rest is here: &lt;strong&gt;&lt;a href="https://teopanta.gumroad.com/l/quiet-fetch?layout=profile" rel="noopener noreferrer"&gt;The Quiet Fetch on Gumroad&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Questions about any of it, or a defense that behaves differently from what I described, are welcome in the comments. The marker sets drift and so does my understanding.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>security</category>
      <category>networking</category>
    </item>
    <item>
      <title>Grapple Dojo</title>
      <dc:creator>Theodore P.</dc:creator>
      <pubDate>Thu, 13 Aug 2026 10:36:28 +0000</pubDate>
      <link>https://dev.to/theodore_p_9749548f7dd03/grapple-dojo-26a</link>
      <guid>https://dev.to/theodore_p_9749548f7dd03/grapple-dojo-26a</guid>
      <description>&lt;p&gt;I built a game for BJJ players who want to keep training off the mats.&lt;/p&gt;

&lt;p&gt;Meet Grapple Dojo: basically "grappling chess" powered by 3D animations, featuring authentic positions and transitions.&lt;/p&gt;

&lt;p&gt;It utilizes Eelis van der Weegen’s incredible GrappleMap dataset—which maps 843 positions and 1,485 transitions from top-tier instructionals (Marcelo Garcia, Ryan Hall, Eddie Bravo, Demian Maia, etc.), complete with exact DVD chapter/second references.&lt;/p&gt;

&lt;p&gt;Key Features:&lt;/p&gt;

&lt;p&gt;Active Recall: Test your positional knowledge in tactical duels.&lt;/p&gt;

&lt;p&gt;Full 3D Viewer: Rotate and analyze moves from any angle through a White-to-Black belt curriculum.&lt;/p&gt;

&lt;p&gt;Virtual Open Mat: Set up positions on an interactive dummy and share techniques via live chat.&lt;/p&gt;

&lt;p&gt;Stat Tracking: Track your mastered techniques and overall rank progress.&lt;/p&gt;

&lt;p&gt;Huge credit to GrappleMap for doing the heavy lifting with the data—I just gamified it!&lt;/p&gt;

&lt;p&gt;Try it out and tell me: what’s your go-to position? 🤙&lt;/p&gt;

&lt;p&gt;&lt;a href="https://grapple-dojo.onrender.com/" rel="noopener noreferrer"&gt;https://grapple-dojo.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;P.S. Still in active development! Working on tighter move validation, better 3D models, and bug fixes.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Stack Panic</title>
      <dc:creator>Theodore P.</dc:creator>
      <pubDate>Thu, 13 Aug 2026 10:34:06 +0000</pubDate>
      <link>https://dev.to/theodore_p_9749548f7dd03/stack-panic-3p6n</link>
      <guid>https://dev.to/theodore_p_9749548f7dd03/stack-panic-3p6n</guid>
      <description>&lt;p&gt;I really miss hand-crafted, line-by-line coding, but the motivation just isn’t there anymore.&lt;/p&gt;

&lt;p&gt;Every time I reopened LeetCode, I’d end up reading old theory books instead of writing code. &lt;/p&gt;

&lt;p&gt;To fix that, I decided to gamify the experience. &lt;/p&gt;

&lt;p&gt;I built a game-like take on LeetCode where you write code, learn, and directly apply that knowledge in Stack Panic.&lt;/p&gt;

&lt;p&gt;Check it out, you're going to love it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stack-panic.onrender.com/" rel="noopener noreferrer"&gt;https://stack-panic.onrender.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leetcode</category>
      <category>gamechallenge</category>
      <category>code</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Streaming SQL Engine: Lightweight Cross-Data Source Integration for Resource-Constrained Environments.</title>
      <dc:creator>Theodore P.</dc:creator>
      <pubDate>Mon, 05 Jan 2026 11:28:45 +0000</pubDate>
      <link>https://dev.to/theodore_p_9749548f7dd03/streaming-sql-engine-lightweight-cross-data-source-integration-for-resource-constrained-1o2l</link>
      <guid>https://dev.to/theodore_p_9749548f7dd03/streaming-sql-engine-lightweight-cross-data-source-integration-for-resource-constrained-1o2l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feph870fnrybk2b2fxv3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feph870fnrybk2b2fxv3j.png" alt=" " width="800" height="717"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One month ago, I shared how we deployed a streaming SQL engine that lets you many data sources using standard SQL syntax. &lt;/p&gt;

&lt;p&gt;Original post is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/theodore_p_9749548f7dd03/how-i-built-a-python-library-that-lets-you-join-mysql-postgresql-mongodb-rest-apis-and-files-in-h5d"&gt;https://dev.to/theodore_p_9749548f7dd03/how-i-built-a-python-library-that-lets-you-join-mysql-postgresql-mongodb-rest-apis-and-files-in-h5d&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today, I want to present our research paper: "Streaming SQL Engine: Lightweight Cross-Data Source Integration for Resource-Constrained Environments."&lt;/p&gt;

&lt;p&gt;Read it here: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Ierofantis/streaming_sql_engine/blob/master/Streaming_SQL_Engine_Paper.pdf" rel="noopener noreferrer"&gt;https://github.com/Ierofantis/streaming_sql_engine/blob/master/Streaming_SQL_Engine_Paper.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem we aimed to solve is simple: many organizations need ETL and big data capabilities without the overhead of heavy infrastructure. &lt;/p&gt;

&lt;p&gt;Not every team can spin up large compute clusters or dedicate substantial resources to data pipelines.&lt;/p&gt;

&lt;p&gt;Our solution is a streaming, iterator-based SQL engine optimized for memory efficiency and cross-source integration.&lt;/p&gt;

&lt;p&gt;This work isn’t about claiming the "fastest" solution, it’s about providing a practical, optimized tool that solves real problems for teams with limited resources.&lt;/p&gt;

&lt;p&gt;All comparisons are transparent, with fair benchmarks. &lt;/p&gt;

&lt;p&gt;I believe the best solutions come from honest evaluations of trade-offs, not marketing hype.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>dataengineering</category>
      <category>python</category>
      <category>sql</category>
    </item>
    <item>
      <title>Join Data from Anywhere: The Streaming SQL Engine That Bridges Databases, APIs, and Files</title>
      <dc:creator>Theodore P.</dc:creator>
      <pubDate>Tue, 16 Dec 2025 21:55:43 +0000</pubDate>
      <link>https://dev.to/theodore_p_9749548f7dd03/how-i-built-a-python-library-that-lets-you-join-mysql-postgresql-mongodb-rest-apis-and-files-in-h5d</link>
      <guid>https://dev.to/theodore_p_9749548f7dd03/how-i-built-a-python-library-that-lets-you-join-mysql-postgresql-mongodb-rest-apis-and-files-in-h5d</guid>
      <description>&lt;p&gt;Have you ever needed to join data from a MySQL database with a PostgreSQL database, a MongoDB collection, and a REST API all in one query? Traditional databases can't do this. That's why I built the Streaming SQL Engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Data Lives Everywhere
&lt;/h2&gt;

&lt;p&gt;Modern applications don't store all their data in one place. You might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User data in PostgreSQL&lt;/li&gt;
&lt;li&gt;Order data in MySQL&lt;/li&gt;
&lt;li&gt;Product catalog in MongoDB&lt;/li&gt;
&lt;li&gt;Pricing information from a REST API&lt;/li&gt;
&lt;li&gt;Inventory data in CSV files&lt;/li&gt;
&lt;li&gt;Product feeds in XML files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The challenge:&lt;/strong&gt; How do you join all this data together?&lt;/p&gt;

&lt;p&gt;Traditional solutions require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exporting data from each system&lt;/li&gt;
&lt;li&gt;Importing into a central database&lt;/li&gt;
&lt;li&gt;Writing complex ETL pipelines&lt;/li&gt;
&lt;li&gt;Maintaining data synchronization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;There had to be a better way.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Streaming SQL Engine
&lt;/h2&gt;

&lt;p&gt;I built a lightweight Python library that lets you join data from &lt;strong&gt;any source&lt;/strong&gt; using standard SQL syntax without exporting, importing, or setting up infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;streaming_sql_engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Engine&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MongoClient&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Register PostgreSQL source (iterator function)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;postgres_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, name, email FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres_users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;postgres_users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Register MySQL source (iterator function)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mysql_products&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, name, price FROM products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mysql_products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mysql_products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Register MongoDB source (iterator function)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mongo_inventory&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mongodb://localhost:27017&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mydb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mongo_inventory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mongo_inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Register REST API source (iterator function)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_prices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/prices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api_prices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_prices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Register CSV source (iterator function)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;csv_suppliers&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;suppliers.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;csv_suppliers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;csv_suppliers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Join them all in one SQL query!
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT
        mysql_products.name,
        postgres_users.email,
        mongo_inventory.quantity,
        api_prices.price,
        csv_suppliers.supplier_name
    FROM mysql_products
    JOIN postgres_users ON mysql_products.user_id = postgres_users.id
    JOIN mongo_inventory ON mysql_products.sku = mongo_inventory.sku
    JOIN api_prices ON mysql_products.sku = api_prices.sku
    JOIN csv_suppliers ON mysql_products.supplier_id = csv_suppliers.id
    WHERE api_prices.price &amp;gt; 100
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it.&lt;/strong&gt; No clusters, no infrastructure, no data export - just pure Python and SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built This: The Problem That Needed Solving
&lt;/h2&gt;

&lt;p&gt;I was working on a data reconciliation project where I needed to join data from multiple sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL database (product catalog)&lt;/li&gt;
&lt;li&gt;PostgreSQL database (user data)&lt;/li&gt;
&lt;li&gt;MongoDB collection (inventory)&lt;/li&gt;
&lt;li&gt;REST API (pricing information)&lt;/li&gt;
&lt;li&gt;CSV files (supplier data)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The challenge:&lt;/strong&gt; Traditional databases can't join across different systems. I had three options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Export everything to one database&lt;/strong&gt; - Time-consuming, requires ETL pipelines, data becomes stale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write custom Python code&lt;/strong&gt; - Complex, error-prone, hard to maintain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use existing tools&lt;/strong&gt; - Spark/Flink require clusters, DuckDB requires data import, Presto needs infrastructure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;None of these worked for my use case.&lt;/strong&gt; I needed something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Could join data from different systems without export&lt;/li&gt;
&lt;li&gt;Was simple to use (SQL syntax)&lt;/li&gt;
&lt;li&gt;Required zero infrastructure&lt;/li&gt;
&lt;li&gt;Worked with Python natively&lt;/li&gt;
&lt;li&gt;Processed data efficiently (streaming)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built the Streaming SQL Engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It: Architecture and Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Design Philosophy
&lt;/h3&gt;

&lt;p&gt;The engine follows a &lt;strong&gt;pipeline architecture&lt;/strong&gt; inspired by database query execution engines (like PostgreSQL and SQLite), but implemented in pure Python using iterators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; Python's iterator protocol is perfect for streaming data. Each operator in the pipeline is an iterator that processes rows one at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Iterator-Based Architecture?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Key advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory efficiency:&lt;/strong&gt; Only one row in memory at a time (except for join indexes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy evaluation:&lt;/strong&gt; Processing starts only when you iterate over results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composability:&lt;/strong&gt; Operators can be chained arbitrarily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility:&lt;/strong&gt; Easy to add new operators&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python-native:&lt;/strong&gt; Uses standard Python iterator protocol&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Python iterators are slower than compiled code, but flexibility is worth it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; Join indexes require memory, but this is necessary for efficient joins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Iterator chains can be complex, but they're composable and testable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Expression Evaluation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How WHERE clauses are evaluated:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The engine uses recursive AST traversal to evaluate expressions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why recursive:&lt;/strong&gt; SQL expressions are trees. Recursive evaluation naturally handles nested expressions like &lt;code&gt;(a &amp;gt; 10 AND b &amp;lt; 20) OR c = 5&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Join Algorithm Selection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How the engine chooses join algorithms:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The engine follows this priority order when selecting a join algorithm:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check if both sides are sorted&lt;/strong&gt; (&lt;code&gt;ordered_by&lt;/code&gt; metadata)&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If yes, use &lt;code&gt;MergeJoinIterator&lt;/code&gt; (most memory-efficient for sorted data)&lt;/li&gt;
&lt;li&gt;Only used when &lt;code&gt;use_polars=False&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check if right side is a file&lt;/strong&gt; (&lt;code&gt;filename&lt;/code&gt; metadata)&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If yes, use &lt;code&gt;MmapLookupJoinIterator&lt;/code&gt; (memory-mapped, 90-99% memory reduction)&lt;/li&gt;
&lt;li&gt;Only used when &lt;code&gt;use_polars=False&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check if Polars is available&lt;/strong&gt; (&lt;code&gt;use_polars&lt;/code&gt; flag)&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If yes, use &lt;code&gt;PolarsLookupJoinIterator&lt;/code&gt; (vectorized, SIMD-accelerated)&lt;/li&gt;
&lt;li&gt;Used when &lt;code&gt;use_polars=True&lt;/code&gt; is explicitly set&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Default fallback&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;LookupJoinIterator&lt;/code&gt; (Python hash-based, most compatible)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When &lt;code&gt;use_polars=False&lt;/code&gt; (default), Merge Join and MMAP Join are checked first before falling back to Python Lookup Join. When &lt;code&gt;use_polars=True&lt;/code&gt; is explicitly set, the engine prioritizes Polars over MMAP and Merge Join.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protocol-Based Optimization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The key innovation:&lt;/strong&gt; Automatic optimization detection via function signature inspection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Engine inspects source function signature using &lt;code&gt;inspect.signature()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If function accepts &lt;code&gt;dynamic_where&lt;/code&gt; or &lt;code&gt;dynamic_columns&lt;/code&gt; parameters, it supports optimizations&lt;/li&gt;
&lt;li&gt;Engine passes optimization parameters automatically&lt;/li&gt;
&lt;li&gt;Source function applies optimizations (filter pushdown, column pruning)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No flags needed:&lt;/strong&gt; Detection is automatic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible:&lt;/strong&gt; Any Python function can be a source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backward compatible:&lt;/strong&gt; Simple sources still work (no optimizations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible:&lt;/strong&gt; Easy to add new optimization parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simple source (no optimizations)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simple_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;

&lt;span class="c1"&gt;# Optimized source (with protocol)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optimized_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; FROM table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; WHERE &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;execute_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Both work the same way:
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;simple_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# No optimizations
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optimized_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Optimizations apply automatically!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Memory Management
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Key strategies:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Streaming:&lt;/strong&gt; Process one row at a time, never load full tables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join indexes:&lt;/strong&gt; Only right side of joins is materialized (necessary for lookups)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory-mapped files:&lt;/strong&gt; For large JSONL files, use OS virtual memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Column pruning:&lt;/strong&gt; Only extract needed columns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter pushdown:&lt;/strong&gt; Filter at source, reduce data transfer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Memory footprint:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left side of join:&lt;/strong&gt; O(1) - one row at a time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right side of join:&lt;/strong&gt; O(n) - hash index in memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total:&lt;/strong&gt; O(n) where n = size of right side&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; In most queries, the right side is smaller (e.g., joining large product table with small category table). The engine is designed to put smaller tables on the right side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Optimizations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Polars Vectorization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When Polars is available, the engine uses vectorized operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Instead of row-by-row filtering:
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;

&lt;span class="c1"&gt;# Use Polars batch filtering:
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;filtered_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;filtered_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iter_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why faster:&lt;/strong&gt; Polars uses SIMD instructions and columnar processing, 10-200x faster than row-by-row Python loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Memory-Mapped Joins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For large JSONL files, use OS virtual memory instead of loading into RAM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Instead of loading entire file:
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;large_file.jsonl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# 10GB in memory!
&lt;/span&gt;
&lt;span class="c1"&gt;# Use memory-mapped file:
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mmap&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;large_file.jsonl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fileno&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACCESS_READ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# OS handles memory, can process files larger than RAM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; OS virtual memory allows accessing file data without loading it all into RAM. 90-99% memory reduction for large files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. First Match Only Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For joins where only the first match matters (prevents Cartesian products).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why useful:&lt;/strong&gt; Prevents Cartesian products when right side has duplicate keys. Significantly reduces output size and processing time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Column Pruning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only extracts columns needed for the query, reducing I/O and memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Query only requests 'name' and 'price'
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT name, price FROM products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Engine automatically requests only these columns from source
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ['name', 'price']
&lt;/span&gt;    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; FROM table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Filter Pushdown&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pushes WHERE conditions to data sources when possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Query has WHERE clause
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM products WHERE price &amp;gt; 100&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Engine automatically pushes filter to source
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM table WHERE &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Merge Joins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Efficient joins for pre-sorted data (O(n+m) time complexity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Register with ordered_by to enable merge join
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ordered_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Engine uses merge join algorithm
# Both sides must be sorted by join key
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Design Decisions and Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Why Python iterators instead of compiled code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Use Python iterators for flexibility&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Slower than compiled code, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works with any Python data source&lt;/li&gt;
&lt;li&gt;Easy to extend and customize&lt;/li&gt;
&lt;li&gt;No compilation step needed&lt;/li&gt;
&lt;li&gt;Python-native integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Why separate logical planning from execution?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Two-phase approach (plan then execute)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Extra step, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enables query optimization&lt;/li&gt;
&lt;li&gt;Easier to test and debug&lt;/li&gt;
&lt;li&gt;Can reuse planning logic&lt;/li&gt;
&lt;li&gt;Clear separation of concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Why protocol-based optimization instead of flags?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Automatic detection via function signature&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Slightly more complex detection, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No flags to remember&lt;/li&gt;
&lt;li&gt;Automatic optimization&lt;/li&gt;
&lt;li&gt;Backward compatible&lt;/li&gt;
&lt;li&gt;More Pythonic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Why materialize right side of joins?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Build hash index on right side&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Memory usage, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O(1) lookup time per left row&lt;/li&gt;
&lt;li&gt;Necessary for efficient joins&lt;/li&gt;
&lt;li&gt;Can use memory-mapped files for large files&lt;/li&gt;
&lt;li&gt;Standard database approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Why limit SQL features (no GROUP BY, aggregations)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Focus on joins and filtering&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; Less SQL support, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simpler implementation&lt;/li&gt;
&lt;li&gt;Faster execution&lt;/li&gt;
&lt;li&gt;Focuses on core use case (cross-system joins)&lt;/li&gt;
&lt;li&gt;Can add later if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;

&lt;p&gt;A lightweight Python library that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Joins data from any source using SQL&lt;/li&gt;
&lt;li&gt;Processes data row-by-row (streaming)&lt;/li&gt;
&lt;li&gt;Requires zero infrastructure&lt;/li&gt;
&lt;li&gt;Automatically optimizes when possible&lt;/li&gt;
&lt;li&gt;Works with any Python iterator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; Python's iterator protocol is perfect for streaming SQL execution. By combining SQL parsing, logical planning, and iterator-based execution, I created a tool that solves a real problem: joining data from different systems without complex infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works: Streaming Architecture
&lt;/h2&gt;

&lt;p&gt;The engine processes data &lt;strong&gt;row-by-row&lt;/strong&gt;, never loading entire tables into memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SQL Query
    |
Parser -&amp;gt; AST
    |
Planner -&amp;gt; Logical Plan
    |
Executor -&amp;gt; Iterator Pipeline
    |
Results (Generator)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Iterator Pipeline:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ScanIterator -&amp;gt; FilterIterator -&amp;gt; JoinIterators -&amp;gt; ProjectIterator -&amp;gt; Results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each iterator processes rows incrementally, enabling true streaming execution. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low memory footprint&lt;/li&gt;
&lt;li&gt;Can process data larger than RAM&lt;/li&gt;
&lt;li&gt;Results yielded immediately&lt;/li&gt;
&lt;li&gt;No buffering required&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Supported Data Sources
&lt;/h2&gt;

&lt;p&gt;The engine works with &lt;strong&gt;any Python iterator&lt;/strong&gt;, making it incredibly flexible:&lt;/p&gt;

&lt;h3&gt;
  
  
  Databases
&lt;/h3&gt;

&lt;p&gt;All databases are accessed via Python iterator functions. The engine doesn't use connectors - it works with any Python function that returns an iterator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; - Create iterator function that queries PostgreSQL and yields rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt; - Create iterator function that queries MySQL and yields rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; - Create iterator function that queries MongoDB and yields documents&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSV&lt;/strong&gt; - Standard CSV files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSONL&lt;/strong&gt; - JSON Lines format with memory-mapped joins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON&lt;/strong&gt; - Standard JSON files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XML&lt;/strong&gt; - XML parsing with ElementTree&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;REST APIs&lt;/strong&gt; - Any HTTP endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL&lt;/strong&gt; - Via custom functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSockets&lt;/strong&gt; - Streaming data sources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Custom Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Any Python function&lt;/strong&gt; that returns an iterator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generators&lt;/strong&gt; - Perfect for streaming data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom transformations&lt;/strong&gt; - Apply Python logic between joins&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SQL Features
&lt;/h2&gt;

&lt;p&gt;The engine supports standard SQL syntax:&lt;/p&gt;

&lt;h3&gt;
  
  
  Supported Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;SELECT&lt;/strong&gt; - Column selection, aliasing, table-qualified columns&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JOIN&lt;/strong&gt; - INNER JOIN and LEFT JOIN with equality conditions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;table3&lt;/span&gt; &lt;span class="n"&gt;t3&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;WHERE&lt;/strong&gt; - Comparisons, boolean logic, NULL checks, IN clauses&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Arithmetic&lt;/strong&gt; - Addition, subtraction, multiplication, division, modulo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;final_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Not Supported
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GROUP BY and aggregations (COUNT, SUM, AVG)&lt;/li&gt;
&lt;li&gt;ORDER BY&lt;/li&gt;
&lt;li&gt;HAVING&lt;/li&gt;
&lt;li&gt;Subqueries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;These limitations keep the engine focused on joins and filtering - its core strength.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example 1: Microservices Data Integration
&lt;/h3&gt;

&lt;p&gt;In a microservices architecture, data is distributed across services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;streaming_sql_engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Engine&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Service 1: User service (PostgreSQL) - iterator function
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;users_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, name, email FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Service 2: Order service (MySQL) - iterator function
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;orders_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order-db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, user_id, total FROM orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Service 3: Payment service (REST API) - iterator function
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;payment_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://payments.service/api/transactions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Join across services
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT users.name, orders.total, payments.status
    FROM users
    JOIN orders ON users.id = orders.user_id
    JOIN payments ON orders.id = payments.order_id
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; No need for a shared database or complex ETL pipelines. The engine accepts any Python function that returns an iterator, making it incredibly flexible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Real-Time Price Comparison
&lt;/h3&gt;

&lt;p&gt;Compare prices from multiple XML feeds and match with MongoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_xml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ET&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.//product&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ean&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ean&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xml1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parse_xml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prices1.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xml2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parse_xml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prices2.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mongo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mongo_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT
        xml1.ean,
        xml1.price AS price1,
        xml2.price AS price2,
        mongo.sf_sku
    FROM xml1
    JOIN xml2 ON xml1.ean = xml2.ean
    JOIN mongo ON xml1.ean = mongo.ean
    WHERE xml1.price != xml2.price
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 3: Python Processing Between Joins
&lt;/h3&gt;

&lt;p&gt;Apply Python logic (ML models, custom functions) between joins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enriched_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Source that processes data with Python before joining&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, name, category_id FROM products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="c1"&gt;# Apply Python logic
&lt;/span&gt;        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ml_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ml_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;custom_field&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;custom_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;categories_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, name FROM categories&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enriched_products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enriched_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT p.name, p.ml_score, c.category_name
    FROM enriched_products p
    JOIN categories c ON p.category_id = c.id
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Seamless integration with Python ecosystem - use any library, apply any logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison with Alternatives
&lt;/h2&gt;

&lt;h3&gt;
  
  
  vs DuckDB
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Streaming SQL Engine&lt;/th&gt;
&lt;th&gt;DuckDB&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cross-system joins&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;⚠️ Requires import&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API + Database join&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;❌ Must export API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time streaming&lt;/td&gt;
&lt;td&gt;✅ True streaming&lt;/td&gt;
&lt;td&gt;⚠️ Buffering needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python processing&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;⚠️ Export/import&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY / Aggregations&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;td&gt;✅ Full support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance (same DB)&lt;/td&gt;
&lt;td&gt;⚠️ Moderate&lt;/td&gt;
&lt;td&gt;✅ Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Use Streaming SQL Engine when:&lt;/strong&gt; You need to join data from different systems that can't be imported into DuckDB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use DuckDB when:&lt;/strong&gt; All data can be imported and you need aggregations.&lt;/p&gt;

&lt;h3&gt;
  
  
  vs Pandas
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Streaming SQL Engine&lt;/th&gt;
&lt;th&gt;Pandas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cross-system joins&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;❌ Must load all data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory efficiency&lt;/td&gt;
&lt;td&gt;✅ Streaming&lt;/td&gt;
&lt;td&gt;❌ Loads all in memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL syntax&lt;/td&gt;
&lt;td&gt;✅ Standard SQL&lt;/td&gt;
&lt;td&gt;❌ DataFrame API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large datasets&lt;/td&gt;
&lt;td&gt;✅ Can exceed RAM&lt;/td&gt;
&lt;td&gt;❌ Limited by RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time data&lt;/td&gt;
&lt;td&gt;✅ Streaming&lt;/td&gt;
&lt;td&gt;❌ Batch only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File formats&lt;/td&gt;
&lt;td&gt;✅ Any Python iterator&lt;/td&gt;
&lt;td&gt;⚠️ Limited formats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance (large data)&lt;/td&gt;
&lt;td&gt;✅ Streaming&lt;/td&gt;
&lt;td&gt;⚠️ Slower for large data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Use Streaming SQL Engine when:&lt;/strong&gt; You need to join data from different systems, process data larger than RAM, or use SQL syntax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Pandas when:&lt;/strong&gt; All data fits in memory and you prefer DataFrame API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are There Other Tools Like This?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Short answer: Not exactly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While there are many tools that do &lt;strong&gt;parts&lt;/strong&gt; of what Streaming SQL Engine does, none combine all these characteristics:&lt;/p&gt;

&lt;h3&gt;
  
  
  What Makes Streaming SQL Engine Unique
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Zero Infrastructure + Cross-System Joins&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Most tools require clusters (Spark, Flink, Drill)&lt;/li&gt;
&lt;li&gt;Or require specific infrastructure (ksqlDB needs Kafka)&lt;/li&gt;
&lt;li&gt;Streaming SQL Engine: Just Python&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Any Python Iterator as Data Source&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Most tools require specific connectors&lt;/li&gt;
&lt;li&gt;Streaming SQL Engine: Any Python function works&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Direct API Joins&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Most tools can't join REST APIs directly&lt;/li&gt;
&lt;li&gt;Streaming SQL Engine: Native support&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Python-Native Architecture&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Most tools are Java/Rust with Python wrappers&lt;/li&gt;
&lt;li&gt;Streaming SQL Engine: Pure Python, seamless integration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Similar Tools (But Different)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Apache Drill&lt;/strong&gt; - Similar cross-system capability, but requires cluster and Java&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ksqlDB&lt;/strong&gt; - Streaming SQL, but Kafka-only and requires infrastructure&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Materialize&lt;/strong&gt; - Streaming database, but requires database server&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DataFusion&lt;/strong&gt; - Fast SQL engine, but limited to Arrow/Parquet data&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polars SQL&lt;/strong&gt; - Fast SQL, but requires loading data into DataFrames first&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Presto/Trino&lt;/strong&gt; - Cross-system SQL, but requires cluster infrastructure&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None of these&lt;/strong&gt; combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero infrastructure&lt;/li&gt;
&lt;li&gt;Any Python iterator as source&lt;/li&gt;
&lt;li&gt;Direct API joins&lt;/li&gt;
&lt;li&gt;Pure Python implementation&lt;/li&gt;
&lt;li&gt;Simple deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;That's what makes Streaming SQL Engine unique.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Here: The Most Secure Way
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install and Basic Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;streaming-sql-engine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Most Stable Join Option
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lookup Join (Default)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Python hash-based join - the default when no special metadata is provided.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# use_polars=False (default)
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;products_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# No special metadata - uses Lookup Join automatically
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Builds hash index on right side (loads all right rows into memory)&lt;/li&gt;
&lt;li&gt;Streams left side row-by-row&lt;/li&gt;
&lt;li&gt;Looks up matches in hash index (O(1) per lookup)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default choice - works with any data&lt;/li&gt;
&lt;li&gt;Small to medium datasets (&amp;lt; 1M rows)&lt;/li&gt;
&lt;li&gt;When right side fits in memory&lt;/li&gt;
&lt;li&gt;When data is not sorted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Most compatible (works with any data types)&lt;/li&gt;
&lt;li&gt;✅ No special requirements&lt;/li&gt;
&lt;li&gt;✅ Reliable and stable&lt;/li&gt;
&lt;li&gt;✅ Good performance for medium datasets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ Loads entire right side into memory&lt;/li&gt;
&lt;li&gt;⚠️ Not optimal for very large right tables&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some recommendations from the author.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Recommendation 1: Use Merge Join if Data is Sorted (Fastest + Lowest Memory)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Pre-sorted data or data that can be sorted once&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Merge Join requires use_polars=False
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;products_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ordered_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ordered_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it's best:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Lowest memory overhead&lt;/li&gt;
&lt;li&gt;✅ True streaming (both sides stream simultaneously)&lt;/li&gt;
&lt;li&gt;✅ Fast execution (comparable to other options)&lt;/li&gt;
&lt;li&gt;✅ Can handle datasets larger than RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data is already sorted by join key&lt;/li&gt;
&lt;li&gt;Memory is a concern&lt;/li&gt;
&lt;li&gt;You can sort data once (e.g., sort JSONL files before processing)&lt;/li&gt;
&lt;li&gt;Both sides of join are sorted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example: Preparing Data for Merge Join&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Step 1: Sort your JSONL files (one-time operation)
# Use a utility script or database ORDER BY
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sort_jsonl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jsonl&lt;/span&gt; &lt;span class="n"&gt;products_sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jsonl&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;
&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sort_jsonl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jsonl&lt;/span&gt; &lt;span class="n"&gt;images_sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jsonl&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;

&lt;span class="c1"&gt;# Step 2: Use sorted files with Merge Join
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;load_jsonl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products_sorted.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;ordered_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;load_jsonl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images_sorted.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;ordered_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Step 3: Query - Merge Join will be used automatically
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT products.product_id, products.title, images.image
    FROM products
    LEFT JOIN images ON products.product_id = images.product_id
    WHERE products.checked = 1
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recommendation 2: Use MMAP Join if Data is NOT Sorted (Low Memory)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Unsorted data, large files, memory-constrained environments&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# MMAP Join requires use_polars=False
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;products_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it's best:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Low memory overhead&lt;/li&gt;
&lt;li&gt;✅ Works with unsorted data (no sorting required)&lt;/li&gt;
&lt;li&gt;✅ OS-managed memory mapping (can handle files larger than RAM)&lt;/li&gt;
&lt;li&gt;✅ 90-99% memory reduction compared to loading entire file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data is NOT sorted (or sorting is expensive)&lt;/li&gt;
&lt;li&gt;Large files (&amp;gt; 100MB)&lt;/li&gt;
&lt;li&gt;Memory-constrained systems&lt;/li&gt;
&lt;li&gt;Files larger than available RAM&lt;/li&gt;
&lt;li&gt;You want low memory without sorting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jsonl_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Register with filename parameter to enable MMAP Join
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonl_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recommendation 3: Use Polars + Optimizations for Best Throughput
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Maximum speed when you can filter early and normalize data types&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_match_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optimized_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Source with all optimizations:
    - Filter pushdown (dynamic_where)
    - Column pruning (dynamic_columns)
    - Data normalization (for Polars)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Build optimized query
&lt;/span&gt;    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamic_where&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dynamic_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;execute_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Normalize types for Polars stability
&lt;/span&gt;        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;normalize_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optimized_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it's best:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Fastest execution time &lt;/li&gt;
&lt;li&gt;✅ Low memory overhead &lt;/li&gt;
&lt;li&gt;✅ Early filtering reduces data volume significantly&lt;/li&gt;
&lt;li&gt;✅ Vectorized operations (SIMD acceleration)&lt;/li&gt;
&lt;li&gt;✅ All optimizations combined (filter pushdown + column pruning)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can filter data early (WHERE clause can be pushed to source)&lt;/li&gt;
&lt;li&gt;Data types are consistent (can normalize)&lt;/li&gt;
&lt;li&gt;Speed is priority&lt;/li&gt;
&lt;li&gt;Polars is available&lt;/li&gt;
&lt;li&gt;You want maximum performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ Requires protocol support in source function (&lt;code&gt;dynamic_where&lt;/code&gt;, &lt;code&gt;dynamic_columns&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;⚠️ Requires data normalization for Polars stability&lt;/li&gt;
&lt;li&gt;⚠️ Requires Polars dependency&lt;/li&gt;
&lt;li&gt;⚠️ Processes fewer rows (because of early filtering)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary: Choosing the Right Configuration for 100K+ Records
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Follow this decision tree:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is your data sorted (or can you sort it)?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;YES&lt;/strong&gt; Use &lt;strong&gt;Merge Join&lt;/strong&gt; (&lt;code&gt;ordered_by&lt;/code&gt; parameter)

&lt;ul&gt;
&lt;li&gt;Best memory efficiency &lt;/li&gt;
&lt;li&gt;Fast execution &lt;/li&gt;
&lt;li&gt;True streaming&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is your data NOT sorted?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;YES&lt;/strong&gt; Use &lt;strong&gt;MMAP Join&lt;/strong&gt; (&lt;code&gt;filename&lt;/code&gt; parameter)

&lt;ul&gt;
&lt;li&gt;Low memory &lt;/li&gt;
&lt;li&gt;Works with unsorted data&lt;/li&gt;
&lt;li&gt;Good for large files&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do you need maximum throughput and can filter early?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;YES&lt;/strong&gt; Use &lt;strong&gt;Polars + Optimizations&lt;/strong&gt; (&lt;code&gt;use_polars=True&lt;/code&gt; + protocols)

&lt;ul&gt;
&lt;li&gt;Fastest execution &lt;/li&gt;
&lt;li&gt;Low memory &lt;/li&gt;
&lt;li&gt;Requires protocol support&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Default fallback:&lt;/strong&gt; If none of the above apply, use &lt;strong&gt;Lookup Join&lt;/strong&gt; (default Python) - it's stable and works with any data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Comparison Table: 100K+ Records (150K+ Joined Rows)
&lt;/h3&gt;

&lt;p&gt;Based on comprehensive benchmarks with &lt;strong&gt;150,048 joined rows&lt;/strong&gt; (50,089 products with ~3 images each, filtered to &lt;code&gt;checked=1&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Rows Processed&lt;/th&gt;
&lt;th&gt;Time (s)&lt;/th&gt;
&lt;th&gt;Memory Overhead (MB)&lt;/th&gt;
&lt;th&gt;CPU %&lt;/th&gt;
&lt;th&gt;Throughput (rows/s)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. Merge Join (Sorted Data)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,048&lt;/td&gt;
&lt;td&gt;159.51&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;69.50&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.9%&lt;/td&gt;
&lt;td&gt;941&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. Lookup Join (Default Python)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,048&lt;/td&gt;
&lt;td&gt;159.44&lt;/td&gt;
&lt;td&gt;236.12&lt;/td&gt;
&lt;td&gt;1.8%&lt;/td&gt;
&lt;td&gt;941&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. Polars Join&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,048&lt;/td&gt;
&lt;td&gt;157.74&lt;/td&gt;
&lt;td&gt;285.64&lt;/td&gt;
&lt;td&gt;4.1%&lt;/td&gt;
&lt;td&gt;951&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4. MMAP Join&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,048&lt;/td&gt;
&lt;td&gt;165.74&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;77.48&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.0%&lt;/td&gt;
&lt;td&gt;905&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5. Polars + Column Pruning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,048&lt;/td&gt;
&lt;td&gt;157.72&lt;/td&gt;
&lt;td&gt;243.85&lt;/td&gt;
&lt;td&gt;3.1%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;951&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6. Polars + Filter Pushdown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;150,048&lt;/td&gt;
&lt;td&gt;157.74&lt;/td&gt;
&lt;td&gt;239.38&lt;/td&gt;
&lt;td&gt;2.0%&lt;/td&gt;
&lt;td&gt;951&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7. All Optimizations Combined&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;50,089&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;54.30&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;46.68&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.6%&lt;/td&gt;
&lt;td&gt;922&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Configuration 7 processes fewer rows (50,089 vs 150,048) because it applies filter pushdown early, filtering products with &lt;code&gt;checked=1&lt;/code&gt; before the join. This demonstrates the power of early filtering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Insights for 100K+ Records
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fastest Execution:&lt;/strong&gt; All Optimizations Combined (54.30s)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Polars Join + Column Pruning + Filter Pushdown + early filtering&lt;/li&gt;
&lt;li&gt;Processes 50,089 rows (filtered early) vs 150,048 without filtering&lt;/li&gt;
&lt;li&gt;Best for speed priority when you can filter early&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lowest Memory (Full Join):&lt;/strong&gt; Merge Join (69.50 MB)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;True streaming, no index needed&lt;/li&gt;
&lt;li&gt;Best for memory-constrained environments with sorted data&lt;/li&gt;
&lt;li&gt;Processes all 150,048 rows with minimal memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lowest Memory (Unsorted Data):&lt;/strong&gt; MMAP Join (77.48 MB)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works with unsorted data&lt;/li&gt;
&lt;li&gt;OS-managed memory mapping&lt;/li&gt;
&lt;li&gt;Best for large files when data is not sorted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Highest Throughput:&lt;/strong&gt; Polars + Column Pruning (951 rows/s)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vectorized operations + reduced I/O&lt;/li&gt;
&lt;li&gt;Best for processing large volumes&lt;/li&gt;
&lt;li&gt;Processes all 150,048 rows efficiently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Balance:&lt;/strong&gt; Merge Join (159.51s, 69.50 MB)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good speed with lowest memory overhead&lt;/li&gt;
&lt;li&gt;Best for sorted data when memory matters&lt;/li&gt;
&lt;li&gt;True streaming architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  By Dataset Size
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt; 10K rows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;use_polars=False&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;Fastest, most stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10K-100K rows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;use_polars=False&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;Still fastest, handles mixed types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;100K-1M rows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;See recommendations below&lt;/td&gt;
&lt;td&gt;Choose based on data characteristics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt; 1M rows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All optimizations&lt;/td&gt;
&lt;td&gt;Maximum performance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;For 100K-1M rows, choose based on the data:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data is sorted&lt;/strong&gt; &lt;strong&gt;Merge Join&lt;/strong&gt; (&lt;code&gt;ordered_by&lt;/code&gt; parameter) &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data is NOT sorted&lt;/strong&gt; &lt;strong&gt;MMAP Join&lt;/strong&gt; (&lt;code&gt;filename&lt;/code&gt; parameter) - &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Need maximum speed + can filter early&lt;/strong&gt; &lt;strong&gt;Polars + Optimizations&lt;/strong&gt; (&lt;code&gt;use_polars=True&lt;/code&gt; + protocols)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pitfall 1: Using Polars Without Normalization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Mixed types cause schema inference errors
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalized_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;raw_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pitfall 2: Using MMAP Without Polars (Very Slow)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Very slow!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# MMAP uses Polars internally for index building if available
# But you still need use_polars=False for MMAP Join algorithm
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_polars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# MMAP Join requires this
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# MMAP will use Polars internally for faster index building
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pitfall 3: Using MMAP for Small Files
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# MMAP overhead &amp;gt; benefit for small files
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;small.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Slower!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# No filename for small files
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Faster for &amp;lt; 100MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Start Here (Most Secure)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Default: use_polars=False
&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Most stable, handles all edge cases, works with any data types&lt;/p&gt;

&lt;h3&gt;
  
  
  Then Experiment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add debug mode&lt;/strong&gt;: &lt;code&gt;Engine(debug=True)&lt;/code&gt; - See what's happening&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try Polars&lt;/strong&gt;: &lt;code&gt;Engine(use_polars=True)&lt;/code&gt; - For large datasets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try MMAP&lt;/strong&gt;: &lt;code&gt;filename="data.jsonl"&lt;/code&gt; - For large files &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try Merge Join&lt;/strong&gt;: &lt;code&gt;ordered_by="key"&lt;/code&gt; - For sorted data &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Streaming SQL Engine fills a unique niche: &lt;strong&gt;cross-system data integration&lt;/strong&gt;. While it may not match the raw performance of specialized tools for their specific use cases, it excels at joining data from different systems - a problem that traditional databases cannot solve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-system joins (databases, APIs, files)&lt;/li&gt;
&lt;li&gt;Zero infrastructure requirements&lt;/li&gt;
&lt;li&gt;Memory-efficient streaming architecture&lt;/li&gt;
&lt;li&gt;Python-native integration&lt;/li&gt;
&lt;li&gt;Automatic optimizations&lt;/li&gt;
&lt;li&gt;Simple deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best suited for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microservices data aggregation&lt;/li&gt;
&lt;li&gt;Cross-system ETL pipelines&lt;/li&gt;
&lt;li&gt;Real-time data integration&lt;/li&gt;
&lt;li&gt;Memory-constrained environments&lt;/li&gt;
&lt;li&gt;Python-native workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For cross-system data integration, the Streaming SQL Engine provides a unique solution that balances performance, simplicity, and flexibility.&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>dataengineering</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
