<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://www.whysthatso.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.whysthatso.net/" rel="alternate" type="text/html" /><updated>2025-12-23T16:22:41+00:00</updated><id>https://www.whysthatso.net/feed.xml</id><title type="html">whysthatso</title><subtitle>this is a blog</subtitle><author><name>Andreas Wagner</name></author><entry><title type="html">Differentiate Between Submit Buttons Rails</title><link href="https://www.whysthatso.net/today-i-learned/differentiate-between-submit-buttons-rails/" rel="alternate" type="text/html" title="Differentiate Between Submit Buttons Rails" /><published>2025-07-05T00:00:00+00:00</published><updated>2025-07-05T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/differentiate-between-submit-buttons-rails</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/differentiate-between-submit-buttons-rails/"><![CDATA[<p>insted of the form.submit, use button_tag, which will POST and also &lt;%= button_tag “Pay now”, {class: “primary-button”, name: “submit”, value: “foo”} %&gt;</p>

<p>which will result in a params hash { “submit” =&gt; “foo”} making params more easily handleable and keeps the button label uncommitted to the value.</p>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><summary type="html"><![CDATA[insted of the form.submit, use button_tag, which will POST and also &lt;%= button_tag “Pay now”, {class: “primary-button”, name: “submit”, value: “foo”} %&gt;]]></summary></entry><entry><title type="html">Problems With Search_path In Postgres 16</title><link href="https://www.whysthatso.net/today-i-learned/problems-with-search-path-in-postgres-16/" rel="alternate" type="text/html" title="Problems With Search_path In Postgres 16" /><published>2025-07-03T00:00:00+00:00</published><updated>2025-07-03T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/problems-with-search_path-in-postgres-16</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/problems-with-search-path-in-postgres-16/"><![CDATA[<p>I discovered that for some reason default search paths in some of my postgres 16 schemas were way off.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">r</span><span class="p">.</span><span class="n">rolname</span><span class="p">,</span> <span class="n">d</span><span class="p">.</span><span class="n">datname</span><span class="p">,</span> <span class="n">rs</span><span class="p">.</span><span class="n">setconfig</span>
<span class="k">FROM</span>   <span class="n">pg_db_role_setting</span> <span class="n">rs</span>
<span class="k">LEFT</span>   <span class="k">JOIN</span> <span class="n">pg_roles</span>      <span class="n">r</span> <span class="k">ON</span> <span class="n">r</span><span class="p">.</span><span class="n">oid</span> <span class="o">=</span> <span class="n">rs</span><span class="p">.</span><span class="n">setrole</span>
<span class="k">LEFT</span>   <span class="k">JOIN</span> <span class="n">pg_database</span>   <span class="n">d</span> <span class="k">ON</span> <span class="n">d</span><span class="p">.</span><span class="n">oid</span> <span class="o">=</span> <span class="n">rs</span><span class="p">.</span><span class="n">setdatabase</span>
<span class="k">WHERE</span>  <span class="n">r</span><span class="p">.</span><span class="n">rolname</span> <span class="o">=</span> <span class="s1">'immich'</span> <span class="k">OR</span> <span class="n">d</span><span class="p">.</span><span class="n">datname</span> <span class="o">=</span> <span class="s1">'immich'</span><span class="p">;</span>
</code></pre></div></div>

<p>this shows the defaults in the current schema, here it was immich db and user, but for some reason it had mattermost as a result.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">alter</span> <span class="k">database</span> <span class="n">immich</span> <span class="k">set</span> <span class="n">search_path</span> <span class="o">=</span> <span class="nv">"$user"</span><span class="p">,</span> <span class="k">public</span> <span class="p">;</span>

</code></pre></div></div>

<p>fixed this</p>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><summary type="html"><![CDATA[I discovered that for some reason default search paths in some of my postgres 16 schemas were way off.]]></summary></entry><entry><title type="html">Postgresql Performing Cache Lookup Failed For Relation</title><link href="https://www.whysthatso.net/today-i-learned/postgresql-performing-cache-lookup-failed-for-relation/" rel="alternate" type="text/html" title="Postgresql Performing Cache Lookup Failed For Relation" /><published>2025-07-02T00:00:00+00:00</published><updated>2025-07-02T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/postgresql-performing-cache-lookup-failed-for-relation</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/postgresql-performing-cache-lookup-failed-for-relation/"><![CDATA[<p>I had a weird state of a table where dropping it would return the error ‘performing cache lookup failed for relation <SOME_OID>'</SOME_OID></p>

<p>what fixed it was looking up the oid and then just removing all its entries.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> <span class="k">from</span> <span class="n">pg_depend</span> <span class="k">where</span> <span class="n">objid</span> <span class="o">=</span> <span class="n">MY_OID</span><span class="p">;</span>
</code></pre></div></div>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">delete</span> <span class="k">from</span> <span class="n">pg_class</span> <span class="k">where</span> <span class="n">oid</span> <span class="o">=</span> <span class="n">MY_OID</span><span class="p">;</span>
<span class="k">delete</span> <span class="k">from</span> <span class="n">pg_depend</span> <span class="k">where</span> <span class="n">objid</span> <span class="o">=</span> <span class="n">MY_OID</span><span class="p">;</span>
<span class="k">delete</span> <span class="k">from</span> <span class="n">pg_constraint</span> <span class="k">where</span> <span class="n">conrelid</span> <span class="o">=</span> <span class="n">MY_OID</span><span class="p">;</span>
</code></pre></div></div>

<p>might be very invasive, the state of that dataset was not too important.</p>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><summary type="html"><![CDATA[I had a weird state of a table where dropping it would return the error ‘performing cache lookup failed for relation ']]></summary></entry><entry><title type="html">Running JavaScript after a Turbo Stream renders</title><link href="https://www.whysthatso.net/today-i-learned/rails-turbo-after-stream-render/" rel="alternate" type="text/html" title="Running JavaScript after a Turbo Stream renders" /><published>2025-05-11T00:00:00+00:00</published><updated>2025-05-11T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/rails-turbo-after-stream-render</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/rails-turbo-after-stream-render/"><![CDATA[<p>Turbo comes with <code class="language-plaintext highlighter-rouge">turbo:before-stream-render</code> but unfortunately doesn’t ship with the equivalent <code class="language-plaintext highlighter-rouge">turbo:after-stream-render</code>. Here’s how to run JavaScript after the stream renders.</p>

<h2 id="why-we-need-this">Why we need this</h2>

<p>If you are building your application with Hotwire, your Turbo streams will likely add, remove, and replace some HTML nodes. This mostly works except when you want to add HTML that comes with some JavaScript. Like a file picker, Trix editor, and the like.</p>

<p>Turbo itself won’t do anything about this. It’s a rather simple tool with simple purpose. JavaScript initialization should come with the HTML Turbo is about to add. Hotwire solves this with Stimulus.</p>

<h2 id="the-hotwire-way">The Hotwire way</h2>

<p>The Hotwire answer to the problem is Stimulus controllers. Stimulus is build on top of MutationObserver which is a browser API providing the ability to watch for changes being made to the DOM.</p>

<p>When a Stimulus controller appears on the page, its <code class="language-plaintext highlighter-rouge">connect</code> method is called automatically. If our HTML doesn’t come with a Stimulus controller, we should create a new controller and put our initialization code inside its <code class="language-plaintext highlighter-rouge">connect</code> method:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app/javascript/controllers/my_controller.js</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Controller</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@hotwired/stimulus</span><span class="dl">"</span>

<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nc">extends</span> <span class="nx">Controller</span> <span class="p">{</span>
  <span class="nf">connect</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// Code to initialize something</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then we simply add the controller to an element inside our Turbo Stream:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;turbo-stream&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-controller=</span><span class="s">"my-controller"</span><span class="nt">&gt;</span>
  	Something that needs to be initialize with JavaScript.
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/turbo-stream&gt;</span>
</code></pre></div></div>

<h2 id="when-stimulus-is-not-an-option">When Stimulus is not an option</h2>

<p>Sometimes Stimulus might not be what we want and we would love to have a Turbo Stream event to hook into anyways.</p>

<p>Luckily, <a href="https://discuss.hotwired.dev/u/sdhull/summary">Steve</a> shared his little implementation of <code class="language-plaintext highlighter-rouge">turbo:after-stream-render</code> in this <a href="https://discuss.hotwired.dev/t/event-to-know-a-turbo-stream-has-been-rendered/1554/25">thread</a>:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// application.js</span>
<span class="kd">const</span> <span class="nx">afterRenderEvent</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Event</span><span class="p">(</span><span class="dl">"</span><span class="s2">turbo:after-stream-render</span><span class="dl">"</span><span class="p">);</span>
<span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">turbo:before-stream-render</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">originalRender</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">detail</span><span class="p">.</span><span class="nx">render</span>

  <span class="nx">event</span><span class="p">.</span><span class="nx">detail</span><span class="p">.</span><span class="nx">render</span> <span class="o">=</span> <span class="nf">function </span><span class="p">(</span><span class="nx">streamElement</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">originalRender</span><span class="p">(</span><span class="nx">streamElement</span><span class="p">)</span>
    <span class="nb">document</span><span class="p">.</span><span class="nf">dispatchEvent</span><span class="p">(</span><span class="nx">afterRenderEvent</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<p>As you can see, the idea is quite simple. We create a new custom <code class="language-plaintext highlighter-rouge">Event</code> and add an event listener for <code class="language-plaintext highlighter-rouge">turbo:before-stream-render</code> which already exist. We then run our event after we are done with original rendering.</p>

<p>To use it we create an event listener and paste the JavaScript that needs to run after rendering:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">turbo:after-stream-render</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">I will run after stream render</span><span class="dl">"</span><span class="p">)</span>
<span class="p">});</span>
</code></pre></div></div>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><category term="hotwire" /><category term="turbo" /><category term="stimulus" /><category term="rails" /><category term="About" /><summary type="html"><![CDATA[Turbo comes with turbo:before-stream-render but unfortunately doesn’t ship with the equivalent turbo:after-stream-render. Here’s how to run JavaScript after the stream renders.]]></summary></entry><entry><title type="html">Check For Custom Errors In Rails Error Objects</title><link href="https://www.whysthatso.net/today-i-learned/check-for-custom-errors-in-rails-error-objects/" rel="alternate" type="text/html" title="Check For Custom Errors In Rails Error Objects" /><published>2025-05-10T00:00:00+00:00</published><updated>2025-05-10T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/check-for-custom-errors-in-rails-error-objects</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/check-for-custom-errors-in-rails-error-objects/"><![CDATA[<p>In a view one can usually do something like this to check if there is an error after a validation and then conditionally change some design, for example:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%</span> <span class="k">if</span> <span class="n">course_registration_form</span><span class="p">.</span><span class="nf">errors</span><span class="p">[</span><span class="ss">:email</span><span class="p">].</span><span class="nf">any?</span> <span class="cp">%&gt;</span>
	<span class="cp">&lt;%=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:p</span><span class="p">,</span> <span class="n">course_registration_form</span><span class="p">.</span><span class="nf">errors</span><span class="p">[</span><span class="ss">:email</span><span class="p">].</span><span class="nf">first</span><span class="p">,</span> <span class="ss">class: </span><span class="s2">"mt-2 text-sm text-red-600"</span><span class="p">)</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>however, if you have a custom validation that groups errors on an arbitrary key that is not backed by an object’s attribute, you have to query differently:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%</span> <span class="k">if</span> <span class="n">course_registration_form</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">:person_info</span><span class="p">).</span><span class="nf">any?</span> <span class="cp">%&gt;</span>
	<span class="cp">&lt;%=</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:p</span><span class="p">,</span> <span class="n">course_registration_form</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">:person_info</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:message</span><span class="p">),</span> <span class="ss">class: </span><span class="s2">"mt-2 text-sm text-red-600"</span><span class="p">)</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><category term="rails" /><summary type="html"><![CDATA[In a view one can usually do something like this to check if there is an error after a validation and then conditionally change some design, for example:]]></summary></entry><entry><title type="html">Setup Rails Staging Environment</title><link href="https://www.whysthatso.net/today-i-learned/setup-rails-staging-environment/" rel="alternate" type="text/html" title="Setup Rails Staging Environment" /><published>2025-05-09T00:00:00+00:00</published><updated>2025-05-09T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/setup-rails-staging-environment</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/setup-rails-staging-environment/"><![CDATA[<ul>
  <li>copy prod env file</li>
  <li>change email settings, set interceptor</li>
  <li>create database.yml staging from production</li>
  <li>check storage.yml</li>
  <li>check secrets.yml</li>
  <li>check if gems are special to staging</li>
</ul>

<p>env settings
RAILS_ENV=staging
RACK_ENV=staging</p>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><summary type="html"><![CDATA[copy prod env file change email settings, set interceptor create database.yml staging from production check storage.yml check secrets.yml check if gems are special to staging]]></summary></entry><entry><title type="html">Turbo Prefetch</title><link href="https://www.whysthatso.net/today-i-learned/turbo-prefetch/" rel="alternate" type="text/html" title="Turbo Prefetch" /><published>2025-05-07T00:00:00+00:00</published><updated>2025-05-07T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/turbo-prefetch</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/turbo-prefetch/"><![CDATA[<p>• Turbo v8 prefetches links on hover
• works automatically with Rails link_to
• disable globally with <code class="language-plaintext highlighter-rouge">&lt;meta name=＂turbo-prefetch＂ content=＂false＂&gt;</code>
• disable per-link or per-section with <code class="language-plaintext highlighter-rouge">data: { turbo_prefetch: false }</code>
• you can re-enable selectively inside disabled areas
• use <code class="language-plaintext highlighter-rouge">turbo:before-prefetch</code> to cancel based on network conditions</p>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><category term="rails" /><category term="turbo" /><summary type="html"><![CDATA[• Turbo v8 prefetches links on hover • works automatically with Rails link_to • disable globally with &lt;meta name=＂turbo-prefetch＂ content=＂false＂&gt; • disable per-link or per-section with data: { turbo_prefetch: false } • you can re-enable selectively inside disabled areas • use turbo:before-prefetch to cancel based on network conditions]]></summary></entry><entry><title type="html">How To Set Default Color In Tailwindcss 4 For A Rails App</title><link href="https://www.whysthatso.net/today-i-learned/how-to-set-default-color-in-tailwindcss-4-for-a-rails-app/" rel="alternate" type="text/html" title="How To Set Default Color In Tailwindcss 4 For A Rails App" /><published>2025-04-29T00:00:00+00:00</published><updated>2025-04-29T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/how-to-set-default-color-in-tailwindcss-4-for-a-rails-app</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/how-to-set-default-color-in-tailwindcss-4-for-a-rails-app/"><![CDATA[<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Application.css looks like this:

```css
@import "tailwindcss";

@theme {
	--brand-color: oklch(0.35 0.2153 266.44);
}

@layer base {
	html {
		@apply text-(--brand-color);
	}
}
</code></pre></div></div>

<p>so the color gets defined in the <code class="language-plaintext highlighter-rouge">@theme</code> directive, and it then gets applied by the base layer.</p>

<p>There are other ways, but this seems to me the cleanest, and when universally needed, the right choice.</p>

<p>Maybe when dynamic application of themes is important, it might be easier to manipulate the <code class="language-plaintext highlighter-rouge">body</code> tag like so:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;body</span> <span class="na">class=</span><span class="s">"text-{color}"</span><span class="nt">&gt;</span>
</code></pre></div></div>

<p>wwwwwwwwwwwwwwwwwyou might still have to define the color somewhere if it is a custom choice.</p>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><category term="tailwindcss" /><category term="rails" /><summary type="html"><![CDATA[``` Application.css looks like this:]]></summary></entry><entry><title type="html">Print Out Postgresql Table Sizes</title><link href="https://www.whysthatso.net/today-i-learned/print-out-postgresql-table-sizes/" rel="alternate" type="text/html" title="Print Out Postgresql Table Sizes" /><published>2025-04-29T00:00:00+00:00</published><updated>2025-04-29T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/print-out-postgresql-table-sizes</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/print-out-postgresql-table-sizes/"><![CDATA[<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span>
    <span class="n">schemaname</span> <span class="o">||</span> <span class="s1">'.'</span> <span class="o">||</span> <span class="n">tablename</span> <span class="k">AS</span> <span class="n">table_full_name</span><span class="p">,</span>
    <span class="n">pg_size_pretty</span><span class="p">(</span><span class="n">pg_total_relation_size</span><span class="p">(</span><span class="n">quote_ident</span><span class="p">(</span><span class="n">schemaname</span><span class="p">)</span> <span class="o">||</span> <span class="s1">'.'</span> <span class="o">||</span> <span class="n">quote_ident</span><span class="p">(</span><span class="n">tablename</span><span class="p">)))</span> <span class="k">AS</span> <span class="n">total_size</span>
<span class="k">FROM</span> <span class="n">pg_tables</span>
<span class="k">WHERE</span> <span class="n">schemaname</span> <span class="k">NOT</span> <span class="k">IN</span> <span class="p">(</span><span class="s1">'pg_catalog'</span><span class="p">,</span> <span class="s1">'information_schema'</span><span class="p">)</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">pg_total_relation_size</span><span class="p">(</span><span class="n">quote_ident</span><span class="p">(</span><span class="n">schemaname</span><span class="p">)</span> <span class="o">||</span> <span class="s1">'.'</span> <span class="o">||</span> <span class="n">quote_ident</span><span class="p">(</span><span class="n">tablename</span><span class="p">))</span> <span class="k">DESC</span><span class="p">;</span>
</code></pre></div></div>

<p>Count mysql table rows (approximate)</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> 
    <span class="n">TABLE_SCHEMA</span><span class="p">,</span>
    <span class="k">TABLE_NAME</span><span class="p">,</span>
    <span class="n">TABLE_ROWS</span>
<span class="k">FROM</span> 
    <span class="n">information_schema</span><span class="p">.</span><span class="n">TABLES</span>
<span class="k">WHERE</span> 
    <span class="n">TABLE_TYPE</span> <span class="o">=</span> <span class="s1">'BASE TABLE'</span>
    <span class="k">AND</span> <span class="n">TABLE_SCHEMA</span> <span class="k">NOT</span> <span class="k">IN</span> <span class="p">(</span><span class="s1">'mysql'</span><span class="p">,</span> <span class="s1">'information_schema'</span><span class="p">,</span> <span class="s1">'performance_schema'</span><span class="p">,</span> <span class="s1">'sys'</span><span class="p">)</span>
<span class="k">ORDER</span> <span class="k">BY</span> 
    <span class="n">TABLE_ROWS</span> <span class="k">DESC</span><span class="p">;</span>
</code></pre></div></div>

<p>show mysql table size</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> 
    <span class="n">TABLE_SCHEMA</span><span class="p">,</span>
    <span class="k">TABLE_NAME</span><span class="p">,</span>
    <span class="n">ROUND</span><span class="p">((</span><span class="n">DATA_LENGTH</span> <span class="o">+</span> <span class="n">INDEX_LENGTH</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1024</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="k">AS</span> <span class="n">size_mb</span>
<span class="k">FROM</span> 
    <span class="n">information_schema</span><span class="p">.</span><span class="n">TABLES</span>
<span class="k">WHERE</span> 
    <span class="n">TABLE_TYPE</span> <span class="o">=</span> <span class="s1">'BASE TABLE'</span>
    <span class="k">AND</span> <span class="n">TABLE_SCHEMA</span> <span class="k">NOT</span> <span class="k">IN</span> <span class="p">(</span><span class="s1">'mysql'</span><span class="p">,</span> <span class="s1">'performance_schema'</span><span class="p">,</span> <span class="s1">'information_schema'</span><span class="p">,</span> <span class="s1">'sys'</span><span class="p">)</span>
<span class="k">ORDER</span> <span class="k">BY</span> 
    <span class="n">size_mb</span> <span class="k">DESC</span><span class="p">;</span>
</code></pre></div></div>

<p>show all unique values in a column</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="k">DISTINCT</span> <span class="n">your_column</span>
<span class="k">FROM</span> <span class="n">your_table</span><span class="p">;</span>
</code></pre></div></div>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><category term="postgresql" /><summary type="html"><![CDATA[SELECT schemaname || '.' || tablename AS table_full_name, pg_size_pretty(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename))) AS total_size FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename)) DESC;]]></summary></entry><entry><title type="html">Linux Optimizations For Subnet Routers And Exit Nodes</title><link href="https://www.whysthatso.net/today-i-learned/linux-optimizations-for-subnet-routers-and-exit-nodes/" rel="alternate" type="text/html" title="Linux Optimizations For Subnet Routers And Exit Nodes" /><published>2025-04-22T00:00:00+00:00</published><updated>2025-04-22T00:00:00+00:00</updated><id>https://www.whysthatso.net/today-i-learned/linux-optimizations-for-subnet-routers-and-exit-nodes</id><content type="html" xml:base="https://www.whysthatso.net/today-i-learned/linux-optimizations-for-subnet-routers-and-exit-nodes/"><![CDATA[<p>https://tailscale.com/kb/1320/performance-best-practices#linux-optimizations-for-subnet-routers-and-exit-nodes</p>

<p>Tailscale version 1.54 or later used with a Linux 6.2 or later kernel enables UDP throughput improvements using <a href="https://www.kernel.org/doc/html/latest/networking/segmentation-offloads.html">transport layer offloads</a>. If a Linux device is acting as an exit node or subnet router, ensure the following network device configuration is in place for the best results:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">NETDEV</span><span class="o">=</span><span class="si">$(</span>ip <span class="nt">-o</span> route get 8.8.8.8 | <span class="nb">cut</span> <span class="nt">-f</span> 5 <span class="nt">-d</span> <span class="s2">" "</span><span class="si">)</span>
<span class="nb">sudo </span>ethtool <span class="nt">-K</span> <span class="nv">$NETDEV</span> rx-udp-gro-forwarding on rx-gro-list off
</code></pre></div></div>

<p>By default, changes made using the <code class="language-plaintext highlighter-rouge">ethtool</code> don’t persistent after a reboot. On Linux distributions using <code class="language-plaintext highlighter-rouge">networkd-dispatcher</code> (which you can verify with <code class="language-plaintext highlighter-rouge">systemctl is-enabled networkd-dispatcher</code>), you can run the following commands to create a script that configures these settings on each boot.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">printf</span> <span class="s1">'#!/bin/sh\n\nethtool -K %s rx-udp-gro-forwarding on rx-gro-list off \n'</span> <span class="s2">"</span><span class="si">$(</span>ip <span class="nt">-o</span> route get 8.8.8.8 | <span class="nb">cut</span> <span class="nt">-f</span> 5 <span class="nt">-d</span> <span class="s2">" "</span><span class="si">)</span><span class="s2">"</span> | <span class="nb">sudo tee</span> /etc/networkd-dispatcher/routable.d/50-tailscale
<span class="nb">sudo chmod </span>755 /etc/networkd-dispatcher/routable.d/50-tailscale
</code></pre></div></div>

<p>Run the following commands to test the script to ensure it runs successfully on your devices:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> /etc/networkd-dispatcher/routable.d/50-tailscale
<span class="nb">test</span> <span class="nv">$?</span> <span class="nt">-eq</span> 0 <span class="o">||</span> <span class="nb">echo</span> <span class="s1">'An error occurred.'</span>
</code></pre></div></div>]]></content><author><name>Andreas Wagner</name></author><category term="today-i-learned" /><summary type="html"><![CDATA[https://tailscale.com/kb/1320/performance-best-practices#linux-optimizations-for-subnet-routers-and-exit-nodes]]></summary></entry></feed>