<?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>DEV Community: Kirill Scherba</title>
    <description>The latest articles on DEV Community by Kirill Scherba (@kirillscherba).</description>
    <link>https://dev.to/kirillscherba</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F867900%2F4e51c00d-b2fc-4e40-b0a9-8f34e3a15ba0.jpeg</url>
      <title>DEV Community: Kirill Scherba</title>
      <link>https://dev.to/kirillscherba</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kirillscherba"/>
    <language>en</language>
    <item>
      <title>Why I Built a Go SQL Helper</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Fri, 12 Jun 2026 10:41:50 +0000</pubDate>
      <link>https://dev.to/kirillscherba/why-i-built-a-go-sql-helper-3ic4</link>
      <guid>https://dev.to/kirillscherba/why-i-built-a-go-sql-helper-3ic4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Zero-boilerplate SQL for Go. Define a struct with tags. That's it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you write Go and talk to SQL databases, you know the pain. Every CRUD operation requires writing a raw SQL string, carefully mapping columns to struct fields with &lt;code&gt;rows.Scan&lt;/code&gt;, manually wrapping writes in &lt;code&gt;Begin/Commit/Rollback&lt;/code&gt;, and keeping your schema definitions in sync with your code. It's tedious, error-prone, and the boilerplate never ends.&lt;/p&gt;

&lt;p&gt;This is the story of &lt;code&gt;sqlh&lt;/code&gt; — a library I built to eliminate all of that, while staying in the "sweet spot" between raw SQL (too much work) and heavy ORMs (too much magic).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Go + SQL = Death by a Thousand &lt;code&gt;rows.Scan&lt;/code&gt;s
&lt;/h2&gt;

&lt;p&gt;Go's &lt;code&gt;database/sql&lt;/code&gt; package is excellent. It gives you a solid, portable foundation for talking to any SQL database. But it intentionally leaves the hard work to you.&lt;/p&gt;

&lt;p&gt;Here's what a simple CRUD workflow looks like with raw &lt;code&gt;database/sql&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 1. Create table — raw DDL string&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`CREATE TABLE IF NOT EXISTS user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    email TEXT,
    age INTEGER
)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// 2. Insert — explicit placeholders and args&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"INSERT INTO user (name, email, age) VALUES (?, ?, ?)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// 3. Get by ID — QueryRow + manual Scan&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT id, name, email, age FROM user WHERE id = ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// 4. List all — Query + rows.Next + rows.Scan loop&lt;/span&gt;
&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT id, name, email, age FROM user ORDER BY name ASC"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;var&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;User&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&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;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;// 5. Update — raw SQL with placeholders&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"UPDATE user SET email = ?, age = ? WHERE id = ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"alice.new@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// 6. Delete — raw SQL&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DELETE FROM user WHERE id = ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is &lt;strong&gt;~115 lines of code&lt;/strong&gt; for six basic operations. And every time you add a column, you must update the &lt;code&gt;CREATE TABLE&lt;/code&gt; string, the &lt;code&gt;INSERT&lt;/code&gt; columns, the &lt;code&gt;SELECT&lt;/code&gt; columns, and the &lt;code&gt;rows.Scan&lt;/code&gt; call. One typo in any of those, and you get a runtime error — no compile-time safety.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pain Points
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pain&lt;/th&gt;
&lt;th&gt;Why it hurts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Manual SQL writing&lt;/td&gt;
&lt;td&gt;Every CRUD operation needs a raw SQL string — no compile-time check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rows.Scan&lt;/code&gt; verbosity&lt;/td&gt;
&lt;td&gt;4–5 lines per query result just to map columns to struct fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction boilerplate&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;db.Begin()&lt;/code&gt; + &lt;code&gt;defer tx.Rollback()&lt;/code&gt; + &lt;code&gt;tx.Commit()&lt;/code&gt; — repeated everywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No schema traceability&lt;/td&gt;
&lt;td&gt;Table DDL lives in migration files, structs in Go — they drift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error-prone column ordering&lt;/td&gt;
&lt;td&gt;Adding a column means updating SQL strings &lt;em&gt;and&lt;/em&gt; &lt;code&gt;Scan&lt;/code&gt; calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Existing Solutions: sqlx, GORM, and the Missing Middle
&lt;/h2&gt;

&lt;p&gt;The Go ecosystem offers two well-known paths to reduce this pain. Both have trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  sqlx: Better, But Still Manual
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jmoiron/sqlx" rel="noopener noreferrer"&gt;&lt;code&gt;sqlx&lt;/code&gt;&lt;/a&gt; is a popular enhancement over &lt;code&gt;database/sql&lt;/code&gt;. It adds &lt;code&gt;StructScan&lt;/code&gt;, &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;Select&lt;/code&gt;, and named query parameters. You still write raw SQL, but &lt;code&gt;rows.Scan&lt;/code&gt; is automated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// sqlx: still manual SQL, but StructScan eliminates Scan&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;dbx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SELECT id, name, email, age FROM user WHERE id = ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sqlx saves about &lt;strong&gt;30% of the boilerplate&lt;/strong&gt; (down to ~80 lines). But you still write every &lt;code&gt;CREATE TABLE&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; by hand. SQL generation is not its job.&lt;/p&gt;

&lt;h3&gt;
  
  
  GORM: Full ORM, Full Magic
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://gorm.io/" rel="noopener noreferrer"&gt;GORM&lt;/a&gt; is the heavyweight champion. It generates everything — schema, queries, migrations — and provides a rich chainable API. But it comes with a cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Heavy reflection overhead&lt;/strong&gt; at runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steep learning curve&lt;/strong&gt; — tags, hooks, scopes, associations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~4 MB binary size increase&lt;/strong&gt; just for the ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Magic that hides complexity&lt;/strong&gt; — until it doesn't, and you spend hours debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For large teams with dedicated DBAs and complex domain models, GORM is a solid choice. For CLI tools, startups, and small-to-medium services, it's overkill.&lt;/p&gt;

&lt;h3&gt;
  
  
  sqlh: The Sweet Spot
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;database/sql&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;sqlx&lt;/th&gt;
&lt;th&gt;GORM&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;sqlh&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL generation&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rows.Scan&lt;/code&gt; needed&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ &lt;code&gt;StructScan&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;❌ Auto&lt;/td&gt;
&lt;td&gt;❌ Auto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type-safe (generics)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-transactions&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lock retry&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary size overhead&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;~200 KB&lt;/td&gt;
&lt;td&gt;~4 MB&lt;/td&gt;
&lt;td&gt;~200 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;sqlh lives between sqlx and GORM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-boilerplate CRUD&lt;/strong&gt; — struct tags generate all SQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe via Go generics&lt;/strong&gt; — &lt;code&gt;Get[User]()&lt;/code&gt; returns &lt;code&gt;*User&lt;/code&gt;, not &lt;code&gt;interface{}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No magic&lt;/strong&gt; — what you see in the struct is what you get in the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt; — minimal reflection, cached metadata, no hidden complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How sqlh Works: Struct Tags as Single Source of Truth
&lt;/h2&gt;

&lt;p&gt;The core idea is simple: &lt;strong&gt;your Go struct is your schema&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;    &lt;span class="kt"&gt;int64&lt;/span&gt;  &lt;span class="s"&gt;`db:"id" db_key:"not null primary key autoincrement"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`db:"name" db_key:"unique"`&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`db:"email"`&lt;/span&gt;
    &lt;span class="n"&gt;Age&lt;/span&gt;   &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="s"&gt;`db:"age"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three struct tags control everything:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Column name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;db:"user_name"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;db_key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Constraints, indexes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;db_key:"primary key autoincrement"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;db_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQL type override&lt;/td&gt;
&lt;td&gt;&lt;code&gt;db_type:"TEXT"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From this single struct definition, sqlh generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CREATE TABLE&lt;/strong&gt; — &lt;code&gt;sqlh.Create[User](db)&lt;/code&gt; generates and executes &lt;code&gt;CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, email TEXT, age INTEGER)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INSERT&lt;/strong&gt; — &lt;code&gt;sqlh.Insert(db, User{Name: "Alice"})&lt;/code&gt; generates &lt;code&gt;INSERT INTO user (name, email, age) VALUES (?, ?, ?)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SELECT&lt;/strong&gt; — &lt;code&gt;sqlh.Get[User](db, ...)&lt;/code&gt; generates &lt;code&gt;SELECT id, name, email, age FROM user WHERE ... LIMIT 2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UPDATE&lt;/strong&gt; — &lt;code&gt;sqlh.Update(db, ...)&lt;/code&gt; generates &lt;code&gt;UPDATE user SET name=?, email=?, age=? WHERE ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DELETE&lt;/strong&gt; — &lt;code&gt;sqlh.Delete[User](db, ...)&lt;/code&gt; generates &lt;code&gt;DELETE FROM user WHERE ...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│  sqlh package                               │
│  Insert, Get, List, Update, Delete, Set,    │
│  Create — with auto-transactions            │
├─────────────────────────────────────────────┤
│  query package                              │
│  SQL generation, metadata cache, JOINs      │
├─────────────────────────────────────────────┤
│  database/sql (stdlib)                      │
│  Connection pool, raw query execution       │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Design Decisions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generics-first (Go 1.25+)&lt;/strong&gt; — &lt;code&gt;Get[User]()&lt;/code&gt; returns &lt;code&gt;*User&lt;/code&gt; with compile-time type safety. No &lt;code&gt;interface{}&lt;/code&gt;, no type assertions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reflection at call-time&lt;/strong&gt; — Struct metadata is parsed once and cached in a &lt;code&gt;sync.Map&lt;/code&gt; keyed by &lt;code&gt;reflect.Type&lt;/code&gt;. Subsequent calls reuse table names, field lists, and scan metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-transactions on writes&lt;/strong&gt; — Every &lt;code&gt;Insert&lt;/code&gt;, &lt;code&gt;Update&lt;/code&gt;, &lt;code&gt;Delete&lt;/code&gt;, and &lt;code&gt;Set&lt;/code&gt; is automatically wrapped in &lt;code&gt;BEGIN...COMMIT&lt;/code&gt; with &lt;code&gt;ROLLBACK&lt;/code&gt; on error. You never forget a transaction again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database lock retry&lt;/strong&gt; — SQLite "database is locked" errors are automatically retried up to 20 times with 100ms backoff. Production-grade resilience out of the box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-database support&lt;/strong&gt; — SQLite (primary), MySQL, PostgreSQL (both CI-tested), and SQL Server (experimental).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  CRUD in 50 Lines: The Quick Start
&lt;/h2&gt;

&lt;p&gt;Here's the complete CRUD workflow with sqlh. Same operations as the raw SQL example above — &lt;strong&gt;~57% less code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/kirill-scherba/sqlh"&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/mattn/go-sqlite3"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;    &lt;span class="kt"&gt;int64&lt;/span&gt;  &lt;span class="s"&gt;`db:"id" db_key:"not null primary key autoincrement"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`db:"name" db_key:"unique"`&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`db:"email"`&lt;/span&gt;
    &lt;span class="n"&gt;Age&lt;/span&gt;   &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="s"&gt;`db:"age"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sqlite3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file::memory:?cache=shared"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 1. Create table from struct&lt;/span&gt;
    &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Insert&lt;/span&gt;
    &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;User&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="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;bobID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsertId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;User&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="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Get by ID — returns *User, not interface{}&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bobID&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// "Bob"&lt;/span&gt;

    &lt;span class="c"&gt;// 4. List all — returns []User + next offset&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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name ASC"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&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="c"&gt;// 2&lt;/span&gt;

    &lt;span class="c"&gt;// 5. Update — pass full struct to avoid zeroing other columns&lt;/span&gt;
    &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdateAttr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;
        &lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;User&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="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"alice.new@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Wheres&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// 6. Delete&lt;/span&gt;
    &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bobID&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;&lt;strong&gt;~50 lines.&lt;/strong&gt; No raw SQL. No &lt;code&gt;rows.Scan&lt;/code&gt;. No transaction management. No column ordering errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Side-by-Side Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Raw &lt;code&gt;database/sql&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;sqlx&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;sqlh&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CREATE TABLE&lt;/td&gt;
&lt;td&gt;Raw SQL string&lt;/td&gt;
&lt;td&gt;Raw SQL string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlh.Create[User](db)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INSERT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Exec(?,?,?)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NamedExec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Insert(T)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;QueryRow + Scan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Get(&amp;amp;T)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Get[T](where)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LIST&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rows.Next + Scan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;List[T](...)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UPDATE&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Exec(?,?,?,?)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NamedExec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Update(attr)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Exec(?)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Exec(?)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Delete[T](where)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COUNT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;QueryRow + Scan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Get(&amp;amp;int)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Count[T]()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Lines of code&lt;/th&gt;
&lt;th&gt;Reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw &lt;code&gt;database/sql&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;~115&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlx&lt;/td&gt;
&lt;td&gt;~80&lt;/td&gt;
&lt;td&gt;−30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sqlh&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~50&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−57%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Benchmarks: The Numbers
&lt;/h2&gt;

&lt;p&gt;How fast is sqlh in practice? I created a standalone &lt;code&gt;bench/&lt;/code&gt; module comparing raw &lt;code&gt;database/sql&lt;/code&gt;, &lt;code&gt;sqlx&lt;/code&gt;, GORM, and sqlh on the same CRUD workload. All benchmarks use in-memory SQLite — zero external setup.&lt;/p&gt;

&lt;p&gt;Reproduce on your machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;bench &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-bench&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-benchmem&lt;/span&gt; &lt;span class="nt"&gt;-benchtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CRUD Throughput (ops/sec)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;raw sql&lt;/th&gt;
&lt;th&gt;sqlx&lt;/th&gt;
&lt;th&gt;GORM&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;sqlh&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Insert&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;158,856&lt;/td&gt;
&lt;td&gt;131,337&lt;/td&gt;
&lt;td&gt;35,288&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;85,631&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get by PK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;169,090&lt;/td&gt;
&lt;td&gt;150,082&lt;/td&gt;
&lt;td&gt;77,489&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;73,601&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List all&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;11,857&lt;/td&gt;
&lt;td&gt;9,076&lt;/td&gt;
&lt;td&gt;6,775&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,607&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List limit 10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;51,000&lt;/td&gt;
&lt;td&gt;43,381&lt;/td&gt;
&lt;td&gt;37,666&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;44,204&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;226,963&lt;/td&gt;
&lt;td&gt;177,242&lt;/td&gt;
&lt;td&gt;65,828&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;84,083&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;170,503&lt;/td&gt;
&lt;td&gt;163,185&lt;/td&gt;
&lt;td&gt;41,375&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;60,503&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Memory Allocations (bytes/op, allocs/op)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;raw sql&lt;/th&gt;
&lt;th&gt;sqlx&lt;/th&gt;
&lt;th&gt;GORM&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;sqlh&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Insert&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;328 B, 12&lt;/td&gt;
&lt;td&gt;721 B, 20&lt;/td&gt;
&lt;td&gt;5,536 B, 82&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,274 B, 39&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Get by PK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;792 B, 27&lt;/td&gt;
&lt;td&gt;976 B, 31&lt;/td&gt;
&lt;td&gt;3,952 B, 66&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,593 B, 78&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List all&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;23,744 B, 528&lt;/td&gt;
&lt;td&gt;26,376 B, 632&lt;/td&gt;
&lt;td&gt;27,669 B, 946&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26,394 B, 745&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3,120 B, 76&lt;/td&gt;
&lt;td&gt;3,624 B, 91&lt;/td&gt;
&lt;td&gt;6,145 B, 141&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3,958 B, 115&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;296 B, 10&lt;/td&gt;
&lt;td&gt;680 B, 19&lt;/td&gt;
&lt;td&gt;5,079 B, 68&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,393 B, 43&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;216 B, 7&lt;/td&gt;
&lt;td&gt;216 B, 7&lt;/td&gt;
&lt;td&gt;5,483 B, 67&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,139 B, 37&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What the Numbers Tell Us
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GORM&lt;/strong&gt; has the highest latency and allocation footprint across all operations, reflecting its rich feature set and internal reflection overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sqlh&lt;/strong&gt; sits between raw sql/sqlx and GORM. The moderate overhead comes from auto-generated SQL, struct tag parsing, and built-in transaction wrapping for writes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sqlh trades raw speed for correctness&lt;/strong&gt;: every write is auto-transacted with rollback on error, eliminating an entire class of bugs at the cost of ~2–6x latency versus raw sql for single-row mutations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ListAll&lt;/strong&gt; is dominated by the cost of scanning 100 rows. All libraries show similar performance here.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Environment:&lt;/strong&gt; Linux AMD Ryzen 9 3900, Go 1.26.3, SQLite in-memory.&lt;br&gt;
Run &lt;code&gt;cd bench &amp;amp;&amp;amp; go test -bench=. -benchmem -benchtime=1s&lt;/code&gt; on your own hardware for an apples-to-apples comparison.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  When to Use sqlh
&lt;/h2&gt;

&lt;p&gt;sqlh is not a silver bullet. Here's where it shines and where you might want something else:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLI tools &amp;amp; utilities&lt;/td&gt;
&lt;td&gt;✅ Perfect — zero migration files, single binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startups &amp;amp; MVPs&lt;/td&gt;
&lt;td&gt;✅ Ship faster, refactor later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microservices with simple schemas&lt;/td&gt;
&lt;td&gt;✅ Low overhead, type-safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-throughput OLTP (&amp;gt;100K writes/sec)&lt;/td&gt;
&lt;td&gt;⚠️ Test first — raw sql may be needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex multi-table analytics&lt;/td&gt;
&lt;td&gt;⚠️ Prefer raw SQL or a query builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large teams with dedicated DBAs&lt;/td&gt;
&lt;td&gt;⚠️ GORM or sqlx may fit better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning Go + SQL&lt;/td&gt;
&lt;td&gt;✅ Great teaching tool — low cognitive load&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/kirill-scherba/sqlh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;📖 &lt;a href="https://github.com/kirill-scherba/sqlh" rel="noopener noreferrer"&gt;README &amp;amp; Quick Start&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;a href="https://pkg.go.dev/github.com/kirill-scherba/sqlh" rel="noopener noreferrer"&gt;pkg.go.dev reference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🏗️ &lt;a href="https://github.com/kirill-scherba/sqlh" rel="noopener noreferrer"&gt;Source code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;sqlh is actively developed. As of v0.8.0 (June 2026), the library supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Full CRUD with auto-transactions&lt;/li&gt;
&lt;li&gt;✅ Native UPSERT (PostgreSQL, SQLite, MySQL)&lt;/li&gt;
&lt;li&gt;✅ JOIN queries with composite struct scanning&lt;/li&gt;
&lt;li&gt;✅ Go 1.25 iterators (&lt;code&gt;ListRange&lt;/code&gt;) for lazy streaming&lt;/li&gt;
&lt;li&gt;✅ Type-safe WHERE helpers (&lt;code&gt;Eq&lt;/code&gt;, &lt;code&gt;Ne&lt;/code&gt;, &lt;code&gt;Gt&lt;/code&gt;, &lt;code&gt;Like&lt;/code&gt;, &lt;code&gt;In&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;✅ Database lock retry for SQLite&lt;/li&gt;
&lt;li&gt;✅ Multi-database support (SQLite, MySQL, PostgreSQL)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the roadmap: aggregate functions (&lt;code&gt;SUM&lt;/code&gt;, &lt;code&gt;AVG&lt;/code&gt;), schema migrations, batch operations, and transactional reads. The API is stabilizing toward v1.0.0.&lt;/p&gt;

&lt;p&gt;If you’re building a Go project that talks to SQL and you’re tired of writing the same boilerplate over and over — give sqlh a try. Define your struct. That’s it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by &lt;a href="https://github.com/kirill-scherba" rel="noopener noreferrer"&gt;Kirill Scherba&lt;/a&gt;. sqlh is open source under the BSD license. Contributions welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>sql</category>
      <category>database</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Ordered map</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Sat, 15 Feb 2025 11:51:43 +0000</pubDate>
      <link>https://dev.to/kirillscherba/ordered-map-13op</link>
      <guid>https://dev.to/kirillscherba/ordered-map-13op</guid>
      <description>&lt;p&gt;Omap is Golang package for working with thread safe ordered maps. The ordered map contains the golang map, list and mutex to execute Ordered Map functions.&lt;/p&gt;

&lt;p&gt;The Ordered Map is a map that remembers the order of items. The map can be iterated over to retrieve the items in the order they were added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to the omap Go Package
&lt;/h2&gt;

&lt;p&gt;The omap Go package is a lightweight and efficient library for working with ordered maps in Go. An ordered map is a data structure that combines the benefits of a map and a list, allowing you to store key-value pairs in a specific order.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is omap?
&lt;/h2&gt;

&lt;p&gt;Omap is a Go package that provides an implementation of an ordered map. It is designed to be fast, efficient, and easy to use. Omap is particularly useful when you need to store data in a specific order, such as when working with configuration files, caching, or data processing pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of omap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ordered: omap preserves the order in which key-value pairs are inserted, allowing you to iterate over the map in a specific order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fast lookups: omap uses a hash table to store key-value pairs, making lookups fast and efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Efficient insertion and deletion: omap uses a linked list to store the order of key-value pairs, making insertion and deletion operations efficient.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using omap
&lt;/h2&gt;

&lt;p&gt;To use omap, you can install it using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/kirill-scherba/omap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of how to use omap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/kirill-scherba/omap"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a new ordered map&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;omap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Insert some key-value pairs&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"value3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Iterate over the omap in order&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pairs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code creates a new omap, inserts some key-value pairs, and then iterates over the omap in order, printing out each key-value pair.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The omap Go package is a useful library for working with ordered maps in Go. Its fast lookups, efficient insertion and deletion, and ordered iteration make it a great choice for a variety of use cases. Whether you're working with configuration files, caching, or data processing pipelines, omap is definitely worth considering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Configuration files: Use omap to store configuration data in a specific order, making it easy to iterate over the configuration and apply settings in the correct order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Caching: Use omap to store cached data in a specific order, making it easy to iterate over the cache and evict items in the correct order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data processing pipelines: Use omap to store data in a specific order, making it easy to iterate over the data and process it in the correct order.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See more description and examples on omap projects github page:&lt;br&gt;
&lt;a href="https://github.com/kirill-scherba/omap/" rel="noopener noreferrer"&gt;https://github.com/kirill-scherba/omap/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>map</category>
      <category>ordered</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Teonet messages queue</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Thu, 21 Mar 2024 07:29:45 +0000</pubDate>
      <link>https://dev.to/kirillscherba/teonet-messages-queue-391d</link>
      <guid>https://dev.to/kirillscherba/teonet-messages-queue-391d</guid>
      <description>&lt;p&gt;The Teonet messages queue is a part of the Teonet network.&lt;/p&gt;

&lt;p&gt;A message queue is a form of asynchronous service-to-service communication used in serverless and microservices architectures. Messages are stored on the queue until they are processed and deleted. Each message is processed only once, by a single consumer. Message queues can be used to decouple heavyweight processing, to buffer or batch work, and to smooth spiky workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic teomq scheme
&lt;/h2&gt;

&lt;p&gt;The Teonet messages queue contains three basic parts:&lt;/p&gt;

&lt;p&gt;Teonet messages broker&lt;br&gt;
Teonet messages consumer&lt;br&gt;
Teonet messages producer&lt;/p&gt;

&lt;p&gt;See more at: &lt;a href="https://github.com/teonet-go/teomq"&gt;https://github.com/teonet-go/teomq&lt;/a&gt;&lt;/p&gt;

</description>
      <category>teonet</category>
      <category>messagequeue</category>
      <category>go</category>
      <category>broker</category>
    </item>
    <item>
      <title>Teonet messages queue</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Thu, 21 Mar 2024 07:29:41 +0000</pubDate>
      <link>https://dev.to/kirillscherba/teonet-messages-queue-3p69</link>
      <guid>https://dev.to/kirillscherba/teonet-messages-queue-3p69</guid>
      <description>&lt;p&gt;The Teonet messages queue is a part of the Teonet network.&lt;/p&gt;

&lt;p&gt;A message queue is a form of asynchronous service-to-service communication used in serverless and microservices architectures. Messages are stored on the queue until they are processed and deleted. Each message is processed only once, by a single consumer. Message queues can be used to decouple heavyweight processing, to buffer or batch work, and to smooth spiky workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic teomq scheme
&lt;/h2&gt;

&lt;p&gt;The Teonet messages queue contains three basic parts:&lt;/p&gt;

&lt;p&gt;Teonet messages broker&lt;br&gt;
Teonet messages consumer&lt;br&gt;
Teonet messages producer&lt;/p&gt;

&lt;p&gt;See more at: &lt;a href="https://github.com/teonet-go/teomq"&gt;https://github.com/teonet-go/teomq&lt;/a&gt;&lt;/p&gt;

</description>
      <category>teonet</category>
      <category>messagequeue</category>
      <category>go</category>
      <category>broker</category>
    </item>
    <item>
      <title>Golang multinode tree with parallel search</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Wed, 06 Sep 2023 09:10:26 +0000</pubDate>
      <link>https://dev.to/kirillscherba/golang-multinode-tree-with-parallel-search-4kcp</link>
      <guid>https://dev.to/kirillscherba/golang-multinode-tree-with-parallel-search-4kcp</guid>
      <description>&lt;p&gt;Golang package tree is pure golang implementation of tree with any direction nodes. Each node in the tree can be connected to many children and has many parents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7makvco5vskgc3h54ex7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7makvco5vskgc3h54ex7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the implementation of this tree, parents are no different from children. The tree element contains references to branches without specifying whether it is a parent or a child.&lt;/p&gt;

&lt;p&gt;Each node reference in an element contains a path cost that is taken into account when calculating the path from point A to point B. The path between points A and B calculates using threads (parallel) to minimize time of find all paths.&lt;/p&gt;

&lt;p&gt;This tree has no root. Paths in a tree can be looped.&lt;/p&gt;

&lt;p&gt;The tree elements can store any data structure. This structure is defined when the tree is created.&lt;/p&gt;

&lt;p&gt;See the part of code to create Tree and it Elements:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TreeData&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="s"&gt;"My first element"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Create new tree elements (first element and end point element)&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My first element"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ep&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"End point"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Create children of e (first element) element&lt;/span&gt;
&lt;span class="n"&gt;ch1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My first child"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;ch2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My second child"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WayOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Cost&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;ch4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My fourth child"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WayOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Cost&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c"&gt;// Create sub children elements&lt;/span&gt;
&lt;span class="n"&gt;ch3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ch2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Some third child"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;ch3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Add children to ep (end point) element&lt;/span&gt;
&lt;span class="n"&gt;ch1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ch2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ch4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Set oneway path from ep (end point) to e (first element) element&lt;/span&gt;
&lt;span class="n"&gt;ep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WayOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Cost&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Oneway&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c"&gt;// Print tree started from e (first element) element&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Print tree:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;%v&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The result of last printf:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;.&lt;/span&gt; My first element
├── My first child, cost: 1.00
│   ├── My fourth child, cost: 1.00
│   │   ├── My first element, cost: 3.00 🡡
│   │   ├── Some third child, cost: 1.00
│   │   │   ├── My second child, cost: 1.00
│   │   │   │   ├── My first element, cost: 3.00 🡡
│   │   │   │   ├── Some third child, cost: 1.00 🡡
│   │   │   │   └── End point, cost: 1.00
│   │   │   │       ├── My second child, cost: 1.00 🡡
│   │   │   │       ├── My fourth child, cost: 1.00 🡡
│   │   │   │       └── My first element, cost: 5.00 &lt;span class="o"&gt;(&lt;/span&gt;one way road&lt;span class="o"&gt;)&lt;/span&gt; 🡡
│   │   │   └── My fourth child, cost: 1.00 🡡
│   │   ├── My first child, cost: 1.00 🡡
│   │   └── End point, cost: 1.00 🡡
│   └── My first element, cost: 1.00 🡡
├── My second child, cost: 3.00 🡡
├── My fourth child, cost: 3.00 🡡
└── End point, cost: 5.00 &lt;span class="o"&gt;(&lt;/span&gt;way not allowed&lt;span class="o"&gt;)&lt;/span&gt; 🡡


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;See the code and more descriptions and example at: &lt;a href="https://github.com/kirill-scherba/tree" rel="noopener noreferrer"&gt;https://github.com/kirill-scherba/tree&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>tree</category>
      <category>parallel</category>
      <category>tread</category>
    </item>
    <item>
      <title>Connect to Tru peers using unix socket</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Tue, 22 Aug 2023 12:01:38 +0000</pubDate>
      <link>https://dev.to/kirillscherba/connect-to-tru-peers-using-unix-socket-f2d</link>
      <guid>https://dev.to/kirillscherba/connect-to-tru-peers-using-unix-socket-f2d</guid>
      <description>&lt;p&gt;If you can't link the &lt;a href="https://github.com/teonet-go/tru"&gt;Tru&lt;/a&gt; package to your application than use this standalone unix socket server to communicate with any Tru peers.&lt;/p&gt;

&lt;p&gt;The Unix socket Server created on golang and can run under Linux or Windows. &lt;/p&gt;

&lt;p&gt;The Unix socket Cliens examples created on Go, C, C++ and build-in sample Visual Studio project.&lt;/p&gt;

&lt;p&gt;See the part of C++ code to connect to the Unix socket Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Get unix socket path&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;socket_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tmp/trugw.sock"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Get tru peer address&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tru_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;":7070"&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="n"&gt;argc&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tru_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Trugw C++ client, "&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"sock path: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;socket_path&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"tru peer: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;socket_path&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Connect to teogw server&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"trying to connect...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Trugw&lt;/span&gt; &lt;span class="nf"&gt;tgw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tru_addr&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;tgw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"can't connect&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&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="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"connected &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Send messages&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"send "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;tgw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tgw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&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="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"receive "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;See the code and more descriptions and example at: &lt;a href="https://github.com/teonet-go/trugw"&gt;https://github.com/teonet-go/trugw&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>unix</category>
      <category>socket</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Tru go net example</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Sun, 30 Apr 2023 11:35:17 +0000</pubDate>
      <link>https://dev.to/kirillscherba/tru-net-example-2g1h</link>
      <guid>https://dev.to/kirillscherba/tru-net-example-2g1h</guid>
      <description>&lt;p&gt;New version of Tru (Teonet reliable UDP) golang package was releaset.&lt;/p&gt;

&lt;p&gt;The Tru golang net module and example was added. This example is Client/Server application which transfer data using golang net standard functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/teonet-go/tru"&gt;https://github.com/teonet-go/tru&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>tru</category>
      <category>reliable</category>
      <category>udp</category>
    </item>
    <item>
      <title>TeoS3 package and s3cp utility</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Fri, 04 Nov 2022 21:34:51 +0000</pubDate>
      <link>https://dev.to/kirillscherba/teos3-package-and-s3cp-utilite-5e02</link>
      <guid>https://dev.to/kirillscherba/teos3-package-and-s3cp-utilite-5e02</guid>
      <description>&lt;p&gt;The TeoS3 package contains Golang features that make it easy to use S3 storage as a key-value database.&lt;/p&gt;

&lt;p&gt;This project contain also the s3cp utility which copy files from disk to s3 storage and back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/teonet-go/teos3"&gt;https://github.com/teonet-go/teos3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a part of code with connect, set and get key value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Connect to S3 storage&lt;/span&gt;
&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;teos3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Set key to teos3 Map&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Get key from TeoS3 Map&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;See the code and more descriptions and example at: &lt;a href="https://github.com/teonet-go/teos3"&gt;https://github.com/teonet-go/teos3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>s3</category>
    </item>
    <item>
      <title>Teonet C library</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Wed, 08 Jun 2022 15:47:49 +0000</pubDate>
      <link>https://dev.to/kirillscherba/teonet-c-library-2gn7</link>
      <guid>https://dev.to/kirillscherba/teonet-c-library-2gn7</guid>
      <description>&lt;p&gt;Teonet news.&lt;/p&gt;

&lt;p&gt;Today we published &lt;a href="https://github.com/teonet-go/teonet-c"&gt;Teonet v5 C library&lt;/a&gt; and examples project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2_zRDQYn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j7appm1nsdjbd5cin7w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2_zRDQYn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j7appm1nsdjbd5cin7w.jpg" alt="Image description" width="750" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/teonet-go/teonet"&gt;Teonet&lt;/a&gt; is designed to create client-server systems and build networks for server applications operating within a microservice architecture.&lt;/p&gt;

&lt;p&gt;Main Teonet v5 code was created on Golang. This project make c/c++ dynamic library to use it in any C/C++ and C compatible applications.&lt;/p&gt;

&lt;p&gt;The description of Teonet you can find at main &lt;a href="https://github.com/teonet-go"&gt;Teonet-go&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;The examples of this project copy Teonet-go examples functional. So look this project examples to get started with teonet-c library: &lt;a href="https://github.com/teonet-go/teonet-c/tree/main/cmd"&gt;examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the project code and description on Github:&lt;br&gt;
&lt;a href="https://github.com/teonet-go/teonet-c"&gt;https://github.com/teonet-go/teonet-c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are waiting your support! Please give stars to our &lt;a href="https://github.com/teonet-go/teonet-c"&gt;Teonet-c&lt;/a&gt; and &lt;a href="https://github.com/teonet-go/teonet"&gt;Teonet&lt;/a&gt; projects in Github.&lt;/p&gt;

&lt;p&gt;Best regards,&lt;br&gt;
Kirill Scherba&lt;br&gt;
&lt;a href="https://github.com/kirill-scherba"&gt;https://github.com/kirill-scherba&lt;/a&gt;&lt;/p&gt;

</description>
      <category>teonet</category>
      <category>microservices</category>
      <category>clientserver</category>
      <category>go</category>
    </item>
    <item>
      <title>Tunnel between hosts without public IPs</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Sun, 05 Jun 2022 17:26:28 +0000</pubDate>
      <link>https://dev.to/kirillscherba/tunnel-between-hosts-without-public-ips-443a</link>
      <guid>https://dev.to/kirillscherba/tunnel-between-hosts-without-public-ips-443a</guid>
      <description>&lt;p&gt;There is Teonet news.&lt;/p&gt;

&lt;p&gt;The Teotun packet and application was published today by &lt;a href="https://github.com/teonet-go"&gt;Teonet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Teotun creates secret tunnel between hosts without public IPs using Teonet. The connection based on &lt;a href="https://github.com/teonet-go/tru"&gt;TRU&lt;/a&gt; transport and create reliable, low latency, encrypted P2P channels between connected peers.&lt;/p&gt;

&lt;p&gt;See the project code and description on Github:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/teonet-go/teotun"&gt;https://github.com/teonet-go/teotun&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are waiting your support! Please give stars to our &lt;a href="https://github.com/teonet-go/teotun"&gt;Teotun&lt;/a&gt; and &lt;a href="https://github.com/teonet-go/teonet"&gt;Teonet&lt;/a&gt; projects in Github.&lt;/p&gt;

&lt;p&gt;Best regards,&lt;br&gt;
Kirill Scherba&lt;br&gt;
&lt;a href="https://github.com/kirill-scherba"&gt;https://github.com/kirill-scherba&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>tunnel</category>
      <category>vpn</category>
      <category>p2p</category>
    </item>
    <item>
      <title>Create secret tunnel using Go</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Mon, 30 May 2022 13:25:14 +0000</pubDate>
      <link>https://dev.to/kirillscherba/create-secret-tunnel-using-go-gp8</link>
      <guid>https://dev.to/kirillscherba/create-secret-tunnel-using-go-gp8</guid>
      <description>&lt;h2&gt;
  
  
  Trutun
&lt;/h2&gt;

&lt;p&gt;Look at my new application &lt;a href="https://github.com/teonet-go/trutun"&gt;Trutun&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw8shuqpn0autoaiy70v.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw8shuqpn0autoaiy70v.jpeg" alt="Trutun" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It creates secret tunnel by IP address using &lt;a href="https://github.com/teonet-go/teonet"&gt;Teonet&lt;/a&gt; &lt;a href="https://github.com/teonet-go/tru"&gt;TRU&lt;/a&gt; transport. &lt;a href="https://github.com/teonet-go/tru"&gt;TRU&lt;/a&gt; create reliable, low latency and encrypted channel between connected peers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/teonet-go/trutun"&gt;Star&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository is available on Github: &lt;br&gt;
&lt;a href="https://github.com/teonet-go/trutun"&gt;https://github.com/teonet-go/trutun&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>udp</category>
      <category>networking</category>
      <category>tunnelling</category>
    </item>
    <item>
      <title>Using a neural network in Go</title>
      <dc:creator>Kirill Scherba</dc:creator>
      <pubDate>Wed, 25 May 2022 20:37:47 +0000</pubDate>
      <link>https://dev.to/kirillscherba/using-a-neural-network-in-go-4ega</link>
      <guid>https://dev.to/kirillscherba/using-a-neural-network-in-go-4ega</guid>
      <description>&lt;h2&gt;
  
  
  NNHelper is a Go package for creating and using a neural network
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funv7ad995nng6wsgmv3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funv7ad995nng6wsgmv3l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article describes the work of the &lt;a href="https://github.com/kirill-scherba/nnhelper" rel="noopener noreferrer"&gt;nnhelper&lt;/a&gt; package, designed to create and use neural networks in Go programs.&lt;/p&gt;

&lt;p&gt;If you are already familiar with machine learning and use it in your work, then this article and the examples described in it may seem too simple for you. If you are at the beginning of your journey and want to get acquainted with this topic or would like to learn how to use the neural network in your Go programs, then you have come to the right place.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/kirill-scherba/nnhelper" rel="noopener noreferrer"&gt;nnhelper&lt;/a&gt; Go package is designed to quickly create a neural network and use it in the applications written in the Go language. To use &lt;a href="https://github.com/kirill-scherba/nnhelper" rel="noopener noreferrer"&gt;nnhelper&lt;/a&gt;, you don't need anything else other than Go. The &lt;a href="https://github.com/kirill-scherba/nnhelper" rel="noopener noreferrer"&gt;nnhelper&lt;/a&gt; package is an add-on to the &lt;a href="https://github.com/fxsjy/gonn" rel="noopener noreferrer"&gt;gonn&lt;/a&gt; package. And this is the only one external dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Neural network (neural matrix)
&lt;/h2&gt;

&lt;p&gt;Let me try to explain in my own words what a neural network or neural matrix is, in order not to copy here complex and, perhaps, incomprehensible explanations from the initial reading.&lt;/p&gt;

&lt;p&gt;A neural matrix is an array of floating point values that allows you to give the desired output values depending on the input values. The values known to us are fed to the input of the neural matrix, and the results we expect are given at the output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6ngzyv1kzl4aorplm70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6ngzyv1kzl4aorplm70.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's use the neural matrix to solve the problem "Two Minuses Make a Plus." To do this, we will make two tables: one with the input data, the other with the results.&lt;/p&gt;

&lt;p&gt;We have two input parameters and two output parameters. The input is two numbers with a plus or minus sign. At the output we get two numbers with values from 0 to 1. If the first value is close to 1, then the result is Plus, if the second value is close to 1, then the result is Minus. That is, we give real values as input, and we get an array of results as output. Than we select the largest value from the array and consider it the answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input data:              Results(plus, minus):
 1, 1  – plus * plus     1, 0 – plus
 1,-1  – plus * minus    0, 1 – minus
-1, 1  – minus* plus     0, 1 – minus
-1,-1  – minus* minus    1, 0 – plus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the prepared tables with input and output data, a neural matrix is created by its training: The input data is fed to the inputs of the matrix. The matrix produces an output that is compared with the result from our results table. Based on these answers, the coefficients in the matrix change. The input data is looped through hundreds, thousands and even hundreds of thousands of iterations until the matrix answers are accurate enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's move on to programming
&lt;/h2&gt;

&lt;p&gt;Enough words and theory. Let's see how it looks in practice.&lt;/p&gt;

&lt;p&gt;Let's create a folder for the project. And create three files in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main.go 
sam03_inp.csv 
sam03_tar.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's place our input and output data in the *.csv files:&lt;/p&gt;

&lt;p&gt;sam03_inp.csv&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,1
1,-1
-1,1
-1,-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sam03_tar.csv&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,0
0,1
0,1
1,0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the main.go file, create the main function and write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Constants with filenames of our matrix and data&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;SAM03_NN&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sam03.nn"&lt;/span&gt;
    &lt;span class="n"&gt;SAM03_INP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sam03_inp.csv"&lt;/span&gt;
    &lt;span class="n"&gt;SAM03_TAR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sam03_tar.csv"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Human answers string array&lt;/span&gt;
&lt;span class="n"&gt;humanAnswers&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Plus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Minus"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Create NN if it file does not exists&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAM03_NN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotExist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAM03_NN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"neural network"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;nnhelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAM03_INP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAM03_TAR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAM03_NN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Load neural matrix from file&lt;/span&gt;
&lt;span class="n"&gt;nn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;nnhelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAM03_NN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Using / testing our neural network&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;PLUS&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;
        &lt;span class="n"&gt;MINUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Intput array for testing&lt;/span&gt;
    &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PLUS&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c"&gt;// Plus * Plus = Plus&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c"&gt;// Plus * Minus = Minus&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MINUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PLUS&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c"&gt;// Minus * Plus = Minus&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MINUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c"&gt;// Minus * Minus = Plus&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c"&gt;// Minus * Plus = Minus&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnswerToHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;humanAnswers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full text of this example and the data files are located in &lt;a href="https://github.com/kirill-scherba/nnhelper/blob/main/examples/sam03" rel="noopener noreferrer"&gt;examples/sam03&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's run the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we get the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1 1] Plus [0.9944239772210877 0.005449692189449571]
[1 -1] Minus [0.006860785779850435 0.9935960167863507]
[-1 1] Minus [0.005651009980489101 0.994384581174021]
[-1 -1] Plus [0.9944591181959666 0.005221796400203198]
[3000 -0.001] Minus [0.005445102841471242 0.9960123783099599]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the results we see (see the first line):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;our original data: [1,1]&lt;/li&gt;
&lt;li&gt;result translated into understandable form: Plus&lt;/li&gt;
&lt;li&gt;result obtained from matrix outputs: [0.9944239772210877 0.005449692189449571]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pay attention to the last line of the results. Our matrix was able to give the correct answer to the "unknown" inputs. This is the whole taste of neural networks. The matrix gives answers not only to the input data that it was trained on, but also to other input parameters unknown to it.&lt;/p&gt;

&lt;p&gt;To see the training process of the neural network, you need to delete the sam03.nn file and run the example again.&lt;/p&gt;

&lt;p&gt;Well, I guess I'll wrap it up here. For a long time I dreamed of writing a clear and simple explanation of how neural networks can be used in programming. I hope I succeeded.&lt;/p&gt;

&lt;p&gt;There are two more examples in the package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;sam02&lt;/em&gt;: Input the time in 24-hour format and get the answer: Morning, Evening, Day or Night;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;sam01&lt;/em&gt;: An example of a matrix for getting the reaction of a game bot. The input is the amount of health, the presence of weapons, the number of enemies, and at the output we get the answer to the question “what to do”: attack, sneak, run away or do nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The package is hosted on Github:&lt;br&gt;
&lt;a href="https://github.com/kirill-scherba/nnhelper" rel="noopener noreferrer"&gt;https://github.com/kirill-scherba/nnhelper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Best regards,&lt;br&gt;
Kirill Scherba&lt;/p&gt;

</description>
      <category>go</category>
      <category>neuralnetwork</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
