192 lines
97 KiB
HTML
192 lines
97 KiB
HTML
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><meta name="theme-color" content="#111111"/><meta name="user-signed-in" content="false"/><title>The architecture of Neki — PlanetScale</title><meta name="description" content="Sharded Postgres, from the team behind Vitess"/><meta name="robots"/><meta property="og:url" content="https://planetscale.com/blog/the-architecture-of-neki"/><meta property="og:type" content="website"/><meta property="og:title" content="The architecture of Neki — PlanetScale"/><meta property="og:image" content="https://planetscale.com/assets/the-architecture-of-neki-social-CvHXrMho.png"/><meta property="og:description" content="Sharded Postgres, from the team behind Vitess"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:site" content="@PlanetScale"/><meta property="twitter:creator" content="@PlanetScale"/><meta property="twitter:url" content="https://planetscale.com/blog/the-architecture-of-neki"/><meta property="twitter:title" content="The architecture of Neki — PlanetScale"/><meta property="twitter:description" content="Sharded Postgres, from the team behind Vitess"/><meta property="twitter:image" content="https://planetscale.com/assets/the-architecture-of-neki-social-CvHXrMho.png"/><link rel="canonical" href="https://planetscale.com/blog/the-architecture-of-neki"/><link rel="preconnect" href="https://planetscale-images.imgix.net"/><link nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=" rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/><link nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=" rel="icon" href="/icon.png" type="image/png" sizes="32x32"/><link nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=" rel="apple-touch-icon" href="/apple-touch-icon.png" type="image/png" sizes="32x32"/><link nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=" rel="manifest" href="/manifest.webmanifest"/><link rel="modulepreload" href="/assets/entry.client-kDMVV6Oi.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/jsx-runtime-CX_JklLS.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/components-BCKQC8SP.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/index-D4hfWY9o.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/errorBoundaries-Cy4CQIhJ.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/root-CiIk8sGI.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/lib-QSj_asTz.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/analytics.client-DNdghg7R.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/SiteHeader-P2mSEufD.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/current-EBMVq01n.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/clsx-eT0YPcGk.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/bugs-B0F8iVAJ.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/keyboard-CthKfdGk.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/use-tab-direction-B6ScOFcW.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/blog-pAhBtijj.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/blog._slug-BZ9bXQPU.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/ContentImage-1Eo_082L.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/BlogCategoryLink-CBscx2Ka.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/Details-Cn3nNItz.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/Skittle-zj2xJWaI.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/SiteFooter-Dluz6Abx.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/Vimeo-CHApOCjU.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/YouTube-CubVn4yq.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/date-Dqn8aLix.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/use-inert-others-Dz0UAR7O.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/description-CXyhf9fU.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/use-is-mounted-C-XJl2Wd.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="modulepreload" href="/assets/types-WVQySR0i.js" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4="/><link rel="stylesheet" href="/assets/styles-ql2gshcx.css"/><script nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=">window.ENV = {"IMAGE_CDN":"https://planetscale-images.imgix.net","IMAGE_CDN_ENABLED":"true","INTERNAL_API":"https://api.planetscale.com","RELEASE":"37f93714-95f0-4b50-97b0-f5723adc3e5c","SENTRY_DSN":"https://bd81903b44804e22a06bdc0c1a91b303@o499952.ingest.us.sentry.io/4504531942572032"}</script></head><body class="flex min-h-screen flex-col"><div class="bg-neki px-3 py-1 text-center font-medium text-gray-900 dark:font-semibold"><span>Neki, sharded Postgres, is now available.</span> <span class="whitespace-nowrap"><a href="https://auth.planetscale.com/sign-up" class="whitespace-nowrap bg-gray-900 px-sm font-semibold text-white">Get started</a></span></div><header class="relative mb-6 mt-4 bg-primary"><div class="flex flex-col gap-y-3 px-3 sm:px-5 container max-w-7xl"><div class="grid w-full grid-cols-[auto_1fr] grid-rows-1 items-center lg:items-start lg:gap-3"><a aria-label="Go to homepage" class="col-start-1 col-end-2 h-4 w-4 rounded-full text-primary lg:hidden" href="/" data-discover="true"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 40 40"><path fill="currentColor" d="M0 20C0 8.954 8.954 0 20 0c8.121 0 15.112 4.84 18.245 11.794l-26.45 26.45a20 20 0 0 1-3.225-1.83L24.984 20H20L5.858 34.142A19.94 19.94 0 0 1 0 20M39.999 20.007 20.006 40c11.04-.004 19.99-8.953 19.993-19.993"></path></svg></a><div class="group col-start-2 col-end-3 row-start-1 flex shrink-0 items-center justify-end gap-1.5 lg:gap-3"><div class="flex flex-row gap-2 lg:flex-col lg:gap-1 xl:flex-row"><div class="flex items-center justify-end gap-1 lg:h-4"><a href="https://auth.planetscale.com/sign-in" class="font-semibold text-primary hover:text-orange">Sign in</a></div><div class="flex items-center justify-end gap-0.5 lg:h-4"><form class="btn-sm hidden sm:inline-flex" action="/api/demo-sessions" method="post"><button type="submit" class="btn btn-outline btn-sm hidden sm:inline-flex">View sandbox</button></form><a class="btn btn-sm" href="/contact" data-discover="true">Get in touch</a></div></div></div><div class="col-start-1 col-end-2 flex items-center gap-x-3 lg:row-start-1 lg:h-4"><a aria-label="Go to homepage" class="col-start-1 col-end-2 hidden h-4 w-4 rounded-full text-primary lg:block" href="/" data-discover="true"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 40 40"><path fill="currentColor" d="M0 20C0 8.954 8.954 0 20 0c8.121 0 15.112 4.84 18.245 11.794l-26.45 26.45a20 20 0 0 1-3.225-1.83L24.984 20H20L5.858 34.142A19.94 19.94 0 0 1 0 20M39.999 20.007 20.006 40c11.04-.004 19.99-8.953 19.993-19.993"></path></svg></a><nav aria-label="Main" data-orientation="horizontal" class="hidden items-center lg:flex"><ul class="flex flex-wrap gap-x-1 md:flex-nowrap"><li><div data-headlessui-state=""><button class="whitespace-nowrap font-semibold text-primary hover:text-contrast focus-visible:ring-0 ui-open:text-orange" type="button" aria-expanded="false" data-headlessui-state="">Platform<span class="ml-sm inline-block ui-open:rotate-180">▾</span></button></div><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></li><li class="text-decoration" role="presentation">|</li><li><div data-headlessui-state=""><button class="whitespace-nowrap font-semibold text-primary hover:text-contrast focus-visible:ring-0 ui-open:text-orange" type="button" aria-expanded="false" data-headlessui-state="">Resources<span class="ml-sm inline-block ui-open:rotate-180">▾</span></button></div><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/blog" data-discover="true">Blog</a></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/docs">Documentation</a></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/pricing" data-discover="true">Pricing</a></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/migrate" data-discover="true">Migrate</a></li></ul></nav></div></div><details class="lg:hidden"><summary>Navigation</summary><nav class="dashed-box mt-1 p-3"><ul class="flex flex-wrap gap-x-1 md:flex-nowrap"><li><div data-headlessui-state=""><button class="whitespace-nowrap font-semibold text-primary hover:text-contrast focus-visible:ring-0 ui-open:text-orange" type="button" aria-expanded="false" data-headlessui-state="">Platform<span class="ml-sm inline-block ui-open:rotate-180">▾</span></button></div><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></li><li class="text-decoration" role="presentation">|</li><li><div data-headlessui-state=""><button class="whitespace-nowrap font-semibold text-primary hover:text-contrast focus-visible:ring-0 ui-open:text-orange" type="button" aria-expanded="false" data-headlessui-state="">Resources<span class="ml-sm inline-block ui-open:rotate-180">▾</span></button></div><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/blog" data-discover="true">Blog</a></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/docs">Documentation</a></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/pricing" data-discover="true">Pricing</a></li><li class="text-decoration" role="presentation">|</li><li><a class="font-semibold text-primary hover:text-contrast" href="/migrate" data-discover="true">Migrate</a></li></ul></nav></details></div></header><main class="container mb-6 flex max-w-7xl flex-1 flex-col px-3 sm:px-5 lg:px-12"><section class=""><p class="block"><a class="pr-sm text-primary hover:text-contrast" href="/blog" data-discover="true">Blog</a><span class="px-sm text-decoration">|</span><a class="px-sm text-blue hover:bg-blue-100 dark:hover:bg-blue-900" href="/blog/category/engineering" data-discover="true">Engineering</a><span class="px-sm text-decoration">|</span><a class="px-sm text-yellow-600 hover:bg-neki/10 dark:text-neki dark:hover:bg-neki/10" href="/blog/category/neki" data-discover="true">Neki</a></p><div class="flex lg:flex-row-reverse lg:gap-x-6"><div class="lg:sticky lg:top-2 lg:self-start"><button class="absolute right-0 bg-gray-100 px-sm md:block lg:hidden dark:bg-gray-800 -mt-9 hidden"><span class="inline">Table of contents «</span><span class="hidden">Close »</span></button><aside class="tree-nav w-full shrink-0 space-y-3 lg:w-36 hidden lg:block"><div><h4 class="text-secondary">Table of contents</h4><ul><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#real-postgres" data-discover="true">Real Postgres</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#postgresmanager" data-discover="true">PostgresManager</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#sidecar" data-discover="true">Sidecar</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#shards" data-discover="true">Shards</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#admin" data-discover="true">Admin</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#operator" data-discover="true">Operator</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#router" data-discover="true">Router</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#data-topology" data-discover="true">Data Topology</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#replicator" data-discover="true">Replicator</a></li><li><a class="font-semibold text-primary hover:text-blue" href="/blog/the-architecture-of-neki#get-started" data-discover="true">Get started</a></li></ul><div class="mb-3 mt-6 border bg-blue-50 p-3 font-semibold text-contrast dark:bg-blue-900"><p>PlanetScale, the fastest cloud Postgres, from $5/month.</p><p><a href="https://app.planetscale.com/new">Start now</a></p></div><p>Get the <a href="/blog/feed.atom">RSS feed</a></p></div></aside></div><article class="min-w-0 flex-grow"><h1>The architecture of Neki</h1><p class="text-secondary"><a class="text-contrast no-underline" href="/blog/author/harshit" data-discover="true">Harshit Gangal</a> |<!-- --> <time dateTime="2026-09-18">September 18, 2026</time></p><div class="blog-post-body"><p>Meet Neki: sharding for Postgres.<!-- --> <!-- -->Neki allows applications to connect to massive, sharded databases over a single connection string.<!-- --> <!-- -->This post takes apart the architecture from the bottom up, one piece at a time, starting with what's underneath all of it.</p><h2 id="real-postgres"><a href="#real-postgres">Real Postgres</a></h2><p>Neki is built as a sharding and scaling solution for real Postgres.<!-- --> <!-- -->It's not a fork, nor a wire-compatible reimplementation, nor a MySQL sharding idea wearing a Postgres label.<!-- --> <!-- -->Neki uses ordinary PostgreSQL instances that store rows in Postgres data pages using MVCC, carry out transactions, and work as you would expect with <code>psql</code> and other Postgres drivers.<!-- --> <!-- -->Neki builds around those instances to let you shard them, scale them, and manage them as one database.</p><p>Let's take a look:</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: The full Neki cluster: an application talking to the Router, two shards each with a primary and a replica running Sidecar, Postgres, and Replicator, PostgresManager controlling each Postgres instance, Admin and etcd forming the control plane, and an Operator provisioning every pod." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-cluster-architecture-BdagkgJK.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-cluster-architecture-darkmode-YGx-3wht.svg?auto=compress%2Cformat"/><img alt="The full Neki cluster: an application talking to the Router, two shards each with a primary and a replica running Sidecar, Postgres, and Replicator, PostgresManager controlling each Postgres instance, Admin and etcd forming the control plane, and an Operator provisioning every pod." src="https://planetscale-images.imgix.net/assets/neki-cluster-architecture-BdagkgJK.svg?auto=compress%2Cformat" width="1504" height="1058" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><h2 id="postgresmanager"><a href="#postgresmanager">PostgresManager</a></h2><p>Using vanilla Postgres means Neki needs a way to run and manage each instance.<!-- --> <!-- -->That includes starting and stopping Postgres, owning its data directory, and configuring replication so a new instance can join a shard.<!-- --> <strong>PostgresManager</strong> handles this coordination, running as the first process in the Postgres container and managing the <code>postgres</code> process directly.</p><h2 id="sidecar"><a href="#sidecar">Sidecar</a></h2><p>Postgres uses a separate backend process for each connection and limits how many can be open at once.<!-- --> <!-- -->Neki’s <strong>Sidecar</strong> sits in front of each instance and pools connections, letting many client connections share fewer Postgres backends.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: A close-up of one Neki shard: the Router sends queries to a primary and replica, each with its own Sidecar highlighted in orange alongside Postgres, PostgresManager, and Replicator." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-sidecar-highlight-B8X81j7N.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-sidecar-highlight-darkmode-CNtJVItJ.svg?auto=compress%2Cformat"/><img alt="A close-up of one Neki shard: the Router sends queries to a primary and replica, each with its own Sidecar highlighted in orange alongside Postgres, PostgresManager, and Replicator." src="https://planetscale-images.imgix.net/assets/neki-sidecar-highlight-B8X81j7N.svg?auto=compress%2Cformat" width="704" height="555" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><p>The Router, which is the component that accepts external client connections, communicates with the Postgres nodes via these Sidecars.</p><p>It also reports each Postgres instance's health and whether it is a primary or replica, so the rest of the cluster knows whether it can receive write queries.</p><p>The pool doesn't treat every connection the same way.<!-- --> <!-- -->The length of time a connection is checked out for use varies depending on what it's being used for.<!-- --> <!-- -->A multi-statement transaction holds on to its connection until <code>commit</code> or <code>rollback</code>.<!-- --> <!-- -->A session-scoped advisory lock needs a connection of its own, because the lock has to outlive whatever transaction is open at the time and can't share that connection.<!-- --> <!-- -->Everything else checks a connection out and hands it back the moment the statement finishes.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: Three connection-pool lifetimes in the Sidecar: a shared connection for an autocommit statement, a dedicated connection for an open transaction, and a reserved connection for a session advisory lock." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-sidecar-pooling-tiers-DqP_9-5W.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-sidecar-pooling-tiers-darkmode-CsEWlGUM.svg?auto=compress%2Cformat"/><img alt="Three connection-pool lifetimes in the Sidecar: a shared connection for an autocommit statement, a dedicated connection for an open transaction, and a reserved connection for a session advisory lock." src="https://planetscale-images.imgix.net/assets/neki-sidecar-pooling-tiers-DqP_9-5W.svg?auto=compress%2Cformat" width="1504" height="454" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><p>The Sidecar knows which of the three to use because the Router sends the necessary information with the query: autocommit, an open transaction, or a session that has to stay on one backend.</p><h2 id="shards"><a href="#shards">Shards</a></h2><p>Each Postgres instance gets its own Sidecar and PostgresManager pair.<!-- --> <!-- -->Real deployments need more than one instance: a primary and its replicas.<!-- --> <!-- -->Neki calls that group a <strong>shard</strong>, the unit it splits data across.<!-- --> <!-- -->It's always advised to run a shard with a primary and 2+ replicas for high availability, as well as for additional read query capacity.</p><p>A shard is considered one Postgres cluster.<!-- --> <!-- -->Its replicas are physical copies of the primary, so they share a catalog and the same object identifiers.</p><p>Object Identifiers (OIDs) are how Postgres tracks objects internally, rather than by name.<!-- --> <!-- -->A client reads a column’s type OID off the wire to interpret its bytes and may cache that OID for later re-use.<!-- --> <!-- -->A custom type therefore needs to carry the same OID no matter which shard answers the query.<!-- --> <!-- -->Independent shards can assign that type different OIDs, so Neki designates one shard in the entire Neki cluster as the <strong>authoritative shard</strong>.<!-- --> <!-- -->This shard is the source of truth for translating custom type OIDs in responses from other shards to match.<!-- --> <!-- -->It ensures OIDs are consistent across the many shards of the Neki cluster.</p><p>The authoritative shard's Sidecar also watches for schema changes and reports them to the Routers.<!-- --> <!-- -->This keeps the Routers' view of the schema current when a table is renamed or a column is dropped.</p><h2 id="admin"><a href="#admin">Admin</a></h2><p>In a distributed system, instances can fail independently while the rest of the system lives on.<!-- --> <!-- -->Neki is no different.<!-- --> <!-- -->A primary or replica can go down at any moment while its fellow instances on the shard are healthy.<!-- --> <!-- -->The <strong>Admin</strong>'s job is to detect failures, promote a replica, and maintain each shard’s durability policy.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: The same cluster diagram with Admin and the control plane highlighted and everything else faded." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-admin-highlight-Bz1IxBf6.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-admin-highlight-darkmode-Uru2fPFH.svg?auto=compress%2Cformat"/><img alt="The same cluster diagram with Admin and the control plane highlighted and everything else faded." src="https://planetscale-images.imgix.net/assets/neki-admin-highlight-Bz1IxBf6.svg?auto=compress%2Cformat" width="1504" height="1058" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><p>It health-checks every Sidecar, tracks replication lag for each replica, and decides when a shard needs a new primary.<!-- --> <!-- -->When a primary goes down, it coordinates an emergency failover, promoting a replica to take its place.<!-- --> <!-- -->It can also coordinate a planned switchover, which are needed for intentional node resizes and version upgrades.<!-- --> <!-- -->In both situations, Admin uses <code>pg_rewind</code> to bring diverged instances onto the new primary’s timeline, copying only the data that changed since the timelines diverged.</p><p>Each shard has a durability policy that determines when a commit is acknowledged:</p><ul><li><strong>Async:</strong> The primary acknowledges the commit without waiting for a replica.</li><li><strong>Sync:</strong> The primary waits for a replica to confirm the commit, protecting against the loss of a single node.</li><li><strong>Cross-zone sync:</strong> The primary waits for confirmation from a replica in another availability zone, protecting against the loss of the primary’s zone.</li></ul><p>Postgres enforces whichever one is configured, using its own synchronous replication machinery.<!-- --> <!-- -->The Admin keeps that configuration correct as replicas join or leave shards, or a failover moves the primary to a different zone.</p><p>Much of Admin’s work, however, doesn’t involve changing the primary.<!-- --> <!-- -->It repoints replicas to the correct replication source and corrects roles when Postgres and the topology disagree.</p><h2 id="operator"><a href="#operator">Operator</a></h2><p>Neki’s components need to be deployed, updated, and replaced when their machines fail.<!-- --> <!-- -->Neki is built Kubernetes-first, and the <strong>Operator</strong> manages this full lifecycle.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: The same cluster diagram with the Operator highlighted and everything else faded." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-operator-highlight-BD94jQsB.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-operator-highlight-darkmode-8OihOEsZ.svg?auto=compress%2Cformat"/><img alt="The same cluster diagram with the Operator highlighted and everything else faded." src="https://planetscale-images.imgix.net/assets/neki-operator-highlight-BD94jQsB.svg?auto=compress%2Cformat" width="1504" height="1058" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><p>The Operator models a cluster as a hierarchy.<!-- --> <!-- -->A cluster owns routers and shards, and each shard owns the pods running its Postgres instances and Sidecars.<!-- --> <!-- -->When the Neki cluster configuration changes, the Operator works out which pods need to be created, updated, or removed.</p><p>How it replaces an instance depends on whether that instance is still running.<!-- --> <!-- -->For a live instance, the Operator builds a replacement and confirms it has caught up before deleting the old one.<!-- --> <!-- -->If a node fails and loses its ephemeral storage, the Operator rebuilds the lost instance from scratch once its safety checks pass.</p><p>Admin and the Router handle the database side of those disruptions.<!-- --> <!-- -->Admin coordinates a switchover for planned primary replacements or a failover when a primary goes down.<!-- --> <!-- -->The Router can buffer queries that are safe to retry while a healthy primary becomes available.</p><h2 id="router"><a href="#router">Router</a></h2><p>We've talked a lot about how the Neki cluster operates and handles failure internally.<!-- --> <!-- -->What we've yet to dive into is how applications use the thing!</p><p>The <strong>Router</strong> is the entry point for clients connecting to a Neki cluster, presenting a single Postgres wire-protocol endpoint to connect to a (potentially) massive sharded database.<!-- --> <!-- -->Applications use Postgres drivers to send SQL and open transactions without managing connections to individual shards.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: The same cluster diagram with the Router and etcd highlighted and everything else faded." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-router-highlight-D4fCw8OU.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-router-highlight-darkmode-DzHaPeoU.svg?auto=compress%2Cformat"/><img alt="The same cluster diagram with the Router and etcd highlighted and everything else faded." src="https://planetscale-images.imgix.net/assets/neki-router-highlight-D4fCw8OU.svg?auto=compress%2Cformat" width="1504" height="1058" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><p>Authentication and role checks are done as if it were the Postgres instance itself, and the protocol's own extended-query flow and prepared-statement lifecycle are all built into the Router.</p><p>Once a query arrives, the Router runs a Postgres-compatible parser against the authoritative shard's catalog, plans it against the current sharding layout, and sends it to whichever Sidecar needs to run it over gRPC.</p><p>Not every query can run on a single shard.<!-- --> <!-- -->A join may need data from several shards or an aggregate may need to read from all of them.<!-- --> <!-- -->The <strong>Router</strong> coordinates that work as a <a href="/blog/what-is-a-neki-router">distributed query</a>.</p><p>Whenever possible, it leaves the work to the Postgres instances.<!-- --> <!-- -->If both sides of a join are on the same shard, the Router sends the join to that shard.<!-- --> <!-- -->When a join needs to run across shards, the Router executes it itself, choosing between nested-loop, hash, and merge joins based on cost estimations.</p><div class="mb-3 border p-3 border-blue-600 dark:border-blue-500"><p><span class="bg-blue-600 px-sm text-white dark:bg-blue-500 dark:text-black">Note</span></p><p>Read more about Routers, parsing, and sharded query planning in our other blog, <a href="/blog/the-lifecycle-of-a-sharded-postgres-query">The lifecycle of a sharded Postgres query</a>.</p></div><p>Earlier, we covered how Admin promotes a new primary during a switchover or failover.<!-- --> <!-- -->If that happens, the Router can buffer queries, giving the Admin time to complete the handover.<!-- --> <!-- -->For queries that can safely be retried after failing against a primary, the Router buffers the query and waits, for a fixed time, for a healthy primary.<!-- --> <!-- -->Once a healthy primary is available, the Router releases queued queries gradually.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: A query hits an unavailable primary and gets buffered. Usually the primary recovers within the time and size limit and the queued query is released gradually, so the client sees nothing. Rarely, the limit is reached first and an error is returned to the client." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-router-buffering-DChcV18g.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-router-buffering-darkmode-HQltRPC0.svg?auto=compress%2Cformat"/><img alt="A query hits an unavailable primary and gets buffered. Usually the primary recovers within the time and size limit and the queued query is released gradually, so the client sees nothing. Rarely, the limit is reached first and an error is returned to the client." src="https://planetscale-images.imgix.net/assets/neki-router-buffering-DChcV18g.svg?auto=compress%2Cformat" width="1504" height="500" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><h2 id="data-topology"><a href="#data-topology">Data Topology</a></h2><p>Router, Sidecars, and Admin all need a consistent picture of which shards exist, what key ranges they own, and which tables are sharded at all.<!-- --> <!-- -->If the Router's copy is wrong, a query can land on the wrong shard.<!-- --> <!-- -->This is all specified with a <a href="/blog/what-is-a-data-topology">Data Topology</a>, and <strong>etcd</strong> holds the single, authoritative copy of it.<!-- --> <!-- -->When the Data Topology changes, the Router, Sidecars, and Admin pick up the updated configuration without a restart or manual synchronization.</p><p>The Data Topology defines <strong>shard groups</strong>, named sets of physical shards, each owning a range of routing keys.<!-- --> <!-- -->Each table belongs to a shard group.<!-- --> <strong>Shard indexes</strong> specify the columns or expressions and the strategy used to turn row values into routing keys.<!-- --> <!-- -->Those keys determine which shard receives each row.</p><h2 id="replicator"><a href="#replicator">Replicator</a></h2><p>As a database grows, its layout may need to change.<!-- --> <!-- -->Tables need to be imported, shards need to be split, and schemas need to change all while applications keep using the database.</p><p>Neki's <strong>Replicator</strong> handles the data movement behind all such operations.<!-- --> <!-- -->It runs as a separate process colocated with a shard's Sidecar and Postgres.<!-- --> <!-- -->It is responsible for copying existing rows to new destinations, and also keeping the data current by decoding changes from a Postgres logical replication stream and applying them as SQL.</p><p><button type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Enlarge image: A close-up of one Neki shard, with the separate Replicator beside each Postgres instance and its connection from Postgres highlighted in orange." class="focus-visible-ring group relative block w-fit max-w-[min(100%,800px)] cursor-zoom-in text-left [&_picture]:contents"><picture class="block"><source media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" srcSet="https://planetscale-images.imgix.net/assets/neki-replicator-highlight-flGISLaG.svg?auto=compress%2Cformat"/><source media="(prefers-color-scheme: dark)" srcSet="https://planetscale-images.imgix.net/assets/neki-replicator-highlight-darkmode-D-xFVR2s.svg?auto=compress%2Cformat"/><img alt="A close-up of one Neki shard, with the separate Replicator beside each Postgres instance and its connection from Postgres highlighted in orange." src="https://planetscale-images.imgix.net/assets/neki-replicator-highlight-flGISLaG.svg?auto=compress%2Cformat" width="704" height="385" loading="lazy" class="w-auto max-w-full"/></picture><span aria-hidden="true" class="pointer-events-none absolute right-1 top-1 z-10 flex h-5 w-5 items-center justify-center border border-white/25 bg-black/70 text-white backdrop-blur-sm transition-colors transition-opacity group-hover:bg-black/90 group-hover:opacity-100 group-focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2h4v4M6 14H2v-4M14 2l-4.5 4.5M2 14l4.5-4.5"></path></svg></span></button><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span></p><p>Three workflows use the Replicator:</p><ul><li><strong>MoveTables</strong> relocates a set of tables, including imports from an external Postgres instance</li><li><strong>Reshard</strong> redistributes data across shard key ranges, allowing a shard to be split when it outgrows its capacity</li><li><strong>OnlineDDL</strong> changes a table's schema by building a shadow table alongside the original and keeping it current through the same change-data-capture pipeline MoveTables and Reshard use to relocate rows.<!-- --> <!-- -->A final rename swaps the new table into place.<!-- --> <!-- -->This supports changes such as repartitioning a table, alongside changes that would otherwise require a blocking operation.</li></ul><p>Once the data has been copied and the destination is caught up, the workflow switches from the original tables or shards to their replacements.<!-- --> <!-- -->This is the cutover.<!-- --> <!-- -->The Router uses the same buffering mechanism that handles primary changes for this step.<!-- --> <!-- -->It buffers queries during that switch and releases them afterward.</p><p>Together, these components let Neki scale Postgres horizontally while presenting a single database to applications.</p><h2 id="get-started"><a href="#get-started">Get started</a></h2><p>Neki is in Platform Preview right now.</p><p>Start a <a href="https://app.planetscale.com/new">Neki</a> cluster today: build on it from scratch, or import an existing Postgres database.</p></div></article></div></section></main><footer class="mb-6 mt-10 px-3 sm:px-5 container max-w-7xl"><nav class="grid grid-cols-1 text-left sm:grid-cols-2 lg:grid-cols-5 lg:mx-7"><div class="dashed-box dashed-box-x-t sm:dashed-box-l-t lg:dashed-box-y-l p-3"><h2 class="font-semibold">Company</h2><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/about" data-discover="true">About</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/brand" data-discover="true">Brand</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/blog" data-discover="true">Blog</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/changelog" data-discover="true">Changelog</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/careers" data-discover="true">Careers</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/events" data-discover="true">Events</a></div><div class="dashed-box dashed-box-x-t lg:dashed-box-y-l p-3"><h2 class="font-semibold">Product</h2><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/case-studies" data-discover="true">Case studies</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/enterprise" data-discover="true">Enterprise</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/pricing" data-discover="true">Pricing</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/benchmarks" data-discover="true">Benchmarks</a></div><div class="dashed-box dashed-box-x-t sm:dashed-box-l-t lg:dashed-box-y-l p-3"><h2 class="font-semibold">Resources</h2><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/docs">Documentation</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/migrate" data-discover="true">Migrate</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="https://support.planetscale.com/hc/en-us" rel="nofollow noopener noreferrer" target="_blank">Support</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="https://planetscalestatus.com" rel="nofollow noopener noreferrer" target="_blank">Status</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="https://trust.planetscale.com" rel="nofollow noopener noreferrer" target="_blank">Trust Center</a></div><div class="dashed-box dashed-box-x-t lg:dashed-box-y-l p-3"><h2 class="font-semibold">Courses</h2><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/learn/courses/mysql-for-developers" data-discover="true">MySQL for Developers</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/learn/courses/database-scaling" data-discover="true">Database Scaling</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/learn/courses/vitess" data-discover="true">Learn Vitess</a></div><div class="dashed-box p-3 sm:col-span-2 lg:col-span-1"><h2 class="font-semibold text-primary hover:text-contrast">Open source</h2><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="/vitess" data-discover="true">Vitess</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="https://vitess.io/slack" rel="nofollow noopener noreferrer" target="_blank">Vitess community</a><a class="block pl-1ch -indent-1ch text-primary hover:text-contrast" href="https://github.com/planetscale" rel="me nofollow noopener noreferrer" target="_blank">GitHub</a></div></nav><div class="dashed-box dashed-box-x-b p-3 lg:mx-7"><p class="mb-3 md:mb-0"><a class="text-primary" rel="nofollow" href="/legal/privacy" data-discover="true">Privacy</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a class="text-primary" rel="nofollow" href="/legal/siteterms" data-discover="true">Terms</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a class="text-primary" rel="nofollow" href="/legal/cookies" data-discover="true">Cookies</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a class="text-primary" rel="nofollow" href="/legal/patents" data-discover="true">Patents</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a class="text-primary" rel="nofollow" href="/legal/privacy#privacy-rights-and-choices" data-discover="true">Do Not Share My Personal Information</a></p><p class="text-secondary">© <!-- -->2026<!-- --> PlanetScale, Inc. All rights reserved.</p></div><p class="mb-0 mt-3 break-normal lg:mx-7"><a class="text-primary" href="https://github.com/planetscale" rel="me nofollow noopener noreferrer" target="_blank">GitHub</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a aria-label="X (formerly Twitter)" class="text-primary" href="https://twitter.com/planetscale" rel="me nofollow noopener noreferrer" target="_blank">X</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a aria-label="LinkedIn" class="text-primary" href="https://www.linkedin.com/company/planetscale" target="_blank" rel="noreferrer">LinkedIn</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a class="text-primary" href="https://www.youtube.com/planetscale" rel="me nofollow noopener noreferrer" target="_blank">YouTube</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a aria-label="Discord" class="text-primary" href="https://pscale.link/community" rel="nofollow noopener noreferrer" target="_blank">Discord</a><span class="text-decoration" role="presentation"> <!-- -->|<!-- --> </span><a class="text-primary" href="https://www.facebook.com/planetscaledata" rel="me nofollow noopener noreferrer" target="_blank">Facebook</a></p></footer><script nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=">((storageKey2, restoreKey) => {
|
||
if (!window.history.state || !window.history.state.key) {
|
||
let key2 = Math.random().toString(32).slice(2);
|
||
window.history.replaceState({ key: key2 }, "");
|
||
}
|
||
try {
|
||
let storedY = JSON.parse(sessionStorage.getItem(storageKey2) || "{}")[restoreKey || window.history.state.key];
|
||
if (typeof storedY === "number") window.scrollTo(0, storedY);
|
||
} catch (error2) {
|
||
console.error(error2);
|
||
sessionStorage.removeItem(storageKey2);
|
||
}
|
||
})("react-router-scroll-positions", null)</script><script nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=">window.__reactRouterContext = {"basename":"/","future":{"unstable_enableNodeReadableStream":false,"unstable_optimizeDeps":true},"routeDiscovery":{"mode":"lazy","manifestPath":"/__manifest"},"ssr":true,"isSpaMode":false};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=" type="module" async="">;
|
||
import * as route0 from "/assets/root-CiIk8sGI.js";
|
||
import * as route1 from "/assets/blog-pAhBtijj.js";
|
||
import * as route2 from "/assets/blog._slug-BZ9bXQPU.js";
|
||
window.__reactRouterManifest = {
|
||
"entry": {
|
||
"module": "/assets/entry.client-kDMVV6Oi.js",
|
||
"imports": [
|
||
"/assets/jsx-runtime-CX_JklLS.js",
|
||
"/assets/components-BCKQC8SP.js",
|
||
"/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js",
|
||
"/assets/index-D4hfWY9o.js",
|
||
"/assets/errorBoundaries-Cy4CQIhJ.js"
|
||
],
|
||
"css": []
|
||
},
|
||
"routes": {
|
||
"root": {
|
||
"id": "root",
|
||
"path": "",
|
||
"hasAction": false,
|
||
"hasLoader": true,
|
||
"hasClientAction": false,
|
||
"hasClientLoader": false,
|
||
"hasClientMiddleware": false,
|
||
"hasDefaultExport": true,
|
||
"hasErrorBoundary": true,
|
||
"module": "/assets/root-CiIk8sGI.js",
|
||
"imports": [
|
||
"/assets/jsx-runtime-CX_JklLS.js",
|
||
"/assets/components-BCKQC8SP.js",
|
||
"/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js",
|
||
"/assets/index-D4hfWY9o.js",
|
||
"/assets/errorBoundaries-Cy4CQIhJ.js",
|
||
"/assets/lib-QSj_asTz.js",
|
||
"/assets/analytics.client-DNdghg7R.js",
|
||
"/assets/SiteHeader-P2mSEufD.js",
|
||
"/assets/current-EBMVq01n.js",
|
||
"/assets/clsx-eT0YPcGk.js",
|
||
"/assets/bugs-B0F8iVAJ.js",
|
||
"/assets/keyboard-CthKfdGk.js",
|
||
"/assets/use-tab-direction-B6ScOFcW.js"
|
||
],
|
||
"css": []
|
||
},
|
||
"routes/blog": {
|
||
"id": "routes/blog",
|
||
"parentId": "root",
|
||
"path": "blog",
|
||
"hasAction": false,
|
||
"hasLoader": false,
|
||
"hasClientAction": false,
|
||
"hasClientLoader": false,
|
||
"hasClientMiddleware": false,
|
||
"hasDefaultExport": false,
|
||
"hasErrorBoundary": false,
|
||
"module": "/assets/blog-pAhBtijj.js",
|
||
"imports": [
|
||
"/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js"
|
||
],
|
||
"css": []
|
||
},
|
||
"routes/blog.$slug": {
|
||
"id": "routes/blog.$slug",
|
||
"parentId": "routes/blog",
|
||
"path": ":slug",
|
||
"hasAction": false,
|
||
"hasLoader": true,
|
||
"hasClientAction": false,
|
||
"hasClientLoader": false,
|
||
"hasClientMiddleware": false,
|
||
"hasDefaultExport": true,
|
||
"hasErrorBoundary": false,
|
||
"module": "/assets/blog._slug-BZ9bXQPU.js",
|
||
"imports": [
|
||
"/assets/components-BCKQC8SP.js",
|
||
"/assets/lib-QSj_asTz.js",
|
||
"/assets/jsx-runtime-CX_JklLS.js",
|
||
"/assets/ContentImage-1Eo_082L.js",
|
||
"/assets/clsx-eT0YPcGk.js",
|
||
"/assets/BlogCategoryLink-CBscx2Ka.js",
|
||
"/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js",
|
||
"/assets/Details-Cn3nNItz.js",
|
||
"/assets/Skittle-zj2xJWaI.js",
|
||
"/assets/SiteFooter-Dluz6Abx.js",
|
||
"/assets/SiteHeader-P2mSEufD.js",
|
||
"/assets/Vimeo-CHApOCjU.js",
|
||
"/assets/YouTube-CubVn4yq.js",
|
||
"/assets/date-Dqn8aLix.js",
|
||
"/assets/errorBoundaries-Cy4CQIhJ.js",
|
||
"/assets/keyboard-CthKfdGk.js",
|
||
"/assets/use-tab-direction-B6ScOFcW.js",
|
||
"/assets/index-D4hfWY9o.js",
|
||
"/assets/use-inert-others-Dz0UAR7O.js",
|
||
"/assets/description-CXyhf9fU.js",
|
||
"/assets/use-is-mounted-C-XJl2Wd.js",
|
||
"/assets/types-WVQySR0i.js",
|
||
"/assets/current-EBMVq01n.js",
|
||
"/assets/analytics.client-DNdghg7R.js",
|
||
"/assets/bugs-B0F8iVAJ.js"
|
||
],
|
||
"css": []
|
||
},
|
||
"routes/_index": {
|
||
"id": "routes/_index",
|
||
"parentId": "root",
|
||
"index": true,
|
||
"hasAction": false,
|
||
"hasLoader": true,
|
||
"hasClientAction": false,
|
||
"hasClientLoader": false,
|
||
"hasClientMiddleware": false,
|
||
"hasDefaultExport": true,
|
||
"hasErrorBoundary": false,
|
||
"module": "/assets/_index-4awD-5br.js",
|
||
"imports": [
|
||
"/assets/components-BCKQC8SP.js",
|
||
"/assets/lib-QSj_asTz.js",
|
||
"/assets/jsx-runtime-CX_JklLS.js",
|
||
"/assets/Logo-DTkDiSqL.js",
|
||
"/assets/SiteFooter-Dluz6Abx.js",
|
||
"/assets/SiteHeader-P2mSEufD.js",
|
||
"/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js",
|
||
"/assets/bugs-B0F8iVAJ.js",
|
||
"/assets/keyboard-CthKfdGk.js",
|
||
"/assets/use-is-mounted-C-XJl2Wd.js",
|
||
"/assets/use-tab-direction-B6ScOFcW.js",
|
||
"/assets/errorBoundaries-Cy4CQIhJ.js",
|
||
"/assets/clsx-eT0YPcGk.js",
|
||
"/assets/current-EBMVq01n.js",
|
||
"/assets/analytics.client-DNdghg7R.js",
|
||
"/assets/index-D4hfWY9o.js"
|
||
],
|
||
"css": []
|
||
},
|
||
"routes/blog._index": {
|
||
"id": "routes/blog._index",
|
||
"parentId": "routes/blog",
|
||
"index": true,
|
||
"hasAction": false,
|
||
"hasLoader": true,
|
||
"hasClientAction": false,
|
||
"hasClientLoader": false,
|
||
"hasClientMiddleware": false,
|
||
"hasDefaultExport": true,
|
||
"hasErrorBoundary": false,
|
||
"module": "/assets/blog._index-BVagdGd7.js",
|
||
"imports": [
|
||
"/assets/components-BCKQC8SP.js",
|
||
"/assets/jsx-runtime-CX_JklLS.js",
|
||
"/assets/social-Cd2AtOZM.js",
|
||
"/assets/BlogCategoryLink-CBscx2Ka.js",
|
||
"/assets/BlogPostLink-BVp8T5cV.js",
|
||
"/assets/BlogCategoryNav-BuIyrAx1.js",
|
||
"/assets/Paginator-DfDZkV_E.js",
|
||
"/assets/SiteFooter-Dluz6Abx.js",
|
||
"/assets/SiteHeader-P2mSEufD.js",
|
||
"/assets/date-Dqn8aLix.js",
|
||
"/assets/_.well-known_.mcp.server-card_.json_-D-jvmstG.js",
|
||
"/assets/lib-QSj_asTz.js",
|
||
"/assets/errorBoundaries-Cy4CQIhJ.js",
|
||
"/assets/clsx-eT0YPcGk.js",
|
||
"/assets/types-WVQySR0i.js",
|
||
"/assets/enumerator-425Ma5CR.js",
|
||
"/assets/current-EBMVq01n.js",
|
||
"/assets/analytics.client-DNdghg7R.js",
|
||
"/assets/bugs-B0F8iVAJ.js",
|
||
"/assets/keyboard-CthKfdGk.js",
|
||
"/assets/use-tab-direction-B6ScOFcW.js",
|
||
"/assets/index-D4hfWY9o.js"
|
||
],
|
||
"css": []
|
||
}
|
||
},
|
||
"url": "/assets/manifest-9195998b.js",
|
||
"version": "9195998b"
|
||
};
|
||
window.__reactRouterRouteModules = {"root":route0,"routes/blog":route1,"routes/blog.$slug":route2};
|
||
|
||
import("/assets/entry.client-kDMVV6Oi.js");</script><script type="application/ld+json" nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=">{"@context":"https://schema.org","@type":"Organization","name":"PlanetScale, Inc.","url":"https://planetscale.com","sameAs":["https://twitter.com/PlanetScale","https://www.facebook.com/planetscaledata/","https://www.instagram.com/planetscale/"],"address":{"@type":"PostalAddress","streetAddress":"WeWork c/o PlanetScale, 535 Mission Street, 14th Floor","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94105","addressCountry":"US"}}</script><!--$--><script nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=">window.__reactRouterContext.streamController.enqueue("[{\"_1\":2,\"_3\":-5,\"_4\":-5},\"loaderData\",{\"_5\":6,\"_7\":8},\"actionData\",\"errors\",\"root\",{\"_766\":767},\"routes/blog.$slug\",{\"_9\":10,\"_5\":11},\"blog\",{\"_12\":13,\"_14\":15,\"_16\":-7,\"_17\":18,\"_19\":20,\"_21\":22,\"_23\":24,\"_25\":26,\"_27\":28,\"_29\":30,\"_31\":32},\"https://planetscale.com\",\"body\",[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,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146],\"body_text\",\"Meet Neki: sharding for Postgres.Neki allows applications to connect to massive, sharded databases over a single connection string.This post takes apart the architecture from the bottom up, one piece at a time, starting with what's underneath all of it.\\nReal Postgres\\nNeki is built as a sharding and scaling solution for real Postgres.It's not a fork, nor a wire-compatible reimplementation, nor a MySQL sharding idea wearing a Postgres label.Neki uses ordinary PostgreSQL instances that store rows in Postgres data pages using MVCC, carry out transactions, and work as you would expect with psql and other Postgres drivers.Neki builds around those instances to let you shard them, scale them, and manage them as one database.\\nLet's take a look:\\n\\nPostgresManager\\nUsing vanilla Postgres means Neki needs a way to run and manage each instance.That includes starting and stopping Postgres, owning its data directory, and configuring replication so a new instance can join a shard.PostgresManager handles this coordination, running as the first process in the Postgres container and managing the postgres process directly.\\nSidecar\\nPostgres uses a separate backend process for each connection and limits how many can be open at once.Neki’s Sidecar sits in front of each instance and pools connections, letting many client connections share fewer Postgres backends.\\n\\nThe Router, which is the component that accepts external client connections, communicates with the Postgres nodes via these Sidecars.\\nIt also reports each Postgres instance's health and whether it is a primary or replica, so the rest of the cluster knows whether it can receive write queries.\\nThe pool doesn't treat every connection the same way.The length of time a connection is checked out for use varies depending on what it's being used for.A multi-statement transaction holds on to its connection until commit or rollback.A session-scoped advisory lock needs a connection of its own, because the lock has to outlive whatever transaction is open at the time and can't share that connection.Everything else checks a connection out and hands it back the moment the statement finishes.\\n\\nThe Sidecar knows which of the three to use because the Router sends the necessary information with the query: autocommit, an open transaction, or a session that has to stay on one backend.\\nShards\\nEach Postgres instance gets its own Sidecar and PostgresManager pair.Real deployments need more than one instance: a primary and its replicas.Neki calls that group a shard, the unit it splits data across.It's always advised to run a shard with a primary and 2+ replicas for high availability, as well as for additional read query capacity.\\nA shard is considered one Postgres cluster.Its replicas are physical copies of the primary, so they share a catalog and the same object identifiers.\\nObject Identifiers (OIDs) are how Postgres tracks objects internally, rather than by name.A client reads a column’s type OID off the wire to interpret its bytes and may cache that OID for later re-use.A custom type therefore needs to carry the same OID no matter which shard answers the query.Independent shards can assign that type different OIDs, so Neki designates one shard in the entire Neki cluster as the authoritative shard.This shard is the source of truth for translating custom type OIDs in responses from other shards to match.It ensures OIDs are consistent across the many shards of the Neki cluster.\\nThe authoritative shard's Sidecar also watches for schema changes and reports them to the Routers.This keeps the Routers' view of the schema current when a table is renamed or a column is dropped.\\nAdmin\\nIn a distributed system, instances can fail independently while the rest of the system lives on.Neki is no different.A primary or replica can go down at any moment while its fellow instances on the shard are healthy.The Admin's job is to detect failures, promote a replica, and maintain each shard’s durability policy.\\n\\nIt health-checks every Sidecar, tracks replication lag for each replica, and decides when a shard needs a new primary.When a primary goes down, it coordinates an emergency failover, promoting a replica to take its place.It can also coordinate a planned switchover, which are needed for intentional node resizes and version upgrades.In both situations, Admin uses pg_rewind to bring diverged instances onto the new primary’s timeline, copying only the data that changed since the timelines diverged.\\nEach shard has a durability policy that determines when a commit is acknowledged:\\nAsync: The primary acknowledges the commit without waiting for a replica.\\nSync: The primary waits for a replica to confirm the commit, protecting against the loss of a single node.\\nCross-zone sync: The primary waits for confirmation from a replica in another availability zone, protecting against the loss of the primary’s zone.\\nPostgres enforces whichever one is configured, using its own synchronous replication machinery.The Admin keeps that configuration correct as replicas join or leave shards, or a failover moves the primary to a different zone.\\nMuch of Admin’s work, however, doesn’t involve changing the primary.It repoints replicas to the correct replication source and corrects roles when Postgres and the topology disagree.\\nOperator\\nNeki’s components need to be deployed, updated, and replaced when their machines fail.Neki is built Kubernetes-first, and the Operator manages this full lifecycle.\\n\\nThe Operator models a cluster as a hierarchy.A cluster owns routers and shards, and each shard owns the pods running its Postgres instances and Sidecars.When the Neki cluster configuration changes, the Operator works out which pods need to be created, updated, or removed.\\nHow it replaces an instance depends on whether that instance is still running.For a live instance, the Operator builds a replacement and confirms it has caught up before deleting the old one.If a node fails and loses its ephemeral storage, the Operator rebuilds the lost instance from scratch once its safety checks pass.\\nAdmin and the Router handle the database side of those disruptions.Admin coordinates a switchover for planned primary replacements or a failover when a primary goes down.The Router can buffer queries that are safe to retry while a healthy primary becomes available.\\nRouter\\nWe've talked a lot about how the Neki cluster operates and handles failure internally.What we've yet to dive into is how applications use the thing!\\nThe Router is the entry point for clients connecting to a Neki cluster, presenting a single Postgres wire-protocol endpoint to connect to a (potentially) massive sharded database.Applications use Postgres drivers to send SQL and open transactions without managing connections to individual shards.\\n\\nAuthentication and role checks are done as if it were the Postgres instance itself, and the protocol's own extended-query flow and prepared-statement lifecycle are all built into the Router.\\nOnce a query arrives, the Router runs a Postgres-compatible parser against the authoritative shard's catalog, plans it against the current sharding layout, and sends it to whichever Sidecar needs to run it over gRPC.\\nNot every query can run on a single shard.A join may need data from several shards or an aggregate may need to read from all of them.The Router coordinates that work as a distributed query.\\nWhenever possible, it leaves the work to the Postgres instances.If both sides of a join are on the same shard, the Router sends the join to that shard.When a join needs to run across shards, the Router executes it itself, choosing between nested-loop, hash, and merge joins based on cost estimations.\\nRead more about Routers, parsing, and sharded query planning in our other blog, The lifecycle of a sharded Postgres query.\\nEarlier, we covered how Admin promotes a new primary during a switchover or failover.If that happens, the Router can buffer queries, giving the Admin time to complete the handover.For queries that can safely be retried after failing against a primary, the Router buffers the query and waits, for a fixed time, for a healthy primary.Once a healthy primary is available, the Router releases queued queries gradually.\\n\\nData Topology\\nRouter, Sidecars, and Admin all need a consistent picture of which shards exist, what key ranges they own, and which tables are sharded at all.If the Router's copy is wrong, a query can land on the wrong shard.This is all specified with a Data Topology, and etcd holds the single, authoritative copy of it.When the Data Topology changes, the Router, Sidecars, and Admin pick up the updated configuration without a restart or manual synchronization.\\nThe Data Topology defines shard groups, named sets of physical shards, each owning a range of routing keys.Each table belongs to a shard group.Shard indexes specify the columns or expressions and the strategy used to turn row values into routing keys.Those keys determine which shard receives each row.\\nReplicator\\nAs a database grows, its layout may need to change.Tables need to be imported, shards need to be split, and schemas need to change all while applications keep using the database.\\nNeki's Replicator handles the data movement behind all such operations.It runs as a separate process colocated with a shard's Sidecar and Postgres.It is responsible for copying existing rows to new destinations, and also keeping the data current by decoding changes from a Postgres logical replication stream and applying them as SQL.\\n\\nThree workflows use the Replicator:\\nMoveTables relocates a set of tables, including imports from an external Postgres instance\\nReshard redistributes data across shard key ranges, allowing a shard to be split when it outgrows its capacity\\nOnlineDDL changes a table's schema by building a shadow table alongside the original and keeping it current through the same change-data-capture pipeline MoveTables and Reshard use to relocate rows.A final rename swaps the new table into place.This supports changes such as repartitioning a table, alongside changes that would otherwise require a blocking operation.\\nOnce the data has been copied and the destination is caught up, the workflow switches from the original tables or shards to their replacements.This is the cutover.The Router uses the same buffering mechanism that handles primary changes for this step.It buffers queries during that switch and releases them afterward.\\nTogether, these components let Neki scale Postgres horizontally while presenting a single database to applications.\\nGet started\\nNeki is in Platform Preview right now.\\nStart a Neki cluster today: build on it from scratch, or import an existing Postgres database.\",\"aside\",\"toc\",[44,45,46,47,48,49,50,51,52,53],\"title\",\"The architecture of Neki\",\"authors\",[40],\"categories\",[38,39],\"excerpt\",\"Sharded Postgres, from the team behind Vitess\",\"createdAt\",\"2026-09-18\",\"slug\",\"the-architecture-of-neki\",\"meta\",{\"_33\":34,\"_35\":26,\"_36\":37,\"_19\":20},\"canonical\",\"https://planetscale.com/blog/the-architecture-of-neki\",\"description\",\"image\",\"/assets/the-architecture-of-neki-social-CvHXrMho.png\",\"engineering\",\"neki\",{\"_29\":41,\"_42\":43},\"harshit\",\"name\",\"Harshit Gangal\",{\"_54\":85,\"_56\":86,\"_58\":59,\"_19\":87},{\"_54\":82,\"_56\":83,\"_58\":59,\"_19\":84},{\"_54\":79,\"_56\":80,\"_58\":59,\"_19\":81},{\"_54\":76,\"_56\":77,\"_58\":59,\"_19\":78},{\"_54\":73,\"_56\":74,\"_58\":59,\"_19\":75},{\"_54\":70,\"_56\":71,\"_58\":59,\"_19\":72},{\"_54\":67,\"_56\":68,\"_58\":59,\"_19\":69},{\"_54\":64,\"_56\":65,\"_58\":59,\"_19\":66},{\"_54\":61,\"_56\":62,\"_58\":59,\"_19\":63},{\"_54\":55,\"_56\":57,\"_58\":59,\"_19\":60},\"children\",[],\"id\",\"get-started\",\"level\",2,\"Get started\",[],\"replicator\",\"Replicator\",[],\"data-topology\",\"Data Topology\",[],\"router\",\"Router\",[],\"operator\",\"Operator\",[],\"admin\",\"Admin\",[],\"shards\",\"Shards\",[],\"sidecar\",\"Sidecar\",[],\"postgresmanager\",\"PostgresManager\",[],\"real-postgres\",\"Real Postgres\",[\"SingleFetchClassInstance\",760],[\"SingleFetchClassInstance\",752],[\"SingleFetchClassInstance\",739],[\"SingleFetchClassInstance\",735],[\"SingleFetchClassInstance\",722],[\"SingleFetchClassInstance\",714],[\"SingleFetchClassInstance\",698],[\"SingleFetchClassInstance\",690],[\"SingleFetchClassInstance\",680],[\"SingleFetchClassInstance\",666],[\"SingleFetchClassInstance\",662],[\"SingleFetchClassInstance\",658],[\"SingleFetchClassInstance\",639],[\"SingleFetchClassInstance\",625],[\"SingleFetchClassInstance\",621],[\"SingleFetchClassInstance\",613],[\"SingleFetchClassInstance\",600],[\"SingleFetchClassInstance\",595],[\"SingleFetchClassInstance\",581],[\"SingleFetchClassInstance\",576],[\"SingleFetchClassInstance\",568],[\"SingleFetchClassInstance\",557],[\"SingleFetchClassInstance\",544],[\"SingleFetchClassInstance\",530],[\"SingleFetchClassInstance\",526],[\"SingleFetchClassInstance\",493],[\"SingleFetchClassInstance\",488],[\"SingleFetchClassInstance\",483],[\"SingleFetchClassInstance\",475],[\"SingleFetchClassInstance\",465],[\"SingleFetchClassInstance\",452],[\"SingleFetchClassInstance\",446],[\"SingleFetchClassInstance\",440],[\"SingleFetchClassInstance\",434],[\"SingleFetchClassInstance\",426],[\"SingleFetchClassInstance\",421],[\"SingleFetchClassInstance\",412],[\"SingleFetchClassInstance\",398],[\"SingleFetchClassInstance\",394],[\"SingleFetchClassInstance\",390],[\"SingleFetchClassInstance\",373],[\"SingleFetchClassInstance\",367],[\"SingleFetchClassInstance\",349],[\"SingleFetchClassInstance\",342],[\"SingleFetchClassInstance\",327],[\"SingleFetchClassInstance\",319],[\"SingleFetchClassInstance\",300],[\"SingleFetchClassInstance\",282],[\"SingleFetchClassInstance\",274],[\"SingleFetchClassInstance\",269],[\"SingleFetchClassInstance\",258],[\"SingleFetchClassInstance\",231],[\"SingleFetchClassInstance\",227],[\"SingleFetchClassInstance\",189],[\"SingleFetchClassInstance\",181],[\"SingleFetchClassInstance\",177],[\"SingleFetchClassInstance\",168],[\"SingleFetchClassInstance\",164],[\"SingleFetchClassInstance\",147],{\"_148\":149,\"_42\":150,\"_151\":152,\"_54\":153},\"$$mdtype\",\"Tag\",\"p\",\"attributes\",{},[154,155,156],\"Start a \",[\"SingleFetchClassInstance\",157],\" cluster today: build on it from scratch, or import an existing Postgres database.\",{\"_148\":149,\"_42\":158,\"_151\":159,\"_54\":160},\"a\",{\"_162\":163},[161],\"Neki\",\"href\",\"https://app.planetscale.com/new\",{\"_148\":149,\"_42\":150,\"_151\":165,\"_54\":166},{},[167],\"Neki is in Platform Preview right now.\",{\"_148\":149,\"_42\":169,\"_151\":170,\"_54\":171},\"h2\",{\"_56\":57},[172],[\"SingleFetchClassInstance\",173],{\"_148\":149,\"_42\":158,\"_151\":174,\"_54\":175},{\"_162\":176},[60],\"#get-started\",{\"_148\":149,\"_42\":150,\"_151\":178,\"_54\":179},{},[180],\"Together, these components let Neki scale Postgres horizontally while presenting a single database to applications.\",{\"_148\":149,\"_42\":150,\"_151\":182,\"_54\":183},{},[184,185,186,185,187,185,188],\"Once the data has been copied and the destination is caught up, the workflow switches from the original tables or shards to their replacements.\",\" \",\"This is the cutover.\",\"The Router uses the same buffering mechanism that handles primary changes for this step.\",\"It buffers queries during that switch and releases them afterward.\",{\"_148\":149,\"_42\":190,\"_151\":191,\"_54\":192},\"ul\",{},[193,194,195],[\"SingleFetchClassInstance\",218],[\"SingleFetchClassInstance\",209],[\"SingleFetchClassInstance\",196],{\"_148\":149,\"_42\":197,\"_151\":198,\"_54\":199},\"li\",{},[200,201,185,202,185,203],[\"SingleFetchClassInstance\",204],\" changes a table's schema by building a shadow table alongside the original and keeping it current through the same change-data-capture pipeline MoveTables and Reshard use to relocate rows.\",\"A final rename swaps the new table into place.\",\"This supports changes such as repartitioning a table, alongside changes that would otherwise require a blocking operation.\",{\"_148\":149,\"_42\":205,\"_151\":206,\"_54\":207},\"strong\",{},[208],\"OnlineDDL\",{\"_148\":149,\"_42\":197,\"_151\":210,\"_54\":211},{},[212,213],[\"SingleFetchClassInstance\",214],\" redistributes data across shard key ranges, allowing a shard to be split when it outgrows its capacity\",{\"_148\":149,\"_42\":205,\"_151\":215,\"_54\":216},{},[217],\"Reshard\",{\"_148\":149,\"_42\":197,\"_151\":219,\"_54\":220},{},[221,222],[\"SingleFetchClassInstance\",223],\" relocates a set of tables, including imports from an external Postgres instance\",{\"_148\":149,\"_42\":205,\"_151\":224,\"_54\":225},{},[226],\"MoveTables\",{\"_148\":149,\"_42\":150,\"_151\":228,\"_54\":229},{},[230],\"Three workflows use the Replicator:\",{\"_148\":149,\"_42\":150,\"_151\":232,\"_54\":233},{},[234],[\"SingleFetchClassInstance\",235],{\"_148\":149,\"_42\":236,\"_151\":237,\"_54\":238},\"ContentImage\",{\"_239\":240,\"_241\":242,\"_243\":244,\"_245\":246,\"_247\":248,\"_249\":250},[],\"alt\",\"A close-up of one Neki shard, with the separate Replicator beside each Postgres instance and its connection from Postgres highlighted in orange.\",\"height\",385,\"loading\",\"lazy\",\"src\",\"https://planetscale-images.imgix.net/assets/neki-replicator-highlight-flGISLaG.svg?auto=compress%2Cformat\",\"srcs\",[251,252],\"width\",704,{\"_253\":246,\"_255\":257},{\"_253\":254,\"_255\":256},\"srcSet\",\"https://planetscale-images.imgix.net/assets/neki-replicator-highlight-darkmode-D-xFVR2s.svg?auto=compress%2Cformat\",\"media\",\"(prefers-color-scheme: dark)\",\"(prefers-color-scheme: light), (prefers-color-scheme: no-preference)\",{\"_148\":149,\"_42\":150,\"_151\":259,\"_54\":260},{},[261,262,263,185,264,185,265],\"Neki's \",[\"SingleFetchClassInstance\",266],\" handles the data movement behind all such operations.\",\"It runs as a separate process colocated with a shard's Sidecar and Postgres.\",\"It is responsible for copying existing rows to new destinations, and also keeping the data current by decoding changes from a Postgres logical replication stream and applying them as SQL.\",{\"_148\":149,\"_42\":205,\"_151\":267,\"_54\":268},{},[63],{\"_148\":149,\"_42\":150,\"_151\":270,\"_54\":271},{},[272,185,273],\"As a database grows, its layout may need to change.\",\"Tables need to be imported, shards need to be split, and schemas need to change all while applications keep using the database.\",{\"_148\":149,\"_42\":169,\"_151\":275,\"_54\":276},{\"_56\":62},[277],[\"SingleFetchClassInstance\",278],{\"_148\":149,\"_42\":158,\"_151\":279,\"_54\":280},{\"_162\":281},[63],\"#replicator\",{\"_148\":149,\"_42\":150,\"_151\":283,\"_54\":284},{},[285,286,287,185,288,185,289,290,185,291],\"The Data Topology defines \",[\"SingleFetchClassInstance\",296],\", named sets of physical shards, each owning a range of routing keys.\",\"Each table belongs to a shard group.\",[\"SingleFetchClassInstance\",292],\" specify the columns or expressions and the strategy used to turn row values into routing keys.\",\"Those keys determine which shard receives each row.\",{\"_148\":149,\"_42\":205,\"_151\":293,\"_54\":294},{},[295],\"Shard indexes\",{\"_148\":149,\"_42\":205,\"_151\":297,\"_54\":298},{},[299],\"shard groups\",{\"_148\":149,\"_42\":150,\"_151\":301,\"_54\":302},{},[303,185,304,185,305,306,307,308,309,185,310],\"Router, Sidecars, and Admin all need a consistent picture of which shards exist, what key ranges they own, and which tables are sharded at all.\",\"If the Router's copy is wrong, a query can land on the wrong shard.\",\"This is all specified with a \",[\"SingleFetchClassInstance\",315],\", and \",[\"SingleFetchClassInstance\",311],\" holds the single, authoritative copy of it.\",\"When the Data Topology changes, the Router, Sidecars, and Admin pick up the updated configuration without a restart or manual synchronization.\",{\"_148\":149,\"_42\":205,\"_151\":312,\"_54\":313},{},[314],\"etcd\",{\"_148\":149,\"_42\":158,\"_151\":316,\"_54\":317},{\"_162\":318},[66],\"/blog/what-is-a-data-topology\",{\"_148\":149,\"_42\":169,\"_151\":320,\"_54\":321},{\"_56\":65},[322],[\"SingleFetchClassInstance\",323],{\"_148\":149,\"_42\":158,\"_151\":324,\"_54\":325},{\"_162\":326},[66],\"#data-topology\",{\"_148\":149,\"_42\":150,\"_151\":328,\"_54\":329},{},[330],[\"SingleFetchClassInstance\",331],{\"_148\":149,\"_42\":236,\"_151\":332,\"_54\":333},{\"_239\":334,\"_241\":335,\"_243\":244,\"_245\":336,\"_247\":337,\"_249\":338},[],\"A query hits an unavailable primary and gets buffered. Usually the primary recovers within the time and size limit and the queued query is released gradually, so the client sees nothing. Rarely, the limit is reached first and an error is returned to the client.\",500,\"https://planetscale-images.imgix.net/assets/neki-router-buffering-DChcV18g.svg?auto=compress%2Cformat\",[339,340],1504,{\"_253\":336,\"_255\":257},{\"_253\":341,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-router-buffering-darkmode-HQltRPC0.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":343,\"_54\":344},{},[345,185,346,185,347,185,348],\"Earlier, we covered how Admin promotes a new primary during a switchover or failover.\",\"If that happens, the Router can buffer queries, giving the Admin time to complete the handover.\",\"For queries that can safely be retried after failing against a primary, the Router buffers the query and waits, for a fixed time, for a healthy primary.\",\"Once a healthy primary is available, the Router releases queued queries gradually.\",{\"_148\":149,\"_42\":350,\"_151\":351,\"_54\":352},\"Callout\",{\"_365\":366},[353],[\"SingleFetchClassInstance\",354],{\"_148\":149,\"_42\":150,\"_151\":355,\"_54\":356},{},[357,358,359],\"Read more about Routers, parsing, and sharded query planning in our other blog, \",[\"SingleFetchClassInstance\",360],\".\",{\"_148\":149,\"_42\":158,\"_151\":361,\"_54\":362},{\"_162\":364},[363],\"The lifecycle of a sharded Postgres query\",\"/blog/the-lifecycle-of-a-sharded-postgres-query\",\"type\",\"note\",{\"_148\":149,\"_42\":150,\"_151\":368,\"_54\":369},{},[370,185,371,185,372],\"Whenever possible, it leaves the work to the Postgres instances.\",\"If both sides of a join are on the same shard, the Router sends the join to that shard.\",\"When a join needs to run across shards, the Router executes it itself, choosing between nested-loop, hash, and merge joins based on cost estimations.\",{\"_148\":149,\"_42\":150,\"_151\":374,\"_54\":375},{},[376,185,377,185,378,379,380,381,359],\"Not every query can run on a single shard.\",\"A join may need data from several shards or an aggregate may need to read from all of them.\",\"The \",[\"SingleFetchClassInstance\",387],\" coordinates that work as a \",[\"SingleFetchClassInstance\",382],{\"_148\":149,\"_42\":158,\"_151\":383,\"_54\":384},{\"_162\":386},[385],\"distributed query\",\"/blog/what-is-a-neki-router\",{\"_148\":149,\"_42\":205,\"_151\":388,\"_54\":389},{},[69],{\"_148\":149,\"_42\":150,\"_151\":391,\"_54\":392},{},[393],\"Once a query arrives, the Router runs a Postgres-compatible parser against the authoritative shard's catalog, plans it against the current sharding layout, and sends it to whichever Sidecar needs to run it over gRPC.\",{\"_148\":149,\"_42\":150,\"_151\":395,\"_54\":396},{},[397],\"Authentication and role checks are done as if it were the Postgres instance itself, and the protocol's own extended-query flow and prepared-statement lifecycle are all built into the Router.\",{\"_148\":149,\"_42\":150,\"_151\":399,\"_54\":400},{},[401],[\"SingleFetchClassInstance\",402],{\"_148\":149,\"_42\":236,\"_151\":403,\"_54\":404},{\"_239\":405,\"_241\":406,\"_243\":244,\"_245\":407,\"_247\":408,\"_249\":338},[],\"The same cluster diagram with the Router and etcd highlighted and everything else faded.\",1058,\"https://planetscale-images.imgix.net/assets/neki-router-highlight-D4fCw8OU.svg?auto=compress%2Cformat\",[409,410],{\"_253\":407,\"_255\":257},{\"_253\":411,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-router-highlight-darkmode-DzHaPeoU.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":413,\"_54\":414},{},[378,415,416,185,417],[\"SingleFetchClassInstance\",418],\" is the entry point for clients connecting to a Neki cluster, presenting a single Postgres wire-protocol endpoint to connect to a (potentially) massive sharded database.\",\"Applications use Postgres drivers to send SQL and open transactions without managing connections to individual shards.\",{\"_148\":149,\"_42\":205,\"_151\":419,\"_54\":420},{},[69],{\"_148\":149,\"_42\":150,\"_151\":422,\"_54\":423},{},[424,185,425],\"We've talked a lot about how the Neki cluster operates and handles failure internally.\",\"What we've yet to dive into is how applications use the thing!\",{\"_148\":149,\"_42\":169,\"_151\":427,\"_54\":428},{\"_56\":68},[429],[\"SingleFetchClassInstance\",430],{\"_148\":149,\"_42\":158,\"_151\":431,\"_54\":432},{\"_162\":433},[69],\"#router\",{\"_148\":149,\"_42\":150,\"_151\":435,\"_54\":436},{},[437,185,438,185,439],\"Admin and the Router handle the database side of those disruptions.\",\"Admin coordinates a switchover for planned primary replacements or a failover when a primary goes down.\",\"The Router can buffer queries that are safe to retry while a healthy primary becomes available.\",{\"_148\":149,\"_42\":150,\"_151\":441,\"_54\":442},{},[443,185,444,185,445],\"How it replaces an instance depends on whether that instance is still running.\",\"For a live instance, the Operator builds a replacement and confirms it has caught up before deleting the old one.\",\"If a node fails and loses its ephemeral storage, the Operator rebuilds the lost instance from scratch once its safety checks pass.\",{\"_148\":149,\"_42\":150,\"_151\":447,\"_54\":448},{},[449,185,450,185,451],\"The Operator models a cluster as a hierarchy.\",\"A cluster owns routers and shards, and each shard owns the pods running its Postgres instances and Sidecars.\",\"When the Neki cluster configuration changes, the Operator works out which pods need to be created, updated, or removed.\",{\"_148\":149,\"_42\":150,\"_151\":453,\"_54\":454},{},[455],[\"SingleFetchClassInstance\",456],{\"_148\":149,\"_42\":236,\"_151\":457,\"_54\":458},{\"_239\":459,\"_241\":406,\"_243\":244,\"_245\":460,\"_247\":461,\"_249\":338},[],\"The same cluster diagram with the Operator highlighted and everything else faded.\",\"https://planetscale-images.imgix.net/assets/neki-operator-highlight-BD94jQsB.svg?auto=compress%2Cformat\",[462,463],{\"_253\":460,\"_255\":257},{\"_253\":464,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-operator-highlight-darkmode-8OihOEsZ.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":466,\"_54\":467},{},[468,185,469,470,471],\"Neki’s components need to be deployed, updated, and replaced when their machines fail.\",\"Neki is built Kubernetes-first, and the \",[\"SingleFetchClassInstance\",472],\" manages this full lifecycle.\",{\"_148\":149,\"_42\":205,\"_151\":473,\"_54\":474},{},[72],{\"_148\":149,\"_42\":169,\"_151\":476,\"_54\":477},{\"_56\":71},[478],[\"SingleFetchClassInstance\",479],{\"_148\":149,\"_42\":158,\"_151\":480,\"_54\":481},{\"_162\":482},[72],\"#operator\",{\"_148\":149,\"_42\":150,\"_151\":484,\"_54\":485},{},[486,185,487],\"Much of Admin’s work, however, doesn’t involve changing the primary.\",\"It repoints replicas to the correct replication source and corrects roles when Postgres and the topology disagree.\",{\"_148\":149,\"_42\":150,\"_151\":489,\"_54\":490},{},[491,185,492],\"Postgres enforces whichever one is configured, using its own synchronous replication machinery.\",\"The Admin keeps that configuration correct as replicas join or leave shards, or a failover moves the primary to a different zone.\",{\"_148\":149,\"_42\":190,\"_151\":494,\"_54\":495},{},[496,497,498],[\"SingleFetchClassInstance\",517],[\"SingleFetchClassInstance\",508],[\"SingleFetchClassInstance\",499],{\"_148\":149,\"_42\":197,\"_151\":500,\"_54\":501},{},[502,503],[\"SingleFetchClassInstance\",504],\" The primary waits for confirmation from a replica in another availability zone, protecting against the loss of the primary’s zone.\",{\"_148\":149,\"_42\":205,\"_151\":505,\"_54\":506},{},[507],\"Cross-zone sync:\",{\"_148\":149,\"_42\":197,\"_151\":509,\"_54\":510},{},[511,512],[\"SingleFetchClassInstance\",513],\" The primary waits for a replica to confirm the commit, protecting against the loss of a single node.\",{\"_148\":149,\"_42\":205,\"_151\":514,\"_54\":515},{},[516],\"Sync:\",{\"_148\":149,\"_42\":197,\"_151\":518,\"_54\":519},{},[520,521],[\"SingleFetchClassInstance\",522],\" The primary acknowledges the commit without waiting for a replica.\",{\"_148\":149,\"_42\":205,\"_151\":523,\"_54\":524},{},[525],\"Async:\",{\"_148\":149,\"_42\":150,\"_151\":527,\"_54\":528},{},[529],\"Each shard has a durability policy that determines when a commit is acknowledged:\",{\"_148\":149,\"_42\":150,\"_151\":531,\"_54\":532},{},[533,185,534,185,535,185,536,537,538],\"It health-checks every Sidecar, tracks replication lag for each replica, and decides when a shard needs a new primary.\",\"When a primary goes down, it coordinates an emergency failover, promoting a replica to take its place.\",\"It can also coordinate a planned switchover, which are needed for intentional node resizes and version upgrades.\",\"In both situations, Admin uses \",[\"SingleFetchClassInstance\",539],\" to bring diverged instances onto the new primary’s timeline, copying only the data that changed since the timelines diverged.\",{\"_148\":149,\"_42\":540,\"_151\":541,\"_54\":542},\"code\",{},[543],\"pg_rewind\",{\"_148\":149,\"_42\":150,\"_151\":545,\"_54\":546},{},[547],[\"SingleFetchClassInstance\",548],{\"_148\":149,\"_42\":236,\"_151\":549,\"_54\":550},{\"_239\":551,\"_241\":406,\"_243\":244,\"_245\":552,\"_247\":553,\"_249\":338},[],\"The same cluster diagram with Admin and the control plane highlighted and everything else faded.\",\"https://planetscale-images.imgix.net/assets/neki-admin-highlight-Bz1IxBf6.svg?auto=compress%2Cformat\",[554,555],{\"_253\":552,\"_255\":257},{\"_253\":556,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-admin-highlight-darkmode-Uru2fPFH.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":558,\"_54\":559},{},[560,185,561,185,562,185,378,563,564],\"In a distributed system, instances can fail independently while the rest of the system lives on.\",\"Neki is no different.\",\"A primary or replica can go down at any moment while its fellow instances on the shard are healthy.\",[\"SingleFetchClassInstance\",565],\"'s job is to detect failures, promote a replica, and maintain each shard’s durability policy.\",{\"_148\":149,\"_42\":205,\"_151\":566,\"_54\":567},{},[75],{\"_148\":149,\"_42\":169,\"_151\":569,\"_54\":570},{\"_56\":74},[571],[\"SingleFetchClassInstance\",572],{\"_148\":149,\"_42\":158,\"_151\":573,\"_54\":574},{\"_162\":575},[75],\"#admin\",{\"_148\":149,\"_42\":150,\"_151\":577,\"_54\":578},{},[579,185,580],\"The authoritative shard's Sidecar also watches for schema changes and reports them to the Routers.\",\"This keeps the Routers' view of the schema current when a table is renamed or a column is dropped.\",{\"_148\":149,\"_42\":150,\"_151\":582,\"_54\":583},{},[584,185,585,185,586,185,587,588,359,185,589,185,590],\"Object Identifiers (OIDs) are how Postgres tracks objects internally, rather than by name.\",\"A client reads a column’s type OID off the wire to interpret its bytes and may cache that OID for later re-use.\",\"A custom type therefore needs to carry the same OID no matter which shard answers the query.\",\"Independent shards can assign that type different OIDs, so Neki designates one shard in the entire Neki cluster as the \",[\"SingleFetchClassInstance\",591],\"This shard is the source of truth for translating custom type OIDs in responses from other shards to match.\",\"It ensures OIDs are consistent across the many shards of the Neki cluster.\",{\"_148\":149,\"_42\":205,\"_151\":592,\"_54\":593},{},[594],\"authoritative shard\",{\"_148\":149,\"_42\":150,\"_151\":596,\"_54\":597},{},[598,185,599],\"A shard is considered one Postgres cluster.\",\"Its replicas are physical copies of the primary, so they share a catalog and the same object identifiers.\",{\"_148\":149,\"_42\":150,\"_151\":601,\"_54\":602},{},[603,185,604,185,605,606,607,185,608],\"Each Postgres instance gets its own Sidecar and PostgresManager pair.\",\"Real deployments need more than one instance: a primary and its replicas.\",\"Neki calls that group a \",[\"SingleFetchClassInstance\",609],\", the unit it splits data across.\",\"It's always advised to run a shard with a primary and 2+ replicas for high availability, as well as for additional read query capacity.\",{\"_148\":149,\"_42\":205,\"_151\":610,\"_54\":611},{},[612],\"shard\",{\"_148\":149,\"_42\":169,\"_151\":614,\"_54\":615},{\"_56\":77},[616],[\"SingleFetchClassInstance\",617],{\"_148\":149,\"_42\":158,\"_151\":618,\"_54\":619},{\"_162\":620},[78],\"#shards\",{\"_148\":149,\"_42\":150,\"_151\":622,\"_54\":623},{},[624],\"The Sidecar knows which of the three to use because the Router sends the necessary information with the query: autocommit, an open transaction, or a session that has to stay on one backend.\",{\"_148\":149,\"_42\":150,\"_151\":626,\"_54\":627},{},[628],[\"SingleFetchClassInstance\",629],{\"_148\":149,\"_42\":236,\"_151\":630,\"_54\":631},{\"_239\":632,\"_241\":633,\"_243\":244,\"_245\":634,\"_247\":635,\"_249\":338},[],\"Three connection-pool lifetimes in the Sidecar: a shared connection for an autocommit statement, a dedicated connection for an open transaction, and a reserved connection for a session advisory lock.\",454,\"https://planetscale-images.imgix.net/assets/neki-sidecar-pooling-tiers-DqP_9-5W.svg?auto=compress%2Cformat\",[636,637],{\"_253\":634,\"_255\":257},{\"_253\":638,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-sidecar-pooling-tiers-darkmode-CsEWlGUM.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":640,\"_54\":641},{},[642,185,643,185,644,645,646,647,359,185,648,185,649],\"The pool doesn't treat every connection the same way.\",\"The length of time a connection is checked out for use varies depending on what it's being used for.\",\"A multi-statement transaction holds on to its connection until \",[\"SingleFetchClassInstance\",654],\" or \",[\"SingleFetchClassInstance\",650],\"A session-scoped advisory lock needs a connection of its own, because the lock has to outlive whatever transaction is open at the time and can't share that connection.\",\"Everything else checks a connection out and hands it back the moment the statement finishes.\",{\"_148\":149,\"_42\":540,\"_151\":651,\"_54\":652},{},[653],\"rollback\",{\"_148\":149,\"_42\":540,\"_151\":655,\"_54\":656},{},[657],\"commit\",{\"_148\":149,\"_42\":150,\"_151\":659,\"_54\":660},{},[661],\"It also reports each Postgres instance's health and whether it is a primary or replica, so the rest of the cluster knows whether it can receive write queries.\",{\"_148\":149,\"_42\":150,\"_151\":663,\"_54\":664},{},[665],\"The Router, which is the component that accepts external client connections, communicates with the Postgres nodes via these Sidecars.\",{\"_148\":149,\"_42\":150,\"_151\":667,\"_54\":668},{},[669],[\"SingleFetchClassInstance\",670],{\"_148\":149,\"_42\":236,\"_151\":671,\"_54\":672},{\"_239\":673,\"_241\":674,\"_243\":244,\"_245\":675,\"_247\":676,\"_249\":250},[],\"A close-up of one Neki shard: the Router sends queries to a primary and replica, each with its own Sidecar highlighted in orange alongside Postgres, PostgresManager, and Replicator.\",555,\"https://planetscale-images.imgix.net/assets/neki-sidecar-highlight-B8X81j7N.svg?auto=compress%2Cformat\",[677,678],{\"_253\":675,\"_255\":257},{\"_253\":679,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-sidecar-highlight-darkmode-CNtJVItJ.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":681,\"_54\":682},{},[683,185,684,685,686],\"Postgres uses a separate backend process for each connection and limits how many can be open at once.\",\"Neki’s \",[\"SingleFetchClassInstance\",687],\" sits in front of each instance and pools connections, letting many client connections share fewer Postgres backends.\",{\"_148\":149,\"_42\":205,\"_151\":688,\"_54\":689},{},[81],{\"_148\":149,\"_42\":169,\"_151\":691,\"_54\":692},{\"_56\":80},[693],[\"SingleFetchClassInstance\",694],{\"_148\":149,\"_42\":158,\"_151\":695,\"_54\":696},{\"_162\":697},[81],\"#sidecar\",{\"_148\":149,\"_42\":150,\"_151\":699,\"_54\":700},{},[701,185,702,185,703,704,705,706],\"Using vanilla Postgres means Neki needs a way to run and manage each instance.\",\"That includes starting and stopping Postgres, owning its data directory, and configuring replication so a new instance can join a shard.\",[\"SingleFetchClassInstance\",711],\" handles this coordination, running as the first process in the Postgres container and managing the \",[\"SingleFetchClassInstance\",707],\" process directly.\",{\"_148\":149,\"_42\":540,\"_151\":708,\"_54\":709},{},[710],\"postgres\",{\"_148\":149,\"_42\":205,\"_151\":712,\"_54\":713},{},[84],{\"_148\":149,\"_42\":169,\"_151\":715,\"_54\":716},{\"_56\":83},[717],[\"SingleFetchClassInstance\",718],{\"_148\":149,\"_42\":158,\"_151\":719,\"_54\":720},{\"_162\":721},[84],\"#postgresmanager\",{\"_148\":149,\"_42\":150,\"_151\":723,\"_54\":724},{},[725],[\"SingleFetchClassInstance\",726],{\"_148\":149,\"_42\":236,\"_151\":727,\"_54\":728},{\"_239\":729,\"_241\":406,\"_243\":244,\"_245\":730,\"_247\":731,\"_249\":338},[],\"The full Neki cluster: an application talking to the Router, two shards each with a primary and a replica running Sidecar, Postgres, and Replicator, PostgresManager controlling each Postgres instance, Admin and etcd forming the control plane, and an Operator provisioning every pod.\",\"https://planetscale-images.imgix.net/assets/neki-cluster-architecture-BdagkgJK.svg?auto=compress%2Cformat\",[732,733],{\"_253\":730,\"_255\":257},{\"_253\":734,\"_255\":256},\"https://planetscale-images.imgix.net/assets/neki-cluster-architecture-darkmode-YGx-3wht.svg?auto=compress%2Cformat\",{\"_148\":149,\"_42\":150,\"_151\":736,\"_54\":737},{},[738],\"Let's take a look:\",{\"_148\":149,\"_42\":150,\"_151\":740,\"_54\":741},{},[742,185,743,185,744,745,746,185,747],\"Neki is built as a sharding and scaling solution for real Postgres.\",\"It's not a fork, nor a wire-compatible reimplementation, nor a MySQL sharding idea wearing a Postgres label.\",\"Neki uses ordinary PostgreSQL instances that store rows in Postgres data pages using MVCC, carry out transactions, and work as you would expect with \",[\"SingleFetchClassInstance\",748],\" and other Postgres drivers.\",\"Neki builds around those instances to let you shard them, scale them, and manage them as one database.\",{\"_148\":149,\"_42\":540,\"_151\":749,\"_54\":750},{},[751],\"psql\",{\"_148\":149,\"_42\":169,\"_151\":753,\"_54\":754},{\"_56\":86},[755],[\"SingleFetchClassInstance\",756],{\"_148\":149,\"_42\":158,\"_151\":757,\"_54\":758},{\"_162\":759},[87],\"#real-postgres\",{\"_148\":149,\"_42\":150,\"_151\":761,\"_54\":762},{},[763,185,764,185,765],\"Meet Neki: sharding for Postgres.\",\"Neki allows applications to connect to massive, sharded databases over a single connection string.\",\"This post takes apart the architecture from the bottom up, one piece at a time, starting with what's underneath all of it.\",\"current\",{\"_768\":769,\"_770\":771,\"_772\":769},\"development\",false,\"env\",{\"_773\":774,\"_775\":776,\"_777\":778,\"_779\":780,\"_781\":782},\"userSignedIn\",\"IMAGE_CDN\",\"https://planetscale-images.imgix.net\",\"IMAGE_CDN_ENABLED\",\"true\",\"INTERNAL_API\",\"https://api.planetscale.com\",\"RELEASE\",\"37f93714-95f0-4b50-97b0-f5723adc3e5c\",\"SENTRY_DSN\",\"https://bd81903b44804e22a06bdc0c1a91b303@o499952.ingest.us.sentry.io/4504531942572032\"]\n");</script><!--$--><script nonce="oIoqroymOzDCpErh5u8Wtsx1kZn8X9/Z5reiXu46MW4=">window.__reactRouterContext.streamController.close();</script><!--/$--><!--/$--></body></html> |