<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Kronotop | Blog</title><description/><link>https://kronotop.com/</link><language>en</language><item><title>The .join() That Should Be a Bug</title><link>https://kronotop.com/blog/the-join-that-should-be-a-bug/</link><guid isPermaLink="true">https://kronotop.com/blog/the-join-that-should-be-a-bug/</guid><description>Serving thousands of connections on a backend where every call blocks.

</description><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h3 id=&quot;serving-thousands-of-connections-on-a-backend-where-every-call-blocks&quot;&gt;Serving thousands of connections on a backend where every call blocks&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;There are two well-known ways to implement connection management in database systems. Each way has a cost. &lt;a href=&quot;https://github.com/kronotop/kronotop&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kronotop&lt;/a&gt; takes a third approach. Let’s start with the problem we have.&lt;/p&gt;
&lt;p&gt;Kronotop stores its metadata in &lt;a href=&quot;https://github.com/apple/foundationdb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;FoundationDB&lt;/a&gt;. It also stores document bodies on the local file system. So almost every operation a client asks for turns into a network call to FDB and a
read from the disk. A network call takes milliseconds, not nanoseconds. Reading a document, committing a transaction, looking up an index: all of it waits on I/O. Our command path is
not in-memory work. It is dominated by waiting.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;two-classic-models&quot;&gt;Two classic models&lt;/h2&gt;&lt;/div&gt;
&lt;svg viewBox=&quot;0 0 760 280&quot; role=&quot;img&quot; aria-label=&quot;Two classic models. Redis uses one thread for many connections: it scales but cannot block. Postgres uses one process per connection: it can block but does not scale. Kronotop needs both.&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; font-family=&quot;&amp;#x27;IBM Plex Sans&amp;#x27;, system-ui, sans-serif&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;760&quot; height=&quot;280&quot; rx=&quot;14&quot; fill=&quot;#061827&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;30&quot; y=&quot;28&quot; width=&quot;320&quot; height=&quot;168&quot; rx=&quot;12&quot; fill=&quot;#0a2030&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;190&quot; y=&quot;62&quot; text-anchor=&quot;middle&quot; fill=&quot;#00c5d1&quot; font-size=&quot;18&quot; font-weight=&quot;700&quot;&gt;Redis&lt;/text&gt;&lt;text x=&quot;190&quot; y=&quot;82&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;12&quot;&gt;one thread&lt;/text&gt;&lt;line x1=&quot;93&quot; y1=&quot;112&quot; x2=&quot;215&quot; y2=&quot;145&quot; stroke=&quot;#2c4d63&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;93&quot; y1=&quot;145&quot; x2=&quot;215&quot; y2=&quot;145&quot; stroke=&quot;#2c4d63&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;93&quot; y1=&quot;178&quot; x2=&quot;215&quot; y2=&quot;145&quot; stroke=&quot;#2c4d63&quot;&gt;&lt;/line&gt;&lt;circle cx=&quot;88&quot; cy=&quot;112&quot; r=&quot;5&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;88&quot; cy=&quot;145&quot; r=&quot;5&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;88&quot; cy=&quot;178&quot; r=&quot;5&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;rect x=&quot;215&quot; y=&quot;133&quot; width=&quot;110&quot; height=&quot;24&quot; rx=&quot;6&quot; fill=&quot;#0d2738&quot; stroke=&quot;#00c5d1&quot; stroke-width=&quot;2&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;270&quot; y=&quot;149&quot; text-anchor=&quot;middle&quot; fill=&quot;#00c5d1&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot;&gt;1 thread&lt;/text&gt;&lt;text x=&quot;95&quot; y=&quot;228&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;13&quot;&gt;✓&lt;/text&gt;&lt;text x=&quot;114&quot; y=&quot;228&quot; fill=&quot;#c4d2db&quot; font-size=&quot;12.5&quot;&gt;holds thousands cheaply&lt;/text&gt;&lt;text x=&quot;95&quot; y=&quot;252&quot; fill=&quot;#f0907e&quot; font-size=&quot;13&quot;&gt;✕&lt;/text&gt;&lt;text x=&quot;114&quot; y=&quot;252&quot; fill=&quot;#c4d2db&quot; font-size=&quot;12.5&quot;&gt;nothing may block&lt;/text&gt;&lt;circle cx=&quot;380&quot; cy=&quot;112&quot; r=&quot;17&quot; fill=&quot;#061827&quot; stroke=&quot;#234659&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;380&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;12&quot; font-style=&quot;italic&quot;&gt;vs&lt;/text&gt;&lt;rect x=&quot;410&quot; y=&quot;28&quot; width=&quot;320&quot; height=&quot;168&quot; rx=&quot;12&quot; fill=&quot;#0a2030&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;570&quot; y=&quot;62&quot; text-anchor=&quot;middle&quot; fill=&quot;#00c5d1&quot; font-size=&quot;18&quot; font-weight=&quot;700&quot;&gt;Postgres&lt;/text&gt;&lt;text x=&quot;570&quot; y=&quot;82&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;12&quot;&gt;process per connection&lt;/text&gt;&lt;line x1=&quot;473&quot; y1=&quot;112&quot; x2=&quot;595&quot; y2=&quot;112&quot; stroke=&quot;#2c4d63&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;473&quot; y1=&quot;145&quot; x2=&quot;595&quot; y2=&quot;145&quot; stroke=&quot;#2c4d63&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;473&quot; y1=&quot;178&quot; x2=&quot;595&quot; y2=&quot;178&quot; stroke=&quot;#2c4d63&quot;&gt;&lt;/line&gt;&lt;circle cx=&quot;468&quot; cy=&quot;112&quot; r=&quot;5&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;468&quot; cy=&quot;145&quot; r=&quot;5&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;circle cx=&quot;468&quot; cy=&quot;178&quot; r=&quot;5&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;rect x=&quot;595&quot; y=&quot;100&quot; width=&quot;110&quot; height=&quot;24&quot; rx=&quot;6&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;595&quot; y=&quot;133&quot; width=&quot;110&quot; height=&quot;24&quot; rx=&quot;6&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;595&quot; y=&quot;166&quot; width=&quot;110&quot; height=&quot;24&quot; rx=&quot;6&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;650&quot; y=&quot;116&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11&quot;&gt;process&lt;/text&gt;&lt;text x=&quot;650&quot; y=&quot;149&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11&quot;&gt;process&lt;/text&gt;&lt;text x=&quot;650&quot; y=&quot;182&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11&quot;&gt;process&lt;/text&gt;&lt;text x=&quot;475&quot; y=&quot;228&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;13&quot;&gt;✓&lt;/text&gt;&lt;text x=&quot;494&quot; y=&quot;228&quot; fill=&quot;#c4d2db&quot; font-size=&quot;12.5&quot;&gt;any command may block&lt;/text&gt;&lt;text x=&quot;475&quot; y=&quot;252&quot; fill=&quot;#f0907e&quot; font-size=&quot;13&quot;&gt;✕&lt;/text&gt;&lt;text x=&quot;494&quot; y=&quot;252&quot; fill=&quot;#c4d2db&quot; font-size=&quot;12.5&quot;&gt;one process per connection, heavy&lt;/text&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;88;215;215&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;112;145;145&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.15s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;88;215;215&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.15s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;145;145;145&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.15s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.3s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;88;215;215&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.3s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;178;145;145&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.3s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.45s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;468;595;595&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.45s&quot; repeatCount=&quot;indefinite&quot; values=&quot;112&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.45s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.6s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;468;595;595&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.6s&quot; repeatCount=&quot;indefinite&quot; values=&quot;145&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.6s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.75s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;468;595;595&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.75s&quot; repeatCount=&quot;indefinite&quot; values=&quot;178&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.75s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;/svg&gt;
&lt;p&gt;Redis(before 6.0) runs a single thread. That thread listens on every connection and processes commands one at a time. This scales connections well, but it forbids blocking.
Nothing in the command path is allowed to wait. If one command waited, every other client would wait with it. That is fine when all your data is in memory. It does not work when
a command has to do more. Our commands call a transactional store over the network, and they append data to a file on disk and call fsync. Every one of those steps blocks.&lt;/p&gt;
&lt;p&gt;Postgres goes the other way. It gives each connection its own process. Now blocking is not a problem. Each connection runs on its own, so you can write plain, sequential code.
But there is a price! A connection is now an operating system process, and that is heavy. A few thousand connections become too much. This is why Postgres deployments almost
always sit behind a separate connection pooler.&lt;/p&gt;
&lt;p&gt;One model scales connections but cannot wait. The other can wait but does not scale large numbers of idle/open connections cheaply. Kronotop needs both.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;splitting-the-connection-from-the-work&quot;&gt;Splitting the connection from the work&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The trick is to stop treating “the connection” and “the work” as the same thing.&lt;/p&gt;
&lt;p&gt;The connection side works the way Redis does. We build it on &lt;a href=&quot;https://netty.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Netty&lt;/a&gt;. A small number of event loop threads listen on many sockets. They react to whatever socket is ready.
They parse the incoming command and write the reply back. This part never blocks and never waits. So a handful of threads can keep thousands of connections alive.&lt;/p&gt;
&lt;p&gt;The work side is the part that does disk and network I/O. It runs somewhere else, on a &lt;a href=&quot;https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;virtual thread&lt;/a&gt;. A virtual thread lets us write the I/O call as plain, blocking, top-to-bottom code.
It is the Postgres style, but without the Postgres cost. The virtual thread blocks while it waits on FoundationDB or the disk. When it blocks, the Java runtime unmounts it and frees the carrier
thread underneath for other work. Thousands of virtual threads can wait on I/O at the same time, while only a few real threads do anything. When the I/O completes, the virtual thread
picks up where it left off.&lt;/p&gt;
&lt;p&gt;So the network threads stay free. They can serve every other connection. The slow part happens off to the side, where waiting is cheap. The result is then handed back to the connection
thread, which writes the reply. We keep the write there by design. That hand-off is the one rule we keep strict.&lt;/p&gt;
&lt;svg viewBox=&quot;0 0 760 440&quot; role=&quot;img&quot; aria-label=&quot;The connection side runs on Netty event loop threads that never block. Slow FoundationDB work is offloaded to a virtual thread, which blocks cheaply while the carrier thread is freed. When the result is ready it is handed back to a Netty thread, where the reply is always written.&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; font-family=&quot;&amp;#x27;IBM Plex Sans&amp;#x27;, system-ui, sans-serif&quot;&gt;&lt;defs&gt;&lt;marker id=&quot;ar-arch&quot; markerWidth=&quot;10&quot; markerHeight=&quot;10&quot; refX=&quot;7&quot; refY=&quot;3.5&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0,0 L7,3.5 L0,7 Z&quot; fill=&quot;#5d7e92&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;760&quot; height=&quot;440&quot; rx=&quot;14&quot; fill=&quot;#061827&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;200&quot; y=&quot;56&quot; fill=&quot;#00c5d1&quot; font-size=&quot;10.5&quot; letter-spacing=&quot;1.5&quot; opacity=&quot;0.85&quot;&gt;CONNECTION SIDE&lt;/text&gt;&lt;text x=&quot;200&quot; y=&quot;290&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;10.5&quot; letter-spacing=&quot;1.5&quot; opacity=&quot;0.85&quot;&gt;WORK SIDE&lt;/text&gt;&lt;text x=&quot;28&quot; y=&quot;62&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11&quot;&gt;clients&lt;/text&gt;&lt;rect x=&quot;28&quot; y=&quot;70&quot; width=&quot;70&quot; height=&quot;18&quot; rx=&quot;5&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;28&quot; y=&quot;96&quot; width=&quot;70&quot; height=&quot;18&quot; rx=&quot;5&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;28&quot; y=&quot;122&quot; width=&quot;70&quot; height=&quot;18&quot; rx=&quot;5&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;28&quot; y=&quot;148&quot; width=&quot;70&quot; height=&quot;18&quot; rx=&quot;5&quot; fill=&quot;#0d2738&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;63&quot; y=&quot;186&quot; text-anchor=&quot;middle&quot; fill=&quot;#5d7e92&quot; font-size=&quot;13&quot;&gt;…&lt;/text&gt;&lt;line x1=&quot;98&quot; y1=&quot;79&quot; x2=&quot;200&quot; y2=&quot;125&quot; stroke=&quot;#234659&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;98&quot; y1=&quot;105&quot; x2=&quot;200&quot; y2=&quot;125&quot; stroke=&quot;#234659&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;98&quot; y1=&quot;131&quot; x2=&quot;200&quot; y2=&quot;125&quot; stroke=&quot;#234659&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;98&quot; y1=&quot;157&quot; x2=&quot;200&quot; y2=&quot;125&quot; stroke=&quot;#234659&quot;&gt;&lt;/line&gt;&lt;rect x=&quot;200&quot; y=&quot;70&quot; width=&quot;530&quot; height=&quot;110&quot; rx=&quot;12&quot; fill=&quot;#0d2738&quot; stroke=&quot;#00c5d1&quot; stroke-width=&quot;2&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;216&quot; y=&quot;104&quot; fill=&quot;#00c5d1&quot; font-size=&quot;17&quot; font-weight=&quot;700&quot;&gt;Netty event loop&lt;/text&gt;&lt;text x=&quot;216&quot; y=&quot;128&quot; fill=&quot;#9db4c2&quot; font-size=&quot;12.5&quot;&gt;A few threads serve thousands of sockets. Parse and write.&lt;/text&gt;&lt;text x=&quot;216&quot; y=&quot;148&quot; fill=&quot;#9db4c2&quot; font-size=&quot;12.5&quot;&gt;Never blocks, never waits.&lt;/text&gt;&lt;text x=&quot;500&quot; y=&quot;168&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;12&quot;&gt;the reply is always written here&lt;/text&gt;&lt;rect x=&quot;200&quot; y=&quot;300&quot; width=&quot;530&quot; height=&quot;110&quot; rx=&quot;12&quot; fill=&quot;#0d2738&quot; stroke=&quot;#c6ff3a&quot; stroke-width=&quot;2&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;216&quot; y=&quot;336&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;17&quot; font-weight=&quot;700&quot;&gt;Virtual thread&lt;/text&gt;&lt;text x=&quot;216&quot; y=&quot;362&quot; font-size=&quot;13.5&quot;&gt;&lt;tspan fill=&quot;#cfeef1&quot; font-family=&quot;&amp;#x27;IBM Plex Mono&amp;#x27;, monospace&quot;&gt;tr.get(…).join()&lt;/tspan&gt;&lt;tspan fill=&quot;#9db4c2&quot;&gt;  blocks on FDB or disk&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;216&quot; y=&quot;386&quot; fill=&quot;#9db4c2&quot; font-size=&quot;12.5&quot;&gt;The runtime parks it. The carrier thread is freed for other work.&lt;/text&gt;&lt;line x1=&quot;290&quot; y1=&quot;186&quot; x2=&quot;290&quot; y2=&quot;296&quot; stroke=&quot;#5d7e92&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#ar-arch)&quot;&gt;&lt;/line&gt;&lt;circle cx=&quot;290&quot; cy=&quot;214&quot; r=&quot;12&quot; fill=&quot;#00c5d1&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;290&quot; y=&quot;218&quot; text-anchor=&quot;middle&quot; fill=&quot;#061827&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot;&gt;1&lt;/text&gt;&lt;text x=&quot;310&quot; y=&quot;210&quot; fill=&quot;#eaf2f6&quot; font-size=&quot;13&quot;&gt;offload&lt;/text&gt;&lt;text x=&quot;310&quot; y=&quot;228&quot; fill=&quot;#7fa6b8&quot; font-size=&quot;11&quot; font-family=&quot;&amp;#x27;IBM Plex Mono&amp;#x27;, monospace&quot;&gt;supplyAsync(…, vtExecutor)&lt;/text&gt;&lt;line x1=&quot;560&quot; y1=&quot;296&quot; x2=&quot;560&quot; y2=&quot;186&quot; stroke=&quot;#5d7e92&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#ar-arch)&quot;&gt;&lt;/line&gt;&lt;circle cx=&quot;560&quot; cy=&quot;266&quot; r=&quot;12&quot; fill=&quot;#c6ff3a&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;560&quot; y=&quot;270&quot; text-anchor=&quot;middle&quot; fill=&quot;#061827&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot;&gt;2&lt;/text&gt;&lt;text x=&quot;540&quot; y=&quot;262&quot; text-anchor=&quot;end&quot; fill=&quot;#eaf2f6&quot; font-size=&quot;13&quot;&gt;result&lt;/text&gt;&lt;text x=&quot;540&quot; y=&quot;280&quot; text-anchor=&quot;end&quot; fill=&quot;#7fa6b8&quot; font-size=&quot;11&quot; font-family=&quot;&amp;#x27;IBM Plex Mono&amp;#x27;, monospace&quot;&gt;thenAcceptAsync(…, nettyExecutor)&lt;/text&gt;&lt;circle r=&quot;3.4&quot; fill=&quot;#c6ff3a&quot; cx=&quot;98&quot; cy=&quot;79&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;98;200;200&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;79;125;125&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.4&quot; fill=&quot;#c6ff3a&quot; cx=&quot;98&quot; cy=&quot;105&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.12s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;98;200;200&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.12s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;105;125;125&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.12s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.4&quot; fill=&quot;#c6ff3a&quot; cx=&quot;98&quot; cy=&quot;131&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.24s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;98;200;200&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.24s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;131;125;125&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.24s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.4&quot; fill=&quot;#c6ff3a&quot; cx=&quot;98&quot; cy=&quot;157&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cx&quot; dur=&quot;5s&quot; begin=&quot;0.36s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;98;200;200&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;0.36s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;157;125;125&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;0.36s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; cx=&quot;290&quot; cy=&quot;186&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;1s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;186;296;296&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;1s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;circle r=&quot;3.6&quot; fill=&quot;#c6ff3a&quot; cx=&quot;560&quot; cy=&quot;296&quot; opacity=&quot;0&quot;&gt;&lt;animate attributeName=&quot;cy&quot; dur=&quot;5s&quot; begin=&quot;2.5s&quot; repeatCount=&quot;indefinite&quot; calcMode=&quot;spline&quot; keyTimes=&quot;0;0.09;1&quot; keySplines=&quot;0.4 0 0.3 1;0 0 1 1&quot; values=&quot;296;186;186&quot;&gt;&lt;/animate&gt;&lt;animate attributeName=&quot;opacity&quot; dur=&quot;5s&quot; begin=&quot;2.5s&quot; repeatCount=&quot;indefinite&quot; keyTimes=&quot;0;0.015;0.07;0.095;1&quot; values=&quot;0;1;1;0;0&quot;&gt;&lt;/animate&gt;&lt;/circle&gt;&lt;/svg&gt;
&lt;p&gt;In code, the whole offload comes down to two moving parts:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;CompletableFuture&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;supplyAsync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;supplier, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;// run on a virtual thread&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;thenAcceptAsync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;action, &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getCtx&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;executor&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;;           &lt;/span&gt;&lt;span&gt;// resume on the Netty thread&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Two executors, two phases. The &lt;code dir=&quot;auto&quot;&gt;supplier&lt;/code&gt; is the slow part. It runs on the virtual thread executor, where blocking on FoundationDB is allowed. The &lt;code dir=&quot;auto&quot;&gt;action&lt;/code&gt; is the reply. It runs on &lt;code dir=&quot;auto&quot;&gt;response.getCtx().executor()&lt;/code&gt;, the Netty event loop that owns this connection. So the work that waits and the write that must not move stay on separate threads. The second only starts once the first is done.&lt;/p&gt;
&lt;p&gt;A read command is written against exactly that shape. The first block fetches the value, the second sends it:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt; request, &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt; response&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;AsyncCommandExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;supplyAsync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context, response,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;// virtual thread: open the transaction and wait on FDB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;Session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getSession&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;Transaction&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tr&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionUtil&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getOrCreateTransaction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context, session&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;DirectorySubspace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subspace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;openZMapSubspace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;tr, session&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tr&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;subspace&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pack&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getKey&lt;/span&gt;&lt;span&gt;()))&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(value) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;// Netty thread: write the reply&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;writeFullBulkString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toMessage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;.join()&lt;/code&gt; in the middle is the interesting line. On a normal thread it would be a waste. The thread would sit there, blocked and useless. On a virtual thread it is the whole point.
The runtime parks the call and frees the real thread. It resumes the call when FDB answers. The handler reads top to bottom, like plain blocking code, and the cost of waiting is gone.
Once the supplier returns, the executor moves to the reply phase. If the command was an auto-commit, it cleans up the transaction. If something threw, it writes the error back to the client.&lt;/p&gt;
&lt;p&gt;You can see the relevant implementation parts from the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kronotop/kronotop/blob/main/kronotop/src/main/java/com/kronotop/AsyncCommandExecutor.java&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AsyncCommandExecutor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kronotop/kronotop/blob/main/kronotop/src/main/java/com/kronotop/zmap/handlers/ZGetRangeHandler.java&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ZGetRangeHandler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kronotop/kronotop/blob/main/kronotop/src/main/java/com/kronotop/bucket/handlers/BucketQueryHandler.java&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;BucketQueryHandler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;what-lives-on-a-connection&quot;&gt;What lives on a connection&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A connection carries a small amount of state while it is open. It knows which client it is, which namespaces it has open, and whether it has authenticated. It also holds the details of
any transaction in flight.&lt;/p&gt;
&lt;p&gt;That last part matters. In Kronotop, a transaction belongs to the connection. You begin it, run several commands inside it, and then commit or roll it back. The whole time, that transaction
is tied to your session. If your connection drops in the middle, the open transaction is cancelled and cleaned up. Nothing is left dangling. You can also reset your session state without
dropping the connection. This clears cursors, watched keys, and any half-finished transaction in one step. That reset is also what makes a connection easy to reuse in a client-side pool.
You hand it back, reset it, and hand it out again.&lt;/p&gt;
&lt;svg viewBox=&quot;0 0 760 300&quot; role=&quot;img&quot; aria-label=&quot;A connection is a session that holds client identity, open namespaces, auth state, and a transaction in flight. If the connection drops, the open transaction is cancelled and cleaned up. A reset clears cursors, watched keys, and any half-finished transaction so the connection can be reused.&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; font-family=&quot;&amp;#x27;IBM Plex Sans&amp;#x27;, system-ui, sans-serif&quot;&gt;&lt;defs&gt;&lt;marker id=&quot;ar-state&quot; markerWidth=&quot;10&quot; markerHeight=&quot;10&quot; refX=&quot;7&quot; refY=&quot;3.5&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0,0 L7,3.5 L0,7 Z&quot; fill=&quot;#5d7e92&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;760&quot; height=&quot;300&quot; rx=&quot;14&quot; fill=&quot;#061827&quot;&gt;&lt;/rect&gt;&lt;rect x=&quot;130&quot; y=&quot;28&quot; width=&quot;500&quot; height=&quot;120&quot; rx=&quot;12&quot; fill=&quot;#0d2738&quot; stroke=&quot;#00c5d1&quot; stroke-width=&quot;2&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;380&quot; y=&quot;56&quot; text-anchor=&quot;middle&quot; fill=&quot;#00c5d1&quot; font-size=&quot;16&quot; font-weight=&quot;700&quot;&gt;A connection is a session&lt;/text&gt;&lt;rect x=&quot;146&quot; y=&quot;76&quot; width=&quot;108&quot; height=&quot;44&quot; rx=&quot;9&quot; fill=&quot;#0a2030&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;200&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#cdd8e0&quot; font-size=&quot;11.5&quot;&gt;client&lt;/text&gt;&lt;text x=&quot;200&quot; y=&quot;112&quot; text-anchor=&quot;middle&quot; fill=&quot;#cdd8e0&quot; font-size=&quot;11.5&quot;&gt;identity&lt;/text&gt;&lt;rect x=&quot;266&quot; y=&quot;76&quot; width=&quot;108&quot; height=&quot;44&quot; rx=&quot;9&quot; fill=&quot;#0a2030&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;320&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#cdd8e0&quot; font-size=&quot;11.5&quot;&gt;open&lt;/text&gt;&lt;text x=&quot;320&quot; y=&quot;112&quot; text-anchor=&quot;middle&quot; fill=&quot;#cdd8e0&quot; font-size=&quot;11.5&quot;&gt;namespaces&lt;/text&gt;&lt;rect x=&quot;386&quot; y=&quot;76&quot; width=&quot;108&quot; height=&quot;44&quot; rx=&quot;9&quot; fill=&quot;#0a2030&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;440&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#cdd8e0&quot; font-size=&quot;11.5&quot;&gt;auth&lt;/text&gt;&lt;text x=&quot;440&quot; y=&quot;112&quot; text-anchor=&quot;middle&quot; fill=&quot;#cdd8e0&quot; font-size=&quot;11.5&quot;&gt;state&lt;/text&gt;&lt;rect x=&quot;506&quot; y=&quot;76&quot; width=&quot;108&quot; height=&quot;44&quot; rx=&quot;9&quot; fill=&quot;#0a2030&quot; stroke=&quot;#c6ff3a&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;560&quot; y=&quot;96&quot; text-anchor=&quot;middle&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;11.5&quot;&gt;transaction&lt;/text&gt;&lt;text x=&quot;560&quot; y=&quot;112&quot; text-anchor=&quot;middle&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;11.5&quot;&gt;in flight&lt;/text&gt;&lt;line x1=&quot;255&quot; y1=&quot;150&quot; x2=&quot;255&quot; y2=&quot;192&quot; stroke=&quot;#5d7e92&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#ar-state)&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;505&quot; y1=&quot;150&quot; x2=&quot;505&quot; y2=&quot;192&quot; stroke=&quot;#5d7e92&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#ar-state)&quot;&gt;&lt;/line&gt;&lt;rect x=&quot;140&quot; y=&quot;196&quot; width=&quot;230&quot; height=&quot;84&quot; rx=&quot;10&quot; fill=&quot;#0a2030&quot; stroke=&quot;#234659&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;255&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; fill=&quot;#eaf2f6&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot;&gt;Connection drops&lt;/text&gt;&lt;text x=&quot;255&quot; y=&quot;242&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11.5&quot;&gt;the open transaction is cancelled&lt;/text&gt;&lt;text x=&quot;255&quot; y=&quot;260&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11.5&quot;&gt;and cleaned up, nothing dangles&lt;/text&gt;&lt;rect x=&quot;390&quot; y=&quot;196&quot; width=&quot;230&quot; height=&quot;84&quot; rx=&quot;10&quot; fill=&quot;#0a2030&quot; stroke=&quot;#c6ff3a&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;505&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; fill=&quot;#c6ff3a&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot;&gt;RESET&lt;/text&gt;&lt;text x=&quot;505&quot; y=&quot;242&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11.5&quot;&gt;clears cursors, watched keys,&lt;/text&gt;&lt;text x=&quot;505&quot; y=&quot;260&quot; text-anchor=&quot;middle&quot; fill=&quot;#9db4c2&quot; font-size=&quot;11.5&quot;&gt;and any half-finished transaction&lt;/text&gt;&lt;/svg&gt;
&lt;div&gt;&lt;h2 id=&quot;try-kronotop&quot;&gt;Try Kronotop&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you read this post until the end, why not try Kronotop?&lt;/p&gt;
&lt;p&gt;Kronotop is a distributed, transactional document database built on FoundationDB. See the &lt;a href=&quot;https://kronotop.com/docs/quickstart&quot;&gt;Quickstart&lt;/a&gt; guide to start a cluster in a few minutes.&lt;/p&gt;</content:encoded><category>Architecture</category><category>Java</category><category>Kronotop Internals</category></item><item><title>Kronotop developer preview is out!</title><link>https://kronotop.com/blog/kronotop-developer-preview-is-out/</link><guid isPermaLink="true">https://kronotop.com/blog/kronotop-developer-preview-is-out/</guid><description>The first developer preview of Kronotop is out. You can try it with Docker Compose in a few minutes.

</description><pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;After almost 3 years of development, the first developer preview of Kronotop is out!&lt;/p&gt;
&lt;p&gt;Kronotop is a distributed multi-model database built on FoundationDB. It provides a document model (Bucket)
and an ordered key-value model (ZMap). Both live inside namespaces and share one transaction boundary.&lt;/p&gt;
&lt;p&gt;Kronotop uses RESP2 and RESP3 as the wire protocol, so it’s compatible with the existing RESP ecosystem.&lt;/p&gt;
&lt;p&gt;Please note that this is a developer preview: the architecture and the transaction model are stable, but
APIs and internal formats can still change before the first stable release.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;try-it&quot;&gt;Try it&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Run a demo cluster with Docker Compose, then insert a document and read it back:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-O&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://kronotop.com/kronotop-quickstart.yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;kronotop-quickstart.yaml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://kronotop.com/docs/quickstart/&quot;&gt;Quickstart&lt;/a&gt; covers the rest: how to connect and run your first query.&lt;/p&gt;
&lt;p&gt;Questions and feedback are welcome on &lt;a href=&quot;https://discord.gg/VPRNvdh2C&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Discord&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>release</category></item></channel></rss>