<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://episclera.github.io/portqueue/blog</id>
    <title>PortQueue Blog</title>
    <updated>2026-07-22T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://episclera.github.io/portqueue/blog"/>
    <subtitle>PortQueue Blog</subtitle>
    <icon>https://episclera.github.io/portqueue/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[Introducing PortQueue]]></title>
        <id>https://episclera.github.io/portqueue/blog/introducing-portqueue</id>
        <link href="https://episclera.github.io/portqueue/blog/introducing-portqueue"/>
        <updated>2026-07-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introducing PortQueue, a broker-less, strictly typed job queue for Node.js that runs on PostgreSQL, Oracle, PGLite, and SQLite from one API.]]></summary>
        <content type="html"><![CDATA[<p><strong>PortQueue is a database-backed job queue for Node.js</strong> — no broker required. If your
app already runs a relational database, you already have everything you need for a
reliable queue. PortQueue treats that database as the single source of truth and works
across PostgreSQL, Oracle, PGLite, and SQLite from one API.</p>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="why-another-queue">Why another queue?<a href="https://episclera.github.io/portqueue/blog/introducing-portqueue#why-another-queue" class="hash-link" aria-label="Direct link to Why another queue?" title="Direct link to Why another queue?" translate="no">​</a></h2>
<p>Most Node queues assume Redis. That's a great fit for some teams and an unwanted extra
moving part for many others — a second datastore to run, secure, back up, and reason
about. Meanwhile, PostgreSQL with <code>SKIP LOCKED</code> and <code>LISTEN/NOTIFY</code> is more than enough for
a robust queue, as pg-boss showed. PortQueue takes that insight and makes it
<strong>multi-driver</strong>, <strong>strictly typed</strong>, and <strong>transactional</strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="what-you-get">What you get<a href="https://episclera.github.io/portqueue/blog/introducing-portqueue#what-you-get" class="hash-link" aria-label="Direct link to What you get" title="Direct link to What you get" translate="no">​</a></h2>
<ul>
<li class=""><strong>One API across every engine.</strong> Swap the driver import; your app code is unchanged.</li>
<li class=""><strong>One process or many.</strong> One <code>pq</code> object produces and consumes, so running everything on a
single server is a complete setup — and splitting producers from consumers is there when you
want it.</li>
<li class=""><strong>Transactional enqueue.</strong> A job exists only if your business transaction commits — the
dual-write bug, gone.</li>
<li class=""><strong>Operational maturity.</strong> Retries with backoff, rate limiting, backpressure, cron with
timezones, a dashboard, and a CLI — no third-party service.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="a-first-taste">A first taste<a href="https://episclera.github.io/portqueue/blog/introducing-portqueue#a-first-taste" class="hash-link" aria-label="Direct link to A first taste" title="Direct link to A first taste" translate="no">​</a></h2>
<div class="language-ts codeBlockContainer_mQmQ theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class="codeBlockContent_t_Hd"><pre tabindex="0" class="prism-code language-ts codeBlock_RMoD thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class="codeBlockLines_AclH"><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> PortQueue </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">from</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'@portqueue/core'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> PostgresqlDriver </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">from</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'@portqueue/core/drivers/postgresql'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">const</span><span class="token plain"> pq </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">new</span><span class="token plain"> </span><span class="token class-name" style="color:hsl(35, 99%, 36%)">PortQueue</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> driver</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">new</span><span class="token plain"> </span><span class="token class-name" style="color:hsl(35, 99%, 36%)">PostgresqlDriver</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> connectionString</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token constant" style="color:hsl(35, 99%, 36%)">DATABASE_URL</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">await</span><span class="token plain"> pq</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">init</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)">// Create the queue and provide a jobs handler.</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain">pq</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">createQueue</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain">  name</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'emails'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain">  limiter</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> concurrency</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">10</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain">  </span><span class="token function-variable function" style="color:hsl(221, 87%, 60%)">handler</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">job</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">await</span><span class="token plain"> </span><span class="token function" style="color:hsl(221, 87%, 60%)">send</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">job</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">data</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)">// Add a job to the queue.</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">await</span><span class="token plain"> pq</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">addJob</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> queue</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'emails'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> data</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> to</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'a@b.com'</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><br></div></code></pre></div></div>
<p>Free and open source. Commercial use permitted. No license fee, no obligations.</p>
<p>Head to <a class="" href="https://episclera.github.io/portqueue/docs/getting-started">Getting Started</a> to try it.</p>]]></content>
        <author>
            <name>Gheorghe Gherciu</name>
        </author>
        <category label="announcement" term="announcement"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why we built a broker-less, database-native job queue]]></title>
        <id>https://episclera.github.io/portqueue/blog/broker-less-database-native-queue</id>
        <link href="https://episclera.github.io/portqueue/blog/broker-less-database-native-queue"/>
        <updated>2026-07-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Why PortQueue is a broker-less, database-native job queue using SKIP LOCKED and LISTEN/NOTIFY, so your existing SQL database is all you need.]]></summary>
        <content type="html"><![CDATA[<p>Adding a queue shouldn't mean adding a broker. If you already run PostgreSQL (or SQLite, or
Oracle), you already have durable storage, transactions, and — with <code>SKIP LOCKED</code> and
<code>LISTEN/NOTIFY</code> — everything a robust queue needs.</p>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="the-hidden-cost-of-a-second-datastore">The hidden cost of a second datastore<a href="https://episclera.github.io/portqueue/blog/broker-less-database-native-queue#the-hidden-cost-of-a-second-datastore" class="hash-link" aria-label="Direct link to The hidden cost of a second datastore" title="Direct link to The hidden cost of a second datastore" translate="no">​</a></h2>
<p>A Redis-backed queue is fast, but it's another system to run, secure, monitor, back up,
and keep consistent with your primary database. And the moment you want "enqueue this job
<strong>only if</strong> the order row commits," a separate datastore turns into a distributed-systems
problem — the classic <strong>dual-write bug</strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="when-db-backed-wins">When DB-backed wins<a href="https://episclera.github.io/portqueue/blog/broker-less-database-native-queue#when-db-backed-wins" class="hash-link" aria-label="Direct link to When DB-backed wins" title="Direct link to When DB-backed wins" translate="no">​</a></h2>
<ul>
<li class="">You already operate a relational database and don't want a broker.</li>
<li class="">You need <strong>transactional enqueue</strong> — the job and the business write commit together.</li>
<li class="">You want jobs to be <strong>queryable with SQL</strong> and visible in a dashboard without a separate
result backend.</li>
<li class="">You ship to customers on <strong>different databases</strong> and want one codebase.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="what-we-kept-broker-like">What we kept broker-like<a href="https://episclera.github.io/portqueue/blog/broker-less-database-native-queue#what-we-kept-broker-like" class="hash-link" aria-label="Direct link to What we kept broker-like" title="Direct link to What we kept broker-like" translate="no">​</a></h2>
<p>Low latency still matters, so where the engine supports it (PostgreSQL) workers wake within
milliseconds via <code>LISTEN/NOTIFY</code>; elsewhere they poll on a tunable interval. You get
broker-like responsiveness without the broker.</p>
<p>Read how it <a class="" href="https://episclera.github.io/portqueue/docs/scale-horizontally">scales horizontally</a>, or start with
<a class="" href="https://episclera.github.io/portqueue/docs/getting-started">Getting Started</a>.</p>]]></content>
        <author>
            <name>Gheorghe Gherciu</name>
        </author>
        <category label="design" term="design"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[PortQueue vs pg-boss vs BullMQ vs Celery]]></title>
        <id>https://episclera.github.io/portqueue/blog/portqueue-vs-pgboss-bullmq-celery</id>
        <link href="https://episclera.github.io/portqueue/blog/portqueue-vs-pgboss-bullmq-celery"/>
        <updated>2026-07-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[PortQueue vs pg-boss, BullMQ, and Celery — a multi-driver, distributed, transactional job queue that runs on any SQL engine with no broker.]]></summary>
        <content type="html"><![CDATA[<p>An honest look at where each of these shines — and where PortQueue's multi-driver,
distributed-by-design, transactional approach matters.</p>
<table><thead><tr><th></th><th>PortQueue</th><th>pg-boss</th><th>BullMQ</th><th>Celery</th></tr></thead><tbody><tr><td>Storage</td><td>Any SQL engine (7 drivers)</td><td>PostgreSQL only</td><td>Redis</td><td>Redis/RabbitMQ + result backend</td></tr><tr><td>Broker required</td><td>No</td><td>No</td><td>Yes</td><td>Yes</td></tr><tr><td>Same code, any engine</td><td>Yes — swap the driver</td><td>PostgreSQL only</td><td>Redis only</td><td>Broker only</td></tr><tr><td>Distributed instances</td><td>Yes (<code>SKIP LOCKED</code>, opt-in keys)</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>Transactional enqueue</td><td>Yes (your tx)</td><td>Limited</td><td>No</td><td>No</td></tr><tr><td>Resumable multi-step</td><td>Yes (each step a row)</td><td>No</td><td>Flows (Redis)</td><td>Chains/canvas</td></tr><tr><td>Fault tolerance</td><td>Timeouts, retries, stall recovery</td><td>Retries</td><td>Retries, stalled checks</td><td>Retries, acks-late</td></tr><tr><td>Local testing</td><td>In-process PGLite / SQLite</td><td>Needs PostgreSQL</td><td>Needs Redis</td><td>Needs broker</td></tr><tr><td>Strict typing</td><td>End to end</td><td>Partial</td><td>Partial</td><td>N/A</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="what-makes-portqueue-different">What makes PortQueue different<a href="https://episclera.github.io/portqueue/blog/portqueue-vs-pgboss-bullmq-celery#what-makes-portqueue-different" class="hash-link" aria-label="Direct link to What makes PortQueue different" title="Direct link to What makes PortQueue different" translate="no">​</a></h2>
<ul>
<li class=""><strong>Great for distributed instances.</strong> Any number of workers, on any number of servers,
claim from the same queue with <code>SKIP LOCKED</code> and never double-process. Multi-instance
mode is a one-line opt-in.</li>
<li class=""><strong>Truly multi-driver — same code, different engines.</strong> The same application code runs
unchanged on PostgreSQL, MySQL, Oracle, PGLite, and SQLite. You swap the
driver, not your code.</li>
<li class=""><strong>Easy to test.</strong> Run your entire suite in-process against PGLite or SQLite — no Redis,
no broker, no external database to stand up.</li>
<li class=""><strong>Fault tolerant.</strong> Visibility timeouts, retries with backoff, and stalled-job recovery
mean an instance can crash mid-job without losing work.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="where-each-shines">Where each shines<a href="https://episclera.github.io/portqueue/blog/portqueue-vs-pgboss-bullmq-celery#where-each-shines" class="hash-link" aria-label="Direct link to Where each shines" title="Direct link to Where each shines" translate="no">​</a></h2>
<ul>
<li class=""><strong>pg-boss</strong> — a great, focused PostgreSQL queue. If you're PostgreSQL-only and happy with
its API, it's excellent. PortQueue generalizes the idea to many engines — the same code
runs on seven of them — with richer results and in-process testing.</li>
<li class=""><strong>BullMQ</strong> — the gold standard on Redis, with a clean producer/consumer split we
admire and adopted. If Redis is already core to your stack, it's a strong choice.
PortQueue trades Redis for your existing database and swaps numeric progress for
resumable steps.</li>
<li class=""><strong>Celery</strong> — battle-tested operational maturity in the Python world (retries, rate
limits, beat, visibility timeouts). PortQueue brings that maturity to typed Node.js
and keeps results in the same database.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="why-the-differences-matter">Why the differences matter<a href="https://episclera.github.io/portqueue/blog/portqueue-vs-pgboss-bullmq-celery#why-the-differences-matter" class="hash-link" aria-label="Direct link to Why the differences matter" title="Direct link to Why the differences matter" translate="no">​</a></h2>
<p>If you want <strong>transactional enqueue</strong> or must run on a <strong>customer-chosen database</strong>, the
broker-based options can't help cleanly. That's the gap PortQueue fills.</p>
<p>See <a class="" href="https://episclera.github.io/portqueue/docs/getting-started">Getting Started</a> to try it.</p>]]></content>
        <author>
            <name>Gheorghe Gherciu</name>
        </author>
        <category label="comparison" term="comparison"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Transactional enqueue: killing the dual-write bug]]></title>
        <id>https://episclera.github.io/portqueue/blog/transactional-enqueue-dual-write</id>
        <link href="https://episclera.github.io/portqueue/blog/transactional-enqueue-dual-write"/>
        <updated>2026-07-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How PortQueue enqueues jobs inside your own database transaction to kill the dual-write bug, so a job exists only if your business write commits.]]></summary>
        <content type="html"><![CDATA[<p>You write an order row, then enqueue its fulfillment job. What happens if the process
dies between the two? Or the transaction rolls back after the job was queued? You get a
job referencing data that doesn't exist — the <strong>dual-write bug</strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="the-fix-join-the-developers-transaction">The fix: join the developer's transaction<a href="https://episclera.github.io/portqueue/blog/transactional-enqueue-dual-write#the-fix-join-the-developers-transaction" class="hash-link" aria-label="Direct link to The fix: join the developer's transaction" title="Direct link to The fix: join the developer's transaction" translate="no">​</a></h2>
<p>PortQueue lets the job insert run <strong>inside your own transaction</strong>. The job exists <strong>iff</strong>
your business write commits, and the <code>NOTIFY</code> that wakes workers fires only <strong>after</strong>
commit.</p>
<div class="language-ts codeBlockContainer_mQmQ theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class="codeBlockContent_t_Hd"><pre tabindex="0" class="prism-code language-ts codeBlock_RMoD thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class="codeBlockLines_AclH"><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> fromKnex </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">from</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'@portqueue/core/tx'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">await</span><span class="token plain"> knex</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">transaction</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token keyword" style="color:hsl(301, 63%, 40%)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">trx</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain">  </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">await</span><span class="token plain"> </span><span class="token function" style="color:hsl(221, 87%, 60%)">trx</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token string" style="color:hsl(119, 34%, 47%)">'order'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">insert</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">order</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain">  </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">await</span><span class="token plain"> pq</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">addJob</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> queue</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(119, 34%, 47%)">'fulfillment'</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> data</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"> orderId </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> tx</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token function" style="color:hsl(221, 87%, 60%)">fromKnex</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">trx</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// your trx owns commit/rollback</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_tleR" id="why-portqueue-never-owns-the-transaction">Why PortQueue never owns the transaction<a href="https://episclera.github.io/portqueue/blog/transactional-enqueue-dual-write#why-portqueue-never-owns-the-transaction" class="hash-link" aria-label="Direct link to Why PortQueue never owns the transaction" title="Direct link to Why PortQueue never owns the transaction" translate="no">​</a></h2>
<p>There is no <code>pq.transaction(fn)</code> and no <code>adoptTransaction</code>. You start the transaction
with your ORM or query-builder, wrap it with a small <strong>adapter</strong> (<code>fromKnex</code>, <code>fromPg</code>,
<code>fromPrisma</code>, <code>fromDrizzle</code>, …), and pass it as <code>tx</code>. Keeping ownership in your code means
the queue never surprises you by committing early or holding a connection.</p>
<p>Read the full <a class="" href="https://episclera.github.io/portqueue/docs/guides/transactions">Transactions guide</a>.</p>]]></content>
        <author>
            <name>Gheorghe Gherciu</name>
        </author>
        <category label="design" term="design"/>
        <category label="transactions" term="transactions"/>
    </entry>
</feed>