<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://duggan.ie/tag/infrastructure/</id>
  <title type="text">@duggan — infrastructure</title>
  <updated>2026-01-05T20:59:48.000Z</updated>
  <author>
    <name>Ross Duggan</name>
    <email>ross@duggan.ie</email>
    <uri>https://duggan.ie/</uri>
  </author>
  <icon>https://duggan.ie/favicon.ico</icon>
  <link href="https://duggan.ie/tag/infrastructure/atom.xml" rel="first"/>
  <link href="https://duggan.ie/tag/infrastructure/atom.xml?page=1" rel="last"/>
  <link href="https://duggan.ie/tag/infrastructure/atom.xml" rel="self"/>
  <logo>https://duggan.ie/og-image.png</logo>
  <rights type="text">All rights reserved 2026, Ross Duggan</rights>
  <subtitle type="text">Posts tagged infrastructure</subtitle>
  <entry>
    <id>https://duggan.ie/posts/self-hosting-git-and-builds-without-running-a-bunch-of-web-services</id>
    <title type="text">Self-hosting git and builds without running a bunch of web services</title>
    <updated>2026-01-05T20:59:48.000Z</updated>
    <author>
      <name>Ross Duggan</name>
      <email>ross@duggan.ie</email>
      <uri>https://duggan.ie/</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;My main side project at the moment is this blog. I tinker away with it when I feel like it. I'm not working on it with anyone else, not fielding pull requests, and not writing documentation.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;That said, I do still like having it in version control. I like having a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;Dockerfile&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; to encapsulate dependencies and configuration, and using docker compose as a portable runtime manager. It's probably not everyone's idea of a simple setup, but I like it.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The blog is deployed to a small VPS in &lt;/span&gt;&lt;a href="http://hetzner.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Hetzner&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, running behind nginx.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;GitHub, Github Actions, and the GitHub Container Registry have been how I've been handling builds, but it's been slow, and it's started to seem absurd to have all these builds occurring in some random datacenter presumably on the other side of the Atlantic ocean, that I am then pulling back to a server in Europe.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I started fishing around for a locally hosted replacement, coming across &lt;/span&gt;&lt;a href="http://forgejo.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Forgejo&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;a href="http://woodpecker-ci.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Woodpecker CI&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and &lt;/span&gt;&lt;a href="https://onedev.io" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;OneDev&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; in addition to more familiar names like &lt;/span&gt;&lt;a href="https://gitlab.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;GitLab&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;a href="http://sourcehut.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;SourceHut&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;At some point it occurred to me that this was a lot of ceremony for something relatively simple. How hard could it be just to have a git remote and hang some builds off it? I'm not trying to launch my own GitHub here.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I want to:&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li value="1"&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;git push&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; to a &lt;/span&gt;&lt;span class="tooltip-wrapper tooltip-trigger cursor-help underline decoration-dotted" data-tooltip="true"&gt;&lt;span style="white-space: pre-wrap;"&gt;central&lt;/span&gt;&lt;template data-tooltip-content=""&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I code from a couple of different machines, so having an actual central remote upstream makes things easier.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;You could probably do a lot of this on just your local machine with a few tweaks.&lt;/span&gt;&lt;/p&gt;&lt;/template&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; repo&lt;/span&gt;&lt;/li&gt;&lt;li value="2"&gt;&lt;span style="white-space: pre-wrap;"&gt;When I push, it kicks of a container image build&lt;/span&gt;&lt;/li&gt;&lt;li value="3"&gt;&lt;span style="white-space: pre-wrap;"&gt;That image is pushed to an image registry that I can deploy from&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This does not need several UIs and databases. In any case git being git, you can always switch out for something more complex later.&lt;/span&gt;&lt;/p&gt;&lt;aside class="callout my-6"&gt;&lt;div class="border-l-4 border-blue-500 bg-blue-50 dark:bg-blue-900/20 px-6 py-4 rounded-r-lg"&gt;&lt;strong class="block text-sm font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-300"&gt;Note&lt;/strong&gt;&lt;div class="mt-2 text-base leading-relaxed"&gt;&lt;p class="mb-0"&gt;One piece of plumbing that makes all this much more flexible is Tailscale. It is not strictly required, but having the various machines on the same private network, regardless of their physical location, makes some parts easier.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;&lt;h3 id="hosting-a-git-repo" class="group relative"&gt;&lt;a href="#hosting-a-git-repo" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Hosting a git repo&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On GitHub, an SSH clone URL looks something like:&lt;/span&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;git@github.com:duggan/duggan.ie.git&lt;/span&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;It's an SSH URI just like it would be with &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;scp&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; or &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;rsync&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;. A helpful breakdown from &lt;/span&gt;&lt;a href="https://stackoverflow.com/a/70330178" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Mike Slinn and Jaditpol on StackOverflow&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4"&gt;&lt;span style="white-space: pre-wrap;"&gt;    git@github&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;com&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;myuser&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;myrepo&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    \_&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; \________&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; \_______________&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;     &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;|&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;|&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;              &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;|&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    user   host           path&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Since I'm the only user, I don't need to get fancy, and I can just use my own login.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;origin&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; of a git repo is more or less just the contents of the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;.git&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; directory in a remote location. That's it. You don't even need to run a git server if you're happy enough using ssh for transport.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I put it outside my home path just to keep it a little out of the way, as I won't need to interact with it too often:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6"&gt;&lt;span style="white-space: pre-wrap;"&gt;sudo mkdir &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;p &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;sudo chown &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;R&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;USER&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;USER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;cd &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;mkdir example&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;cd example&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;git init &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;bare&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;In my case I've replaced my GitHub &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;origin&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; with this one directly in the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;.git/config&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; of my local checkout, but when I was testing the waters I just added an additional remote:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1"&gt;&lt;span style="white-space: pre-wrap;"&gt;$ git remote add test ross@buildmachine&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;duggan&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;ie&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="building-an-image-on-push" class="group relative"&gt;&lt;a href="#building-an-image-on-push" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Building an image on push&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Git's own hooks system, combined with a Makefile and Docker, is enough to put together a pretty flexible build system for anything you can deploy using a container.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;In my newly minted upstream repo, I added a file named &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;post-receive&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; into the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;hooks&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; directory, i.e., &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;/srv/git/duggan.ie.git/hooks/post-receive&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; (and &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;chmod +x&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; it):&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38"&gt;&lt;span style="white-space: pre-wrap;"&gt;#!/bin/bash&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;set &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;e&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;# Read the push &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;info&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;branch being updated&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;while&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; read oldrev newrev refname&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;do&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; # Only trigger on main branch&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $refname &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;==&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"refs/heads/main"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; then&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"=== Build triggered for main branch ==="&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Get repo name from current directory&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPO_NAME&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;basename &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$(pwd)"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WORK_TREE&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"/tmp/git-build-${REPO_NAME}"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;GIT_DIR&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;pwd&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Clean checkout&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; rm &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;rf &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mkdir &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;p &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; git &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;work&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;tree&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;dir&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$GIT_DIR"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; checkout &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;f main&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cd &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Check &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Makefile and build target&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;f Makefile &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; then&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; grep &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;q &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"^build:"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Makefile&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; then&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"=== Running make build ==="&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; make build&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"=== Build complete ==="&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;else&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Makefile found but no 'build' target, skipping"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fi&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;else&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"No Makefile found, skipping build"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Cleanup&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; rm &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;rf &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; fi&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;done&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;When I push to the repo, this looks for a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;Makefile&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; in the root of the repo, checks for a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;build&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; command and executes it.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;figure class="video-wrapper"&gt;&lt;video src="/files/4ab4a809cd6e7fa2.mp4" autoplay="" muted="" loop="" playsinline="" data-playback="silentLoop" title="push1.mp4" style="max-width: 100%; height: auto"&gt;&lt;/video&gt;&lt;/figure&gt;&lt;span style="white-space: pre-wrap;"&gt;My &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;Makefile&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; is super basic, just:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IMAGE&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;localhost&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;5000&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;duggan&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;ie&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;latest&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;build&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;docker build &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;t &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IMAGE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;docker push &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IMAGE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;As a bonus, I could have thrown that post-receive script into a &lt;/span&gt;&lt;span class="tooltip-wrapper tooltip-trigger cursor-help underline decoration-dotted" data-tooltip="true"&gt;&lt;span style="white-space: pre-wrap;"&gt;git template&lt;/span&gt;&lt;template data-tooltip-content=""&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;For example, you can throw `post-receive` into a directory at `/srv/git/template` then configure via the git config:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;git config --global init.templateDir /srv/git/template&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;/template&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; so that new repos are automatically created with a copy of the post receive script, but it doesn't seem necessary just yet.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On the build machine I've also enabled my user account to run docker directly without requiring &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;sudo&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;div class="link-preview-card border border-gray-200 rounded-lg overflow-hidden my-4"&gt;&lt;a href="https://docs.docker.com/engine/install/linux-postinstall/" target="_blank" rel="noopener noreferrer" class="no-underline text-inherit flex flex-row"&gt;&lt;div class="flex-1 p-3 min-w-0"&gt;&lt;div class="link-preview-site flex items-center gap-1.5 mb-2 text-xs text-gray-500"&gt;&lt;img src="/files/7d2f187075a49630.svg" class="w-4 h-4 m-0"&gt;&lt;span&gt;Docker Documentation&lt;/span&gt;&lt;/div&gt;&lt;div class="link-preview-title font-semibold text-base leading-tight mb-1"&gt;Post-installation steps&lt;/div&gt;&lt;div class="link-preview-description text-sm text-gray-500 leading-snug line-clamp-2"&gt;Find the recommended Docker Engine post-installation steps for Linux users, including how to run Docker as a non-root user and more.&lt;/div&gt;&lt;div class="text-xs text-gray-400 mt-2"&gt;docs.docker.com&lt;/div&gt;&lt;/div&gt;&lt;div class="w-[200px] shrink-0 overflow-hidden bg-gray-100"&gt;&lt;img src="/files/9fdaa65db57e044d.webp" alt="Post-installation steps" class="w-full h-full object-cover m-0"&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;h3 id="hosting-an-image" class="group relative"&gt;&lt;a href="#hosting-an-image" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Hosting an image&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;At this point the image is built, which will be fine if I just wanted to run the container on the same machine, but I need to deploy this to my little Hetzner VPS.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This is where Tailscale comes in handy, as it lets me have Docker's own &lt;/span&gt;&lt;a href="https://distribution.github.io/distribution/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;container registry&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; running on my build server, which I then pull from on the VPS. It's very straightforward to set up.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On the build server I have a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;docker-compose.yml&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; file with the registry configured:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11"&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;services&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; registry&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; image&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; registry&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; container_name&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; registry&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; restart&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; always&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; ports&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"5000:5000"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; volumes&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;data&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;var&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;lib&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;registry&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; environment&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REGISTRY_STORAGE_DELETE_ENABLED&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"true"&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="deploying-from-a-private-registry" class="group relative"&gt;&lt;a href="#deploying-from-a-private-registry" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Deploying from a private registry&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;With the registry and VPS both on the Tailscale network, I can allow regular HTTP traffic, which requires a small tweak to Docker on the VPS.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;In &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;/etc/docker/daemon.json&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3"&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"insecure-registries"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"buildmachine:5000"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;With that done, I'm able to update my &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;make deploy&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; command to point at the new registry:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4"&gt;&lt;span style="white-space: pre-wrap;"&gt;services&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; web&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; image&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; buildmachine&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;5000&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;duggan&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;ie&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;latest&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This all works much more quickly than before, and since it's running the builds over SSH directly, I'm getting feedback faster than I would refreshing a build log.&lt;/span&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;There is currently no comments system. If you'd like to share an opinion either with me or about this post, please feel free to do so with me either via email (&lt;/em&gt;&lt;/i&gt;&lt;a href="mailto:ross@duggan.ie" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;ross@duggan.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) on Mastodon (&lt;/em&gt;&lt;/i&gt;&lt;a href="http://mastodon.ie/@duggan" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;@duggan@mastodon.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) or even on &lt;/em&gt;&lt;/i&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;Hacker News&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;.&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;</content>
    <link href="https://duggan.ie/posts/self-hosting-git-and-builds-without-running-a-bunch-of-web-services" rel="alternate"/>
    <published>2026-01-12T00:12:06.449Z</published>
    <summary type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;GitHub, Github Actions, and the GitHub Container Registry have been how I've been handling builds, but it's been slow, and it's started to seem absurd to have all these builds occurring in some random datacenter presumably on the other side of the Atlantic ocean, that I am then pulling back to a server in Europe.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I started fishing around for a locally hosted replacement, coming across &lt;/span&gt;&lt;a href="http://forgejo.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Forgejo&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;a href="http://woodpecker-ci.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Woodpecker CI&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and &lt;/span&gt;&lt;a href="https://onedev.io" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;OneDev&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; in addition to more familiar names like &lt;/span&gt;&lt;a href="https://gitlab.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;GitLab&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;a href="http://sourcehut.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;SourceHut&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;At some point it occurred to me that this was a lot of ceremony for something relatively simple. How hard could it be just to have a git remote and hang some builds off it? I'm not trying to launch my own GitHub here.&lt;/span&gt;&lt;/p&gt;</summary>
  </entry>
  <entry>
    <id>https://duggan.ie/posts/hacking-together-a-job-runner-using-supabase-edge-functions</id>
    <title type="text">Hacking together a Job Runner using Supabase Edge Functions</title>
    <updated>2025-02-11T00:00:22.000Z</updated>
    <author>
      <name>Ross Duggan</name>
      <email>ross@duggan.ie</email>
      <uri>https://duggan.ie/</uri>
    </author>
    <content type="html">&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;If you're not familiar with &lt;/span&gt;&lt;a href="http://supabase.com/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Supabase&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, it's a collection of backend services (Postgres, PostgREST auth service, S3 storage, function runtime) that you can develop against locally and then deploy to production.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;The Supabase team have also spent time making each of these parts complement each other, and since they're stitching together different open source projects this is seriously impressive. They also have in-depth documentation and examples, and the dashboard is top notch.&lt;/span&gt;&lt;/p&gt;&lt;details class="bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg mb-2"&gt;&lt;summary class="cursor-pointer py-1 px-6 relative font-bold list-none outline-none text-zinc-900 dark:text-zinc-100 [&amp;::-webkit-details-marker]:hidden [&amp;::marker]:hidden before:content-[''] before:block before:absolute before:left-2 before:top-1/2 before:-translate-y-1/2 before:w-0 before:h-0 before:border-[6px] before:border-transparent before:border-l-black dark:before:border-l-white before:rotate-0 before:transition-transform [details[open]_&amp;]:before:rotate-90 [div[data-open]_&amp;]:before:rotate-90 hover:bg-zinc-100 dark:hover:bg-zinc-700/50"&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;ℹ️ What makes Supabase so useful?&lt;/span&gt;&lt;/p&gt;&lt;/summary&gt;&lt;div class="px-5 pb-1 pt-0 text-zinc-900 dark:text-zinc-100" data-lexical-collapsible-content="true"&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;For example, items uploaded to the storage service are accessible via the database. The auth service is also hooked directly into the database, with convenience functions to wire up user permissions to the authenticated users. This means you can enforce object storage access permissions (&lt;/span&gt;&lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;using Postgres Row-Level Security&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;) all the way through the stack via the database:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4"&gt;&lt;span style="white-space: pre-wrap;"&gt;create policy &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Individual user Access"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;on storage&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;objects &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; select&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;to authenticated&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;using&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;select auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uid&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; owner_id&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;uuid &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Additionally, the database is presented via a REST interface using &lt;/span&gt;&lt;a href="https://docs.postgrest.org/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;PostgREST&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;. With the aforementioned RLS policies, this gives you a permission-controlled API that you can auto-generate clients for. That means a React application can have a type-hinted, robustly authorized interface to the database – this makes it easier to build systems that are both secure and have a nice developer experience.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This is powerful stuff, and not necessarily easy to grok, but ultimately you're just learning how to use Postgres, so the knowledge is portable.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/details&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Anyway, this is all great stuff, but at some point I wanted to be able to run a bunch of asynchronous work in the background.&lt;/span&gt;&lt;/p&gt;&lt;h2 id="background-jobs" class="group relative"&gt;&lt;a href="#background-jobs" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Background jobs&lt;/span&gt;&lt;/h2&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;What you need out of a background job runner varies wildly. Supabase provides some helpful primitives around this that are enough to get started with –&amp;nbsp;you can fire off &lt;/span&gt;&lt;a href="https://supabase.com/docs/guides/functions" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Edge Functions&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; after a request and set them to process as &lt;/span&gt;&lt;a href="https://supabase.com/docs/guides/functions/background-tasks" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Background Tasks&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and you can use &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_cron&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; to run them on a schedule.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;If you just want to run a few static tasks regularly with Supabase, send emails, or fire off an API request after a user action, it's pretty straight forward. There are also ways to set up triggers, and &lt;/span&gt;&lt;a href="https://supabase.com/docs/guides/database/webhooks" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;fire webhooks&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; on database events.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;What I needed was a system where I could regularly run tasks in the context of a user –&amp;nbsp;AI agents running on behalf of the user, checking third party services for new information, etc. Something which might take a while to run, or is part of automation that will trigger another action, maybe alert a customer.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;I wanted to be able to queue up sometimes thousands of jobs to run –&amp;nbsp;this would help me stagger their execution so that I wasn't overwhelming either the Supabase Edge Function dispatcher or my own APIs.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;It was also important to me to keep the overall infrastructure stack as lean as possible, because I was deploying a stack per-company to keep resources isolated, as well as geographically proximate to the people using it.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: 400; text-decoration: none; white-space: pre-wrap;"&gt;There are, of course, third-party job runner systems like &lt;/span&gt;&lt;a href="http://inngest.com/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Inngest&lt;/span&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: 400; text-decoration: none; white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;a href="http://hatchet.run" rel="noreferrer"&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: 400; text-decoration: none; white-space: pre-wrap;"&gt;Hatchet&lt;/span&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 0, 0); font-weight: 400; text-decoration: none; white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;a href="https://trigger.dev/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;trigger&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, or &lt;/span&gt;&lt;a href="https://temporal.io" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Temporal&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, some of which are open source, have UIs, a local dev system, and I'm sure lots of other useful features. But this is a class of problem that ranges from "Google Calendar reminder for me to run a script manually on Sundays" to "distributed MapReduce operation across a Kubernetes cluster" –&amp;nbsp;you'll want something that suits your own circumstances.&lt;/span&gt;&lt;/p&gt;&lt;h2 id="cobbling-together-a-job-runner" class="group relative"&gt;&lt;a href="#cobbling-together-a-job-runner" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Cobbling together a job runner&lt;/span&gt;&lt;/h2&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;The Supabase Edge runtime environment takes the form of Deno functions triggered via HTTP, and authorized using &lt;/span&gt;&lt;a href="https://jwt.io/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;JWT&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;. They are normal HTTP API endpoints, you can send payloads to them, stream results and return status codes.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Supabase also provides a PostgreSQL database with a variety of Postgres extensions installed, including &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_cron&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_net&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;With those pieces in place, we have the primitives for a system that can:&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li value="1" style="text-align: start;"&gt;&lt;span style="white-space: pre-wrap;"&gt;store a list of jobs and config&lt;/span&gt;&lt;/li&gt;&lt;li value="2" style="text-align: start;"&gt;&lt;span style="white-space: pre-wrap;"&gt;run Typescript functions (with some resource limits, see &lt;/span&gt;&lt;a href="#caveats" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Caveats&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;/li&gt;&lt;li value="3" style="text-align: start;"&gt;&lt;span style="white-space: pre-wrap;"&gt;run tasks on a schedule&lt;/span&gt;&lt;/li&gt;&lt;li value="4" style="text-align: start;"&gt;&lt;span style="white-space: pre-wrap;"&gt;execute and evaluate the success of functions called over HTTP&lt;/span&gt;&lt;/li&gt;&lt;li value="5"&gt;&lt;span style="white-space: pre-wrap;"&gt;propagate user authorization context into the edge functions&lt;/span&gt;&lt;/li&gt;&lt;li value="6"&gt;&lt;span style="white-space: pre-wrap;"&gt;be permission controlled via RLS&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;The basic idea is that you write your jobs as Supabase Edge Functions, and you control when they're executed and what data they are passed using the database. The name of the job should map directly to the name of the Supabase Edge Function associated with it. &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_cron&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; is used to pick up new jobs and clean up old ones.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Because it's implemented in the database, you can use it via Supabase's dashboard too, so if you want to see what jobs are running, fix them or run them manually, you can do it easily via the UI:&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;figure class="video-wrapper"&gt;&lt;video src="/files/5f94342354608ec1.mp4" controls="" title="queue_job.mp4"&gt;&lt;/video&gt;&lt;figcaption class="not-prose font-sans text-sm text-neutral-400 whitespace-pre-wrap break-words mt-2"&gt;Running queue_job via the Supabase Dashboard.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Supabase's dashboard gives you the option to impersonate a user and run queries as them, which is immensely helpful when you have a lot of business logic and permissions encoded in SQL.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;I found the only public endpoints I needed were some functions to queue and dequeue tasks on behalf of the user, everything else was internal to the job system and rarely needs to be used directly.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;So I ended up with a system where a job is queued from the code like:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9"&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; error &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;await&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; supabase&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;rpc&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"queue_job"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_job_name&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"example-job"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_request_body&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"data"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenBoolean" style="white-space: pre-wrap;"&gt;true&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Or more frequently as part of a database trigger:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;queue_directory_agent_for_meeting_trigger&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TRIGGER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    existing_run &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Check &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; an agent run is already pending&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;running &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;this&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; meeting&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; existing_run&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;agent_run&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; request_body &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'meetingId'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NEW&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;meeting_id&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;text&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; agent_name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'directory-agent'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; existing_run &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; No pending&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;running run exists&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; queue the agent &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the meeting&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;queue_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'directory-agent'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;jsonb_build_object&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'meetingId'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NEW&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;meeting_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NEW&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$ &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TRIGGER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; queue_directory_agent_after_insert_update&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AFTER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INSERT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user_meetings&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EACH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ROW&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXECUTE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;queue_directory_agent_for_meeting_trigger&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;That then gets picked up by the job system, which then runs the task via Supabase Edge Functions.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;There is a lot of code on this page – feel free to copy and paste it into an LLM to ask questions and customize. &lt;/span&gt;&lt;a href="https://github.com/duggan/supabae-job-runner" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;You can find all the code in this Github repository&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;h2 id="assumptions" class="group relative"&gt;&lt;a href="#assumptions" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Assumptions&lt;/span&gt;&lt;/h2&gt;&lt;p dir="ltr"&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;This setup assumes you want to run a job in the permission context of each user.&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; There's nothing to stop you having global jobs, I just didn't need/want them so they're not accounted for in this set up.&lt;/span&gt;&lt;/p&gt;&lt;p dir="ltr"&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;Every Edge Function needs to accept and respond with headers it receives from the job runner&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; for tracking purposes (&lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;X-Job&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;X-Correlation-ID&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;). I originally tried doing this with internal request IDs for &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_net&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;, but they're just integers that are reset when the database is restarted, so not reliable.&lt;/span&gt;&lt;/p&gt;&lt;h2 id="some-initial-configuration" class="group relative"&gt;&lt;a href="#some-initial-configuration" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Some initial configuration&lt;/span&gt;&lt;/h2&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;I'm making use of a few extensions and a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;private&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; schema:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXTENSION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXISTS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; pg_cron &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SCHEMA&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXTENSION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXISTS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; pgjwt &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SCHEMA&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXTENSION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXISTS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; pg_net &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SCHEMA&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SCHEMA&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;And the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;supabase_url()&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; helper function is something I picked up from example projects. It's useful for getting the URL for your REST endpoints within the database (part of how the system will execute Edge Functions):&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;supabase_url&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  secret_value &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; decrypted_secret &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; secret_value &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; vault&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;decrypted_secrets &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'supabase_url'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; secret_value&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;You may have noticed this pulls a &lt;/span&gt;&lt;a href="https://supabase.com/docs/guides/database/vault" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Supabase Vault&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; secret. This is really just somewhere to keep a variable for the project URL. You can configure this locally with a statement in your &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;seed.sql&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; file like:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    secret_value &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Check &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the secret already exists&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; decrypted_secret &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; secret_value &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; vault&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;decrypted_secrets &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'supabase_url'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; If secret_value is &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;null&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; it means the secret does not exist&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; so create it&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; secret_value &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; vault&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;create_secret&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'http://api.supabase.internal:8000'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'supabase_url'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;But if you're deploying this to production, you'll want to copy your supabase URL from the &lt;/span&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;API&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; → &lt;/span&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;API Settings&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; page:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;figure class="image-wrapper"&gt;&lt;img src="/files/8433ca1804068610.png" alt="Screenshot 2025-01-15 at 16.39.34.png" width="inherit" height="inherit"&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Into Vault:&lt;/span&gt;&lt;/p&gt;&lt;p dir="ltr"&gt;&lt;figure class="image-wrapper"&gt;&lt;img src="/files/5a54d496b20ed906.png" alt="Screenshot 2025-01-15 at 16.46.01.png" width="inherit" height="inherit"&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Due to security changes in November 2024, we also need a function for retrieving the JWT secret:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;jwt_secret&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  secret_value &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; decrypted_secret &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; secret_value &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; vault&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;decrypted_secrets &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'app.jwt_secret'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; secret_value&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Again, you'll need to populate this secret value via the Vault interface. You can find the appropriate a few settings below the Project URL:&lt;/span&gt;&lt;/p&gt;&lt;p dir="ltr"&gt;&lt;figure class="image-wrapper"&gt;&lt;img src="/files/0ef7656c9ab84f02.png" alt="Screenshot 2025-01-21 at 16.35.16.png" width="inherit" height="inherit"&gt;&lt;/figure&gt;&lt;/p&gt;&lt;h2 id="tables" class="group relative"&gt;&lt;a href="#tables" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Tables&lt;/span&gt;&lt;/h2&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;I've defined three tables, &lt;/span&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;job&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;job_config&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt;, and &lt;/span&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;job_logs&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt;. Everything else is private functions for managing the state in those tables and in &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_net&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;, and defining &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_cron&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; jobs.&lt;/span&gt;&lt;/p&gt;&lt;h4 id="job" class="group relative"&gt;&lt;a href="#job" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;/h4&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This is the execution information for a specific job. Most of the column names are self-explanatory, but just to highlight that &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;request_body&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; is what will be send as a JSON payload to the defined Supabase Edge Function.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;An RLS policy allows the user CRUD operations on the table for their own jobs.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;Indexes are included which might be useful by the time your job system has been running for a while, but at the same time you might be just as well deleting older &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;completed&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; jobs.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PRIMARY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;KEY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uuid_generate_v4&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    user_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REFERENCES&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;users&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DELETE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CASCADE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    job_name &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CHECK&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TRIM&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;''&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    request_body &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JSONB&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    state &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;0&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    max_retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;3&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    created_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIME&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ZONE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOW&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    updated_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIME&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ZONE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOW&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    last_run_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIME&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ZONE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    next_run_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIME&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ZONE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    locked_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIME&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ZONE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CONSTRAINT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; chk_valid_state &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CHECK&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;state &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'completed'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'failed'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'canceled'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ALTER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ENABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ROW&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LEVEL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;POLICY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Users can manage their own job runs"&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ALL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; authenticated &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;USING&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uid&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user_id&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CHECK&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;select auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uid&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user_id&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INDEX&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; idx_job_user_jobname &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INDEX&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; idx_job_state &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;state&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INDEX&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; idx_job_locked_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;locked_at&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INDEX&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; idx_job_pending &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h4 id="job-config" class="group relative"&gt;&lt;a href="#job-config" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;job_config&lt;/span&gt;&lt;/h4&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This is used to control configuration for all jobs with a specific name, in this case enabling/disabling the jobs and controlling the concurrency. RLS is enabled, but no policy is added, meaning it is not directly accessible to users.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job_config&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    job_name &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PRIMARY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;KEY&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    concurrency_limit &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CHECK&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;concurrency_limit &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;0&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    enabled &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BOOLEAN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ALTER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_config &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ENABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ROW&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LEVEL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h4 id="job-logs" class="group relative"&gt;&lt;a href="#job-logs" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;job_logs&lt;/span&gt;&lt;/h4&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Basic job logging. This could be made more useful –&amp;nbsp;what it does at the moment is log when a job starts and finishes. RLS policy for user CRUD also included.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job_run_logs&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PRIMARY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;KEY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uuid_generate_v4&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    job_run_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REFERENCES&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job_run&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DELETE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ACTION&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    log_message &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    log_level &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'INFO'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    created_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIME&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ZONE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ALTER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_run_logs &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ENABLE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ROW&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LEVEL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;POLICY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Users can manage their own job run logs"&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_logs &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ALL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; authenticated &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;USING&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXISTS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_logs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_id&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;select auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uid&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WITH&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CHECK&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;EXISTS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_logs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_id&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;select auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uid&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h2 id="public-functions" class="group relative"&gt;&lt;a href="#public-functions" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Public functions&lt;/span&gt;&lt;/h2&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;As I mentioned, there's really only two functions I needed to use on a regular basis, one to queue up a new job, and another to remove a job from the queue. You could have functions to list jobs, etc, but I found just &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;SELECT&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; over the table was all I needed.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42"&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Public methods&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;queue_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _job_name &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _request_body &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JSONB&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _max_retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; search_path &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  new_job_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Insert a &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;new&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenClassName" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the current user&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INSERT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; request_body&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; max_retries&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VALUES&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;select auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;uid&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _request_body&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _max_retries&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNING&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; new_job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Log the creation &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;of&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;new_job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job run created'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Return the &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ID&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;of&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;new&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenClassName" style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; new_job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$ &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;dequeue_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _job_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; search_path &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Update the job to &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'canceled'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; it is still &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;in&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the pending state&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'canceled'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; updated_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _job_id&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Log the cancellation &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;of&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job run canceled'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$ &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h2 id="management-functions" class="group relative"&gt;&lt;a href="#management-functions" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Management functions&lt;/span&gt;&lt;/h2&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;All of these are created in a schema named &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;private&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;, which by default has no permissions attached to it, and is not presented via the REST API. That means (without specific configuration) they are executable only by special roles like the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;postgres&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; user, which is exactly what we need.&lt;/span&gt;&lt;/p&gt;&lt;h3 id="helper-generate-a-jwt-for-a-user" class="group relative"&gt;&lt;a href="#helper-generate-a-jwt-for-a-user" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Helper: generate a JWT for a user&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This is a helper function that lets your job management system create a valid JWT token that it can use to execute Edge Functions on behalf of a user.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;This is handy, and also one you have to be very careful about. You don't want to accidentally allow anyone to mint a JWT for any user via the API just by suppling the user's ID 😅&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;Being in the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;private&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; schema should prevent this, and the user_id is a UUID (so not amenable to iterating through like integers), but just keep in mind that you probably don't want a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;public&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; schema version of this function.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;generate_user_jwt&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;p_user_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; p_user_role &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; p_user_email &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; search_path &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  jwt_token &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  exp_time &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Calculate expiration &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;time&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; hour from now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;exp_time&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;+&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; interval &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'1 hour'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Generate the &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JWT&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;jwt_token&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;sign&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;payload&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;json_build_object&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'sub'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; p_user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'aud'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; p_user_role&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'role'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; p_user_role&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'email'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; p_user_email&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'iat'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;extract&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;epoch from &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'exp'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;extract&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;epoch from exp_time&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;json&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;secret&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;select &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;jwt_secret&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;algorithm&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'HS256'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'Bearer '&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;||&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; jwt_token&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="running-jobs" class="group relative"&gt;&lt;a href="#running-jobs" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Running jobs&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;These two functions handle job initialization. The http request lifecycle is managed via &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_net&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;, the rest of the work is in checking job state and concurrency management.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;run_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _job_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _job_name &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _request_body &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JSONB&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _user_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _role &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _email &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  timeout_milliseconds &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;5&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;*&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;60&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;*&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1000&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  auth_header &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Generate the &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JWT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the current user based on their role and email&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;auth_header&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;generate_user_jwt&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _role&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _email&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    net&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;http_post&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;url&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;supabase_url&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;||&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'/functions/v1/'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;||&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;jsonb_build_object&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'Content-Type'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'application/json'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'Authorization'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; auth_header&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'X-Correlation-ID'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_job_id&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'X-Job'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'true'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;body&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _request_body&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;timeout_milliseconds&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; timeout_milliseconds&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;run_jobs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; search_path &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    job_group &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RECORD&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    max_concurrency &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    running_jobs &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    available_slots &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    job_enabled &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BOOLEAN&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    current_job &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RECORD&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Fetch all user&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job pairs &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;with&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; pending jobs&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;locked_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;next_run_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;next_run_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;lt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;GROUP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOOP&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Skip &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job is disabled&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; enabled &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_enabled&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_config&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NOT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_enabled &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RAISE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOG&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job %s disabled via job_config'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CONTINUE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Get the global concurrency limit &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; concurrency_limit &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; max_concurrency&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_config&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Default to &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; not specified&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; max_concurrency &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;max_concurrency&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Count the number &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;of&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; currently running jobs &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;this&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job pair&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;COUNT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;*&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; running_jobs&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user_id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Calculate available slots &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;this&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job pair&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;available_slots&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; max_concurrency &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; running_jobs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; available_slots &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;0&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Select pending jobs up to the available slots&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; current_job &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;request_body&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; u&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; u&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;role&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; u&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;email&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JOIN&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;users u &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ON&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; u&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_group&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;locked_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;next_run_at &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;next_run_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;lt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ORDER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;created_at&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LIMIT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; available_slots&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SKIP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOCKED&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOOP&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Update the job to &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;set&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; locked_at&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; locked_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;last_run_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;updated_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Log the start &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;of&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the job run&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job run started'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Execute the job using pg_net&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;run_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                    current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                    current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_name&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                    current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;request_body&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                    current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                    current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;role&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                    current_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;email&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;                &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOOP&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOOP&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="process-jobs" class="group relative"&gt;&lt;a href="#process-jobs" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Process jobs&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This will check through &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_net&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;'s internal &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;_http_response&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; table for requests that have been annotated with the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;X-Job&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; header.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;From there, it matches up the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;X-Correlation-ID&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; header to the corresponding &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;job&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; table entry, and decides whether the job was successful based on the HTTP status code returned.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;process_jobs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; search_path &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; extensions&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  response &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RECORD&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  job_record &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RECORD&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  normalized_headers &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JSONB&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; response &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;*&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; net&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_http_response&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SKIP&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOCKED&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Lock the response to prevent concurrent processing&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOOP&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Normalize headers to lowercase&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;jsonb_object_agg&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;lower&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;key&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; value&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; normalized_headers&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;jsonb_each_text&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; normalized_headers &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;?&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'x-job'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Find and lock the corresponding job record&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; normalized_headers &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;?&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'x-correlation-id'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;*&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_record&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;normalized_headers&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'x-correlation-id'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FOUND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DELETE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; net&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_http_response &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status_code &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;200&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status_code &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;201&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status_code &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;204&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Success&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Update job to &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'completed'&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'completed'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; locked_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; updated_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job_record&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Log the successful completion&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_record&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job run completed successfully'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ELSE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Failure&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Handle retries or mark &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;as&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; failed&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;fail_job_with_retry&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_record&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;PERFORM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_record&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;            &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job run failed and will be retried'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ELSE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RAISE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOG&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'job run failed due to a missing X-Correlation-ID header. '&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;              &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'This is a permanent failure and will not be retried.'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DELETE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; net&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_http_response &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LOOP&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="logging" class="group relative"&gt;&lt;a href="#logging" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Logging&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This is relatively barebones, and just tracks job starting/finishing.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;public&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log_job&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _job_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _log_message &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _log_level &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;TEXT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'INFO'&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INSERT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;job_logs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; log_message&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; log_level&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VALUES&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;_job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _log_message&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _log_level&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$ &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="handling-failed-jobs" class="group relative"&gt;&lt;a href="#handling-failed-jobs" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Handling failed jobs&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Jobs fail, the system will handle failures with configurable retries, and reset jobs where the edge function never returned a status (due to resource limits, this can happen).&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49"&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; mark a job &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;as&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; failed&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; and queue &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; a retry &lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;fail_job_with_retry&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _job_id &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UUID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DECLARE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _max_retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INT&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Fetch the current retries and max_retries &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; the job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; retries&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; max_retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTO&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _retries&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _max_retries&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FROM&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _retries &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;+&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _max_retries &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;THEN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Mark job &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;as&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; permanently failed&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'failed'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; locked_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; updated_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;ELSE&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Increment retry count and schedule next retry&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; retries &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _retries &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;+&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;1&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;     &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Exponential backoff&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        next_run_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;+&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTERVAL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'5 minutes'&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;*&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;2&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;^&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _retries&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        locked_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        updated_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; id &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _job_id&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IF&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$ &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; When a job has expired&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; reset its status to pending&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;CREATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;OR&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPLACE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;FUNCTION&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;reset_stuck_jobs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  _timeout &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;INTERVAL&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFAULT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'15 minutes'&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;RETURNS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;VOID&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SECURITY&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;DEFINER&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AS&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;BEGIN&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;UPDATE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; job&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SET&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; locked_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;NULL&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'pending'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; updated_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WHERE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; locked_at &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;now&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; _timeout&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;AND&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; state &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'running'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;END&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;$$ &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;LANGUAGE&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; plpgsql&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="pg-cron" class="group relative"&gt;&lt;a href="#pg-cron" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;pg_cron&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;With &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;pg_cron&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; enabled, you can configure the management functions to execute on a schedule that suits your workloads.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;Below I've chosen to run the jobs very frequently because I wanted a relatively high throughput, but it can be tweaked to your preferences. One thing though: I would try to have &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;run-jobs&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;process-jobs&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; run out of sync with each other. You don't really want the job that picks up work running at the exact same time as the job that determines whether the work is completed, as it might introduce delays.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24"&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; cron&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;schedule&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'run-jobs'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'20 seconds'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  $$&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;run_jobs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; cron&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;schedule&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'process-jobs'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'6 seconds'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  $$ &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;process_jobs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; cron&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;schedule&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'reset-stuck-jobs'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'*/8 * * * *'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  $$&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;SELECT&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;private&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;reset_stuck_job_runs&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;'8 minutes'&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  $$&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="edge-functions" class="group relative"&gt;&lt;a href="#edge-functions" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Edge functions&lt;/span&gt;&lt;/h3&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;This is a barebones edge function that contains only the logic required to successfully process a job.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;The important thing is that it responds in a way that the management functions can process. In the Postgres functions above, this means accepting and responding with specific headers to identify the request as coming from the job runner, and returning HTTP status codes to indicate whether the job was successful or not.&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82"&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;import&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"@supabase/edge-runtime"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;import&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; createClient &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;from&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"@supabase/supabase-js"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;Deno&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;serve&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;async&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;req&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; data &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;await&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; req&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;json&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenComment" style="white-space: pre-wrap;"&gt;// X-Job and X-Correlation-ID are used as part of the Job Runner system.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; correlationId &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; req&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;get&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"X-Correlation-ID"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; isJob &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; req&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;get&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"X-Job"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;!&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;correlationId &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;||&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;!&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;isJob&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    console&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;error&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Missing required Job Runner headers"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;return&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;new&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenClassName" style="white-space: pre-wrap;"&gt;Response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JSON&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;stringify&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;error&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Missing required Job Runner headers"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;400&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"Content-Type"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"application/json"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  console&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"X-Correlation-ID: "&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; correlationId&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  console&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"X-Job"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; isJob&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; authHeader &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; req&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;get&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Authorization"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;!&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; supabase &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; createClient&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;Database&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;&amp;gt;&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    Deno&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;env&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;get&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"SUPABASE_URL"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;??&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;""&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    Deno&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;env&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;get&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"SUPABASE_ANON_KEY"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;??&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;""&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;global&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;Authorization&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; authHeader &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; token &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; authHeader&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;replace&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Bearer "&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;""&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;data&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; user &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;await&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; supabase&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;auth&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;getUser&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;token&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenComment" style="white-space: pre-wrap;"&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenComment" style="white-space: pre-wrap;"&gt;   * Only run if we have a user context&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenComment" style="white-space: pre-wrap;"&gt;   */&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;!&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;user&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;?.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;email&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    console&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;error&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"No valid user token received"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;return&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;new&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenClassName" style="white-space: pre-wrap;"&gt;Response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;JSON&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;stringify&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;error&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Unauthorized"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;401&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"Content-Type"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"application/json"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"X-Correlation-ID"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; correlationId&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;          &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"X-Job"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; isJob&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenComment" style="white-space: pre-wrap;"&gt;// This is about where the rest of your worker logic will go.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;const&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; success &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;!&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;!&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;data&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;success&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    console&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;log&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Successfully processed request"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;return&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;new&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenClassName" style="white-space: pre-wrap;"&gt;Response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;null&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;204&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"Content-Type"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"application/json"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"X-Correlation-ID"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; correlationId&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;        &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"X-Job"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; isJob&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  console&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;error&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Failed to process request"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;return&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;new&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenClassName" style="white-space: pre-wrap;"&gt;Response&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;null&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;status&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;500&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;headers&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"Content-Type"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"application/json"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"X-Correlation-ID"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; correlationId&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;"X-Job"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; isJob&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;,&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;  &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;h2 id="caveats" class="group relative"&gt;&lt;a href="#caveats" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Caveats&lt;/span&gt;&lt;/h2&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;For me, this has worked best for tasks that are mostly I/O-bound – API calls, reading from the database, saving to the database, transforming JSON. Supabase Edge Functions have &lt;/span&gt;&lt;a href="https://supabase.com/docs/guides/functions/limits" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;a few limits&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, but working around the &lt;/span&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;two second&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;CPU time&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt; one is particularly awkward. Forget running ML tasks, and decompressing a large-ish file won't work either.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;The good news is that it seems Supabase will be adding flexibility there in 2025:&lt;/span&gt;&lt;/p&gt;&lt;blockquote style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;We have a very exciting roadmap planned for 2025. One of the main priorities is to provide customizable compute limits (memory, CPU, and execution duration). We will soon announce an update on it.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;–&amp;nbsp;&lt;/span&gt;&lt;a href="https://supabase.com/blog/edge-functions-background-tasks-websockets" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Supabase blog&lt;/span&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;p dir="ltr"&gt;&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;In the mean time it is prudent to break jobs up into discrete units of work. For example, instead of a single job that does a given task for all users, create a "router" task that queues up the task for each user.&lt;/span&gt;&lt;/p&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;There's plenty of ways you can extend this –&amp;nbsp;I never added recurring jobs, for example, but I don't think it would be too tricky. You might also not like other decisions I made, like wiring the job names directly to the functions, but that is an intentional choice on my part to try and ensure the functions didn't end up too large.&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: start;" dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Most importantly, this is a system that worked for me. A a job system goes, it is, shall we say... &lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;no frills&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt;. But it might be interesting to some others.&lt;/span&gt;&lt;/p&gt;&lt;hr&gt;&lt;p dir="ltr"&gt;&lt;p dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;There is currently no comments system. If you'd like to share an opinion either with me or about this post, please feel free to do so with me either via email (&lt;/em&gt;&lt;/i&gt;&lt;a href="mailto:ross@duggan.ie" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;ross@duggan.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) on Mastodon (&lt;/em&gt;&lt;/i&gt;&lt;a href="http://mastodon.ie/@duggan" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;@duggan@mastodon.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) or even on &lt;/em&gt;&lt;/i&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;Hacker News&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;.&lt;/em&gt;&lt;/i&gt;&lt;br&gt;&lt;br&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;Corrections and suggestions also welcome, you might prefer to use the &lt;/em&gt;&lt;/i&gt;&lt;a href="https://github.com/duggan/supabae-job-runner" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;accompanying Github repository&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt; if so.&lt;/em&gt;&lt;/i&gt;&lt;/p&gt;&lt;/p&gt;</content>
    <link href="https://duggan.ie/posts/hacking-together-a-job-runner-using-supabase-edge-functions" rel="alternate"/>
    <published>2025-01-23T17:39:18.671Z</published>
    <summary type="html">&lt;p dir="ltr"&gt;&lt;span style="white-space: pre-wrap;"&gt;Supabase is a collection of backend services (Postgres, PostgREST auth service, S3 storage, function runtime) that you can develop against locally and then deploy to production.&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;They don't yet provide a fully integrated background job running system. This post shows a detailed example of how you might keep your stack slim and build a system like this for yourself in Supabase, rather than using a third party!&lt;/span&gt;&lt;/p&gt;</summary>
  </entry>
</feed>