<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Omnigres</title>
    <description>The latest articles on Forem by Omnigres (@omnigres).</description>
    <link>https://forem.com/omnigres</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F6489%2F68c3bded-6a89-4eda-9f1d-69a1c79b0c76.png</url>
      <title>Forem: Omnigres</title>
      <link>https://forem.com/omnigres</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/omnigres"/>
    <language>en</language>
    <item>
      <title>Firing Developers Won’t Make Software Cheaper</title>
      <dc:creator>Yurii Rashkovskii</dc:creator>
      <pubDate>Tue, 08 Jul 2025 17:31:22 +0000</pubDate>
      <link>https://forem.com/omnigres/firing-developers-wont-make-software-cheaper-4p3j</link>
      <guid>https://forem.com/omnigres/firing-developers-wont-make-software-cheaper-4p3j</guid>
      <description>&lt;p&gt;Everyone cheering AI coding tools says the quiet part out loud eventually:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“So… when can I fire my developers?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the real subtext no one puts in their blog posts.&lt;/p&gt;

&lt;p&gt;“A new class of builders is emerging” → (Translation: maybe we don’t need expensive engineers?)&lt;br&gt;
“Focus on outcomes, not methods” → (Translation: maybe we don’t need people who care about methods?)&lt;/p&gt;

&lt;p&gt;Under the polished veneer of empowerment, the motivation is clear: cut costs, move faster. And there’s nothing inherently wrong with that.&lt;/p&gt;

&lt;p&gt;Except we’re focusing on the visible output of software developers, and confusing that with their value. Simulating the output doesn’t eliminate the role — it just replaces developers with something fast, unpredictable, and equally expensive in other ways.&lt;/p&gt;

&lt;p&gt;You're still left with messy, brittle, over-complicated systems – just built faster and broken differently.&lt;/p&gt;

&lt;p&gt;The real move isn’t to replace developers with AI.&lt;/p&gt;

&lt;p&gt;It’s to stop needing so much software engineering, operations, and maintenance in the first place.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fantasy of Fewer Engineers
&lt;/h3&gt;

&lt;p&gt;Every few years, a new wave of tooling arrives that promises to make engineers obsolete. Even in the early days. COBOL. 4GLs. Visual Basic. No-code. Now, AI.&lt;/p&gt;

&lt;p&gt;Each time, the hope is the same: let someone else (or something else) do the building. But the underlying complexity doesn’t go away. It just gets hidden. Until it leaks.&lt;/p&gt;

&lt;p&gt;AI today is like hiring a half-drunk junior developer: fast, tireless, and unaware of consequences. It can spit out working code, but it can’t reason about tradeoffs, edge cases, evolving requirements, or failure modes.&lt;/p&gt;

&lt;p&gt;Software isn't expensive because of syntax. It's expensive because systems live for years, not minutes.&lt;/p&gt;

&lt;p&gt;The Problem Isn’t Developers. It’s Over-Engineering&lt;br&gt;
If you're trying to build business systems cheaper, the goal shouldn't be firing developers.&lt;/p&gt;

&lt;p&gt;The goal should be to need less bespoke code in the first place.&lt;/p&gt;

&lt;p&gt;Too many products are weighed down by:&lt;/p&gt;

&lt;p&gt;Separation of concerns&lt;br&gt;
Over-abundant, over-dependent microservices&lt;br&gt;
Ongoing maintenance and migration tax&lt;/p&gt;

&lt;p&gt;We've been chasing clever architectures instead of asking whether we need this much architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data, Not Software, Is the Core of Business Systems
&lt;/h3&gt;

&lt;p&gt;That’s what I’m working on.&lt;/p&gt;

&lt;p&gt;Since 2004, for over 20 years, I haven’t been able to shake the belief that the real heart of most business systems isn’t the code.&lt;/p&gt;

&lt;p&gt;It’s the data.&lt;/p&gt;

&lt;p&gt;Business workflows are usually just data state machines: records moving through stages, reacting to events, triggering outcomes.&lt;/p&gt;

&lt;p&gt;But data has been:&lt;/p&gt;

&lt;p&gt;Misunderstood by app developers&lt;br&gt;
Constrained by layers of abstractions&lt;br&gt;
Boxed away in the realm of DBAs and managed services&lt;/p&gt;

&lt;p&gt;What if we stopped pushing data to the edges of the system? What if we started with data and let systems emerge from there?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Future: Data-Native Architectures
&lt;/h3&gt;

&lt;p&gt;Not AI-generated code. Not yet another abstraction layer. But a fundamental shift in how we model, build, and reason about systems.&lt;/p&gt;

&lt;p&gt;If we want cheaper, more maintainable business software, we need to stop repeating the cycle: new tool, same mess, slightly better.&lt;/p&gt;

&lt;p&gt;We must reduce the need for software and understand that data is not buckets of "things" but a structured and fluid representation of reality. &lt;/p&gt;

&lt;p&gt;Let’s stop asking, "When can I fire my developers?"&lt;/p&gt;

&lt;p&gt;Let’s ask, "What can we build so we don’t need as many developers in the first place?"&lt;/p&gt;

&lt;p&gt;Let’s stop hiding the complexity.&lt;/p&gt;

&lt;p&gt;Let’s eliminate it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What happens if you put HTTP server inside Postgres?</title>
      <dc:creator>Yurii Rashkovskii</dc:creator>
      <pubDate>Mon, 06 Feb 2023 15:53:36 +0000</pubDate>
      <link>https://forem.com/omnigres/what-happens-if-you-put-http-server-inside-postgres-33kd</link>
      <guid>https://forem.com/omnigres/what-happens-if-you-put-http-server-inside-postgres-33kd</guid>
      <description>&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@clarktibbs?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Clark Tibbs&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/oqStl2L5oxI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Benchmarks and performance claims are attention-grabbers, but that's not what drew me to work on Omnigres. When I first built a prototype of its HTTP server, I didn't foresee the desire to share the numbers. As we all know, getting benchmarks right is hard, and everybody's mileage may vary. But I'll show you some numbers here anyway. It'll be great to validate or invalidate my findings!&lt;/p&gt;

&lt;p&gt;But first, what's Omnigres? The shortest definition I came up with is "Postgres as a Platform."&lt;/p&gt;

&lt;p&gt;What do I mean by this? Well, this comes from the idea that much of your application and its infrastructure can live inside or next to Postgres. Your business logic, deployment orchestration, caching, job queues, API endpoints, form handlers, HTTP server, you name it. Instead of building a system composed of multiple services, you only need one server that takes care of it. And if it can scale, you've got something great!&lt;/p&gt;

&lt;p&gt;I am not the only one intrigued by this idea (and many don't like it, either): check out &lt;a href="https://news.ycombinator.com/item?id=33934139" rel="noopener noreferrer"&gt;this HN thread&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And it is not "one-size-fits-all." But it fits a good set of problems. Anyway, that's a subject for another conversation.&lt;/p&gt;

&lt;p&gt;In the past weeks, I've taken the task of adding an embedded HTTP server to Omnigres. Since Omnigres is &lt;a href="https://dev.to/omnigres/why-not-rust-for-omnigres-43ak"&gt;implemented in C &lt;/a&gt;, it was only natural for me to choose &lt;a href="https://github.com/h2o/h2o" rel="noopener noreferrer"&gt;libh2o&lt;/a&gt; to implement the functionality of an HTTP server. It did help that H2O is known for its good performance characteristics. &lt;/p&gt;

&lt;p&gt;The idea was relatively simple: initialize a few HTTP worker processes, each quickly handing off details of the incoming HTTP requests to a Postgres instance in the same process. &lt;/p&gt;

&lt;p&gt;Sounds a bit weird, right? But bear with me, I will try to explain what's going on.&lt;/p&gt;

&lt;p&gt;Below is a sample piece of code of how an HTTP request can be handled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;omni_httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;omni_httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'content-type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Hello, &amp;lt;b&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/b&amp;gt;!'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;
       &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
       &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;string_to_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;UNION&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;omni_httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'method'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'query_string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;
       &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first query in the union joins the request path of &lt;code&gt;/users/:user&lt;/code&gt; with the &lt;code&gt;users&lt;/code&gt; table on &lt;code&gt;users.handle&lt;/code&gt; column and serves an HTML response with that user's name.&lt;/p&gt;

&lt;p&gt;The second query, being lower in priority, simply returns a 404 Not Found response with a JSON that contains some of the request details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ http POST localhost:9000/users/johndoe
HTTP/1.1 200 OK
Connection: keep-alive
Server: omni_httpd-0.1
content-type: text/html
transfer-encoding: chunked

Hello, &amp;lt;b&amp;gt;John&amp;lt;/b&amp;gt;!

$ http POST localhost:9000/test\?q
HTTP/1.1 404 OK
Connection: keep-alive
Server: omni_httpd-0.1
content-type: text/json
transfer-encoding: chunked

{
    "method": "POST",
    "path": "/test",
    "query_string": "q"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once all this worked, I decided to try benchmark it (mostly because I wanted to see how bad would it be):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wrk http://localhost:9000/users/johndoe -c 20 -t 10
Running 10s test @ http://localhost:9000/users/johndoe
  10 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   354.85us    1.40ms  47.24ms   97.05%
    Req/Sec    12.51k     2.37k   25.01k    73.84%
  1256199 requests in 10.10s, 179.70MB read
Requests/sec: 124366.73
Transfer/sec:     17.79MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's on MacBook Pro M1 Max. I've also ran the same test on an older x86_64-based Linux machine and got about 70K/sec.&lt;/p&gt;

&lt;p&gt;For reference, I decided to try comparing this with a similar (but quick-and-dirty-written) Node.js implementation that goes to the database for the same effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* connection details */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;idleTimeMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;split&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT users.name FROM users WHERE users.handle = $1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]}).&lt;/span&gt;
        &lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;qres&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qres&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello, &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;qres&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/b&amp;gt;!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestListener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is running on http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I re-ran the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wrk http://localhost:8000/users/johndoe -c 20 -t 10
Running 10s test @ http://localhost:8000/users/johndoe
  10 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.11ms    0.99ms  33.09ms   99.15%
    Req/Sec     1.89k   166.30     2.19k    92.18%
  190319 requests in 10.10s, 33.76MB read
Requests/sec:  18838.56
Transfer/sec:      3.34MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the surface, it looks like omni_httpd is faster for the time being! I am sure with time it'll get slower and I did not explore enough optimizations available for the Node.js experiment (please contribute to make this picture fairer!). But it shows what bundling can do to performance!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: As &lt;a href="https://lobste.rs/s/39ao1j/what_happens_if_you_put_http_server_inside#c_ipefyz" rel="noopener noreferrer"&gt;someone pointed out&lt;/a&gt;, using TCP locally brings unnecessary overhead where UNIX sockets would have performed better. So, I re-ran the Node.js test over UNIX sockets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wrk http://localhost:8000/users/johndoe -c 20 -t 10
Running 10s test @ http://localhost:8000/users/johndoe
  10 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   663.15us  224.77us   9.87ms   93.24%
    Req/Sec     3.05k   139.17     3.30k    88.71%
  306649 requests in 10.10s, 54.39MB read
Requests/sec:  30361.88
Transfer/sec:      5.39MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's indeed &lt;em&gt;quite a bit&lt;/em&gt; faster but still way below the numbers I was able to get with &lt;code&gt;omni_httpd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I want to emphasize that performance was not and is not a primary goal behind this work. What I think is more important there is changing the way we think about serving HTTP clients, doing data-aware routing, etc. There's still a lot of research to be done on this end.&lt;/p&gt;

&lt;p&gt;I also want to mention that this is still early work. There's a lot to be done to make this server production-grade (starting from &lt;a href="https://github.com/omnigres/omnigres/issues/44" rel="noopener noreferrer"&gt;enabling HTTPS support&lt;/a&gt;, and improving the architecture to make &lt;a href="https://github.com/omnigres/omnigres/issues/40" rel="noopener noreferrer"&gt;HTTP2 multiplexing really work&lt;/a&gt;, but not ending there), as well as building out other extensions to make building applications really easy and convenient.&lt;/p&gt;

&lt;p&gt;If you want to chat more, join us on &lt;a href="https://discord.gg/Jghrq588qS" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or &lt;a href="http://t.me/omnigres" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>devto</category>
      <category>announcement</category>
      <category>community</category>
      <category>career</category>
    </item>
    <item>
      <title>Why not Rust for Omnigres?</title>
      <dc:creator>Yurii Rashkovskii</dc:creator>
      <pubDate>Fri, 06 Jan 2023 17:19:09 +0000</pubDate>
      <link>https://forem.com/omnigres/why-not-rust-for-omnigres-43ak</link>
      <guid>https://forem.com/omnigres/why-not-rust-for-omnigres-43ak</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/omnigres/omnigres" rel="noopener noreferrer"&gt;Omnigres&lt;/a&gt; is a new project to turn Postgres into a complete development and production deployment platform. I've started it to reflect on the complexity and inefficiencies plaguing modern business software development. &lt;/p&gt;

&lt;p&gt;As an aging (and sometimes cranky!) developer, I crave simplicity. But that's a topic for another post. Here I wanted to address a common question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why didn't you implement this in Rust?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a great question, considering I've been using Rust for a number of years now, and I generally advocate its use for its rich ecosystem, safety and tooling. I &lt;a href="https://github.com/tcdi/pgx/pulls?q=is%3Apr+author%3Ayrashk+" rel="noopener noreferrer"&gt;actively contribute&lt;/a&gt; to &lt;a href="https://github.com/tcdi/pgx" rel="noopener noreferrer"&gt;pgx&lt;/a&gt;, a library for building Postgres extensions in Rust. Yet, Omnigres appears to be all done in C.&lt;/p&gt;

&lt;p&gt;Here's why.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Beware: the list has a lot of subjective and controversial opinions.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making a good wrapper (pgx) is a lot of work
&lt;/h3&gt;

&lt;p&gt;While it's getting there, when you try to build something pushing the boundaries, pgx may not yet provide sufficient API coverage, so you have to resort to unsafe FFI. It drove me to make a number of contributions to it. Still, since it takes a while to refine them enough to be considered well-rounded, it impedes general development velocity.&lt;/p&gt;

&lt;p&gt;Making a good, safe wrapper for such a complex project as Postgres is a significant undertaking. It'll take however long it takes. That's why I am wholeheartedly supporting it and spending time evolving it.&lt;/p&gt;

&lt;p&gt;But as of today, I want to be able to move fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postgres is [quirky] C
&lt;/h3&gt;

&lt;p&gt;Its whole internal API is designed to be consumed by C code, and Postgres doesn't know anything about your other language.&lt;/p&gt;

&lt;p&gt;It uses &lt;code&gt;setjmp/longjmp&lt;/code&gt; for exceptions (so you'd have to roll your own guards to unwind the stack). It has its own memory management system. It manages the SPI stack. It has lots of mutable global variables!&lt;/p&gt;

&lt;p&gt;So, to consume it from C is only natural, even though it can be dangerous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;Between not really knowing what's inside of your dependencies and obnoxious compile times, there's a reason for having less dependencies.&lt;/p&gt;

&lt;p&gt;Rust's Cargo makes it terribly easy to add dependencies, so there's no tax on adding yet another library. This is generally seen as a good thing in the industry, but I keep coming back to the idea that less is better.&lt;/p&gt;

&lt;p&gt;If, however, you had to pay for every dependency, we'd use a lot less of them, only when necessary. C is excellent in this department! I gave up on a number of dependencies simply because I had difficulties integrating them into my build system. So I see this is a filter for the need &lt;em&gt;and&lt;/em&gt; the quality of the dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast compile times
&lt;/h3&gt;

&lt;p&gt;Rust compiler is notoriously slow. Pgx brings its own complexity that makes rapid iteration nearly impossible. My builds are extremely fast with C. It's a simple language, and I tend to add very few dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex language fatigue
&lt;/h3&gt;

&lt;p&gt;I've been developing software for a few decades now and am growing tired of complex languages. Going back to C feels like taking a vacation and stopping chasing ideals and just focusing on the problem at hand.&lt;/p&gt;

&lt;p&gt;If Postgres (or Linux kernel) can be done in C, I am sure a few extensions could, too!&lt;/p&gt;

&lt;h3&gt;
  
  
  Formal verification methods
&lt;/h3&gt;

&lt;p&gt;There are projects that are focusing on bringing this to Rust, but there are some great static analysis and formal verification tools that have been used in C for quite a while. I fancy &lt;a href="https://frama-c.com" rel="noopener noreferrer"&gt;Frama-C&lt;/a&gt; and try to add it &lt;a href="https://github.com/omnigres/omnigres/blob/a291b00172a5ce430a9536c21f4a78f2f98a0114/libgluepg_curl/libgluepg_curl.c#L46-L64" rel="noopener noreferrer"&gt;bit&lt;/a&gt; by bit to Omnigres where I think it may be necessary. &lt;/p&gt;

&lt;h3&gt;
  
  
  Rust safeties are slightly less relevant in Postgres
&lt;/h3&gt;

&lt;p&gt;Postgres is inherently &lt;strong&gt;single-threaded&lt;/strong&gt; and therefore, "fearless concurrency" is not buying us much. Sure, you can build an extension that'll communicate with other processes and threads over shared memory, but this is not much of a concern at the core database level.&lt;/p&gt;

&lt;p&gt;Postgres has its own &lt;strong&gt;memory management&lt;/strong&gt; story, which is quite good. Instead of having a single allocation pool, it has memory contexts that are established temporarily or for a scope. When they are deleted, all the memory allocated in them is released. So memory leaks are not necessarily a concern. Rust doesn't necessarily consider leaks a safety issue, but it's nice to know we will not crash the server with an OOM.&lt;/p&gt;

&lt;p&gt;Of course, there's still use-after-free, which you have to be careful about. This is where tools like AddressSanitizer can come in very handy.&lt;/p&gt;

&lt;p&gt;I've seen that most of my crashes in the extensions were coming from misusing some Postgres API, and I was getting a lot of them in Rust as well.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;But Yurii, isn't C a dangerous and flawed language?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, it is both dangerous and flawed. In general, I wouldn't build new projects in C, but after months of going back on forth on this, I think, ultimately, building a project on top of Postgres in C is reasonable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did you consider Zig?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I absolutely did. I am very interested in Zig (and have previously developed some small projects in it) and I keep track of its development. But I ultimately didn't want to fight both Postgres and Zig at the same time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did you consider Nim?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You bet! The attraction was that it compiles to C, and I can emit C code where necessary, obliterating the need to replicate certain tricks. But it's a complex, big language, and I am not confident about its own bugs, obscurities and upgrade stories (Nim 2 is coming!)&lt;/p&gt;




&lt;p&gt;Knowing how Rust crowd behaves sometimes, there may be those inclined to persuade me to change my opinion or prove me wrong. Of course, they are free to do so, but I'd prefer to focus on getting things done!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ultimately&lt;/strong&gt;, I think Rust is great. Omnigres will have first-class support for it and will promote its use and the use of &lt;a href="https://github.com/tcdi/pgx" rel="noopener noreferrer"&gt;pgx&lt;/a&gt; for building extensions.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>postgres</category>
      <category>c</category>
    </item>
  </channel>
</rss>
