<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Random Musings on Technology]]></title><description><![CDATA[I used to be a banker, but I lost interest...]]></description><link>http://baqueiro.com/</link><image><url>http://baqueiro.com/favicon.png</url><title>Random Musings on Technology</title><link>http://baqueiro.com/</link></image><generator>Ghost 5.5</generator><lastBuildDate>Sat, 27 Sep 2025 19:14:56 GMT</lastBuildDate><atom:link href="http://baqueiro.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Testing ChatGPT]]></title><description><![CDATA[<p>With all the rage of ChatGPT going around, I gave it a test to see how would it do as a code reviewer. </p><p>To test, I gave it the code of my DominoParty game at: <a href="https://github.com/obaqueiro/dominoparty/blob/master/backend/app/app.ts">https://github.com/obaqueiro/dominoparty/blob/master/backend/app/app.ts</a> .</p><p>I asked it to</p>]]></description><link>http://baqueiro.com/testing-chatgpt/</link><guid isPermaLink="false">638ea2c3457eaa000123b81a</guid><dc:creator><![CDATA[Omar Baqueiro]]></dc:creator><pubDate>Tue, 06 Dec 2022 02:10:18 GMT</pubDate><content:encoded><![CDATA[<p>With all the rage of ChatGPT going around, I gave it a test to see how would it do as a code reviewer. </p><p>To test, I gave it the code of my DominoParty game at: <a href="https://github.com/obaqueiro/dominoparty/blob/master/backend/app/app.ts">https://github.com/obaqueiro/dominoparty/blob/master/backend/app/app.ts</a> .</p><p>I asked it to analyze it and give me the top errors, it replied the following:</p><blockquote>There are no obvious errors in the code. However, there are some potential issues and areas for improvement.</blockquote><blockquote>The code does not check for errors when calling Redis functions. For example, in the <code>onPlayerConnect</code> function, the code assumes that the <code>get</code> call will always return a value, but it does not check for errors or handle the case where no value is returned.</blockquote><blockquote>The code does not handle disconnections or clean up after players leave a game room. If a player disconnects, the server will continue to treat that player as being in the game room, even though they are no longer connected. This can lead to inconsistencies in the game state and potentially cause errors.</blockquote><blockquote>The code uses a global <code>redis</code> variable to store the Redis client instance. This could cause problems if multiple instances of the game server are running concurrently, as they would all be sharing the same Redis client. It would be better to create a new Redis client instance for each game server instance.</blockquote><blockquote>The code does not use any authentication or authorization mechanisms to prevent unauthorized access to the game server. It is possible for anyone who knows the server&apos;s address and port to connect to it and potentially manipulate the game state.</blockquote><blockquote>The code does not validate the data received from clients. This means that it is possible for malicious clients to send invalid or malformed data to the server, potentially causing errors or security vulnerabilities.</blockquote><blockquote>Overall, the code works as intended, but it could benefit from improved error handling, better handling of player disconnections, better management of the Redis client, and improved security.</blockquote><p></p><p>Then I asked it to tell me how to fix the firs terror, to which it responded:</p><pre><code class="language-JavaScript">// When a player connects a room, add the player to the room and notify other playersonPlayerConnect(roomName: string, socket: Socket) {console.log(Player ${socket.id} connecting to room:  + roomName);let roomData: {boardData: any, players: any} = {boardData:{}, players:[]};
// get room data from DB
this.redis.get(roomName, (err, data) =&gt; {
  if (err) {
    // handle the error
    console.error(`Error getting room data for ${roomName}: ${err}`);
    return;
  }

  if (data) {
    let room = JSON.parse(data);

    // construct response payload with room data
    roomData = {
      boardData: room.boardData,
      players: room.players || []
    };

    // notify other players of player joining the room
    if (room.players) {
      let availablePlayers: string[] = [];
      room.players.forEach(player =&gt; {
        if (this.io.sockets.connected[player]) {
          availablePlayers.push(player);
          this.io.sockets.connected[player].emit(&apos;message&apos;, { event: &apos;playerConnected&apos; });
</code></pre><p>It basically inserted the <code>if(err)</code> condition. &#xA0;Pretty neat.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Quick Script to Backup Postgres WAL]]></title><description><![CDATA[<p>I wanted to run a simple and quick script that could backup Postgres WAL into S3. The following simple Bash script does exactly what I need:</p><pre><code>pg_recvlogical -f -  --start -d database  -h 127.0.0.1 \ 
  -U postgres --slot test_slot | gsplit  -dl 1000000 \ 
  --filter=&apos;bzip2 -9</code></pre>]]></description><link>http://baqueiro.com/quick-script-to-backup-postgres-wal/</link><guid isPermaLink="false">6324a62f457eaa000123b7ea</guid><dc:creator><![CDATA[Omar Baqueiro]]></dc:creator><pubDate>Fri, 16 Sep 2022 16:43:36 GMT</pubDate><media:content url="http://baqueiro.com/content/images/2022/09/Postgresql_elephant.svg.png" medium="image"/><content:encoded><![CDATA[<img src="http://baqueiro.com/content/images/2022/09/Postgresql_elephant.svg.png" alt="Quick Script to Backup Postgres WAL"><p>I wanted to run a simple and quick script that could backup Postgres WAL into S3. The following simple Bash script does exactly what I need:</p><pre><code>pg_recvlogical -f -  --start -d database  -h 127.0.0.1 \ 
  -U postgres --slot test_slot | gsplit  -dl 1000000 \ 
  --filter=&apos;bzip2 -9 &gt; $(date +%s).bz2&apos; - logical</code></pre><p>This will continuously monitor Postgres WAL through a WAL slot named <code>test_slot</code> (must be previously configured), &#xA0;split the data after 1&apos;000,000 lines, BZip it and save it as a file with the current Unix timestamp as date. </p><p>Combining this with say S3FS one could keep a continuous &#xA0;compressed copy of ALL changes happening to the Postgres databases. In case some catastrophic failure happen, the data could be restored by <em>replaying</em> the WAL. &#xA0;This assumes that the received WAL is in JSON format, which provides every change as a one line JSON string.</p>]]></content:encoded></item><item><title><![CDATA[TicTacToe in C]]></title><description><![CDATA[<p>One of the<em> coding exercises</em> I&apos;ve used to interview people is to implement a TicTacToe in 30 minutes. The idea is to implement as much as possible from the simple game in the language of their choice, being able to search whatever they need to on the internet.</p>]]></description><link>http://baqueiro.com/tictactoe-in-c/</link><guid isPermaLink="false">630ae7aa457eaa000123b7cf</guid><dc:creator><![CDATA[Omar Baqueiro]]></dc:creator><pubDate>Sun, 28 Aug 2022 04:01:40 GMT</pubDate><content:encoded><![CDATA[<p>One of the<em> coding exercises</em> I&apos;ve used to interview people is to implement a TicTacToe in 30 minutes. The idea is to implement as much as possible from the simple game in the language of their choice, being able to search whatever they need to on the internet. </p><p>Tonight I was in the mood to do some C programming (haven&apos;t done that in a long time) so I decided to implement the TicTacToe game in C. &#xA0;In total I spent 25 minutes doing the implementation. &#xA0;I had to look a couple of things like how to get a random int, and how to initialize an array. &#xA0;</p><p>I liked my resulting code:</p><!--kg-card-begin: markdown--><pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;time.h&gt;

#define ERROR_INVALID_POSITION -1 
#define ERROR_POSITION_NOT_EMPTY -2
#define ERROR_INVALID_TOKEN -3

#define NO_WINNER &apos;-&apos;
#define WINNER_TIE &apos;T&apos;

void printBoard();
int setToken(int n, char token);
void userMove();
void computerMove();
char checkWinner();

char board[] = {&apos;-&apos;, &apos;-&apos;, &apos;-&apos;, &apos;-&apos;, &apos;-&apos;, &apos;-&apos;, &apos;-&apos;, &apos;-&apos;,&apos;-&apos; };

int main() {
  srand(time(NULL));   
  char winner = &apos;-&apos;;
  while(1) {
    printBoard();  
    userMove(); 
    winner = checkWinner();
    if (winner != NO_WINNER) break;
    printf(&quot;%c\n&quot;, winner);
    computerMove();
    winner = checkWinner();
    printf(&quot;%c\n&quot;, winner);
    if (winner != NO_WINNER) break;
  }

  printBoard();  
  if (winner == WINNER_TIE) {
    puts(&quot;A boring tie.... how boring&quot;);
  }
  else 
  {
    printf(&quot;Winner is: %c. Congrats!\n&quot;, winner);
  }
  return 0;
}

void  printBoard() {
  for (int row=0; row &lt; 3; row++) {
    printf(&quot;%c|%c|%c\n&quot;,board[3*row+0], board[3*row+1], board[3*row+2]);
  }
  puts(&quot;&quot;);
}

int setToken(int n, char token) {
 n--; // To start positions from 1
 if (n &lt; 0 || n&gt; 8) {
  return ERROR_INVALID_POSITION; 
 }
 if (board[n] != &apos;-&apos;) {
  return ERROR_POSITION_NOT_EMPTY;
 }

 if (token != &apos;O&apos; &amp;&amp; token !=&apos;X&apos;) {
  return ERROR_INVALID_TOKEN;
 } 
 board[n] = token;
 return 0;
}

void userMove() {
  int position;
  while (1) {
    puts(&quot;Enter a number [1-9] to set an O:&quot;);
    scanf(&quot;%d&quot;,&amp;position);
    int error = setToken( position, &apos;O&apos;);
    if (error == ERROR_INVALID_POSITION) {
      puts(&quot;Invalid Position Entered, try again\n&quot;);
    } else
    if (error == ERROR_POSITION_NOT_EMPTY) {
      puts(&quot;That position is already taken, try again\n&quot;);
    } else 
    { 
      break;
    }
  }
}

void computerMove() {
  int n = 0;
  while (1) {
    n = rand() % 8 + 1;
    if (setToken(n,&apos;X&apos;) == 0) break;
  }
}

char checkWinner() {
 if (board[0] == board[1]  &amp;&amp; board[1] == board[2]) { return board[0]; }
 if (board[3] == board[4]  &amp;&amp; board[4] == board[5]) { return board[3]; }
 if (board[6] == board[7]  &amp;&amp; board[7] == board[8]) { return board[6]; }


 if (board[0] == board[3]  &amp;&amp; board[3] == board[6]) { return board[0]; }
 if (board[1] == board[4]  &amp;&amp; board[4] == board[7]) { return board[1]; }
 if (board[2] == board[5]  &amp;&amp; board[5] == board[8]) { return board[2]; }

 if (board[0] == board[4]  &amp;&amp; board[4] == board[8]) { return board[0]; }
 if (board[2] == board[4]  &amp;&amp; board[4] == board[6]) { return board[2]; }

 int empty = 0;
 for (int i = 0; i &lt; 9; i++ ) {
   if (board[i] == &apos;-&apos;) { empty++; }
 }
 if (empty == 0) { return WINNER_TIE; }

 return NO_WINNER;
}

</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Comparing Compression Performance between xz, bzip2 and gzip]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>There&apos;s a good article about comparing the performance between these three compression formats at <a href="https://www.rootusers.com/gzip-vs-bzip2-vs-xz-performance-comparison/">https://www.rootusers.com/gzip-vs-bzip2-vs-xz-performance-comparison/</a> .  However, I felt there was one chart missing: I wanted to visualize together the Compression Speed vs Ratio performance together.</p>
<p><img src="https://i.ibb.co/58X11xN/image.png" alt="image" loading="lazy"></p>
<p>This gives a good overall comparison between the 3</p>]]></description><link>http://baqueiro.com/comparing-compression-performance-between-xz-bzip2-and-gzip/</link><guid isPermaLink="false">62e80f0380883e0001faeeb4</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Thu, 28 Jul 2022 20:31:13 GMT</pubDate><media:content url="http://baqueiro.com/content/images/2022/08/plastic-bottles-g95ebe413a_640.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://baqueiro.com/content/images/2022/08/plastic-bottles-g95ebe413a_640.jpeg" alt="Comparing Compression Performance between xz, bzip2 and gzip"><p>There&apos;s a good article about comparing the performance between these three compression formats at <a href="https://www.rootusers.com/gzip-vs-bzip2-vs-xz-performance-comparison/">https://www.rootusers.com/gzip-vs-bzip2-vs-xz-performance-comparison/</a> .  However, I felt there was one chart missing: I wanted to visualize together the Compression Speed vs Ratio performance together.</p>
<p><img src="https://i.ibb.co/58X11xN/image.png" alt="Comparing Compression Performance between xz, bzip2 and gzip" loading="lazy"></p>
<p>This gives a good overall comparison between the 3 methods on both factors.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Tech Layoffs]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Now that tech lay offs are starting to happen, these are some interesting pages where one can follow them:</p>
<ul>
<li><a href="https://layoffs.fyi">https://layoffs.fyi</a></li>
<li><a href="https://trueup.io/layoffs">https://trueup.io/layoffs</a> (great charts!)</li>
<li>... will add more here if I find something noteworthy</li>
</ul>
<!--kg-card-end: markdown-->]]></description><link>http://baqueiro.com/tech-layoffs/</link><guid isPermaLink="false">62e80f0380883e0001faeeb3</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Fri, 01 Jul 2022 14:12:42 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Now that tech lay offs are starting to happen, these are some interesting pages where one can follow them:</p>
<ul>
<li><a href="https://layoffs.fyi">https://layoffs.fyi</a></li>
<li><a href="https://trueup.io/layoffs">https://trueup.io/layoffs</a> (great charts!)</li>
<li>... will add more here if I find something noteworthy</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Pause & Resume VirtualBox VM in OSX on Focus change]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>This little script will pause and resume a virtual machine running under VirtualBox when the window focus changes. It resumes (unpause) the machine whenever its window gets the mouse focus, and it pauses the VM whenever it loses focus.</p>
<p>It is useful for when you have an alternate &quot;desktop&</p>]]></description><link>http://baqueiro.com/pause-resume-virtualbox-vm-in-osx-on-focus-change/</link><guid isPermaLink="false">62e80f0380883e0001faeeb1</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Fri, 08 Apr 2022 19:44:25 GMT</pubDate><media:content url="http://baqueiro.com/content/images/2022/08/vbox_logo2_gradient.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://baqueiro.com/content/images/2022/08/vbox_logo2_gradient.png" alt="Pause &amp; Resume VirtualBox VM in OSX on Focus change"><p>This little script will pause and resume a virtual machine running under VirtualBox when the window focus changes. It resumes (unpause) the machine whenever its window gets the mouse focus, and it pauses the VM whenever it loses focus.</p>
<p>It is useful for when you have an alternate &quot;desktop&quot;  within a VM, so that it only runs whenever you are doing something in it.</p>
<pre><code>#!/usr/bin/python

try:
    from AppKit import NSWorkspace
except ImportError:
    print &quot;Can&apos;t import AppKit -- maybe you&apos;re running python from brew?&quot;
    print &quot;Try running with Apple&apos;s /usr/bin/python instead.&quot;
    exit(1)

from datetime import datetime
from time import sleep
import os

last_active_name = None
while True:
    active_app = NSWorkspace.sharedWorkspace().activeApplication()
    if active_app[&apos;NSApplicationName&apos;] != last_active_name:
        if last_active_name == &apos;VirtualBox VM&apos;:
            os.system(&apos;VBoxManage controlvm  db3bd778-3e91-4834-8730-1a6e3f5e59c3 pause&apos;)

        last_active_name = active_app[&apos;NSApplicationName&apos;]

        if last_active_name == &apos;VirtualBox VM&apos;:
            os.system(&apos;VBoxManage controlvm  db3bd778-3e91-4834-8730-1a6e3f5e59c3 resume&apos;)

        print &apos;%s: %s [%s]&apos; % (
            datetime.now().strftime(&apos;%Y-%m-%d %H:%M:%S&apos;),
            active_app[&apos;NSApplicationName&apos;],
            active_app[&apos;NSApplicationPath&apos;]
        )
    sleep(1)
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Good writeup on migrating from AWS to GCP]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="http://hokstad.com/blog">Vidar Hokstad</a> wrote a really good comment on his experience migrating some infrastructure from AWS to GCP <a href="https://news.ycombinator.com/item?id=30945448">in this HN thread</a>. I&apos;m reproducing here verbatim because I think it is pretty insightful:</p>
<blockquote>
<p>Unfortunately not, but it&apos;s surprisingly straight-forward, apart from the database bit, but here&apos;</p></blockquote>]]></description><link>http://baqueiro.com/good-writeup-on-migrating-from-aws-to-gcp-2/</link><guid isPermaLink="false">62e80f0380883e0001faeeb0</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Thu, 07 Apr 2022 16:00:55 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><a href="http://hokstad.com/blog">Vidar Hokstad</a> wrote a really good comment on his experience migrating some infrastructure from AWS to GCP <a href="https://news.ycombinator.com/item?id=30945448">in this HN thread</a>. I&apos;m reproducing here verbatim because I think it is pretty insightful:</p>
<blockquote>
<p>Unfortunately not, but it&apos;s surprisingly straight-forward, apart from the database bit, but here&apos;s a bit more detail from memory. There are many ways of doing this and some will depend strongly on which tools you&apos;re comfortable with (e.g. nginx vs. haproxy vs. some other reverse proxy is largely down to which one you know best and/or already have in the mix) [Today I might have considered K8s, but this was before that was even a realistic option, but frankly even with K8s I&apos;m not sure -- the setup in question was very simple to maintain]:</p>
</blockquote>
<blockquote>
<ul>
<li>Set up haproxy, nginx or similar as reverse proxy and carefully decide if you can handle retries on failed queries. If you want true zero-downtime migration there&apos;s a challenge here in making sure you have a setup that lets you add and remove backends transparently. There are many ways of doing this of various complexity. I&apos;ve tended to favour using dynamic dns updates for this; in this specific instance we used Hashicorp&apos;s Consul to keep dns updated w/services. I&apos;ve also used ngx_mruby for instances where I needed more complex backend selection (allows writing Ruby code to execute within nginx)</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Set up a VPN (or more depending on your networking setup) between the locations so that the reverse proxy can reach backends in both/all locations, and so that the backends can reach databases both places.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Replicate the database to the new location.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Ensure your app has a mechanism for determining which database to use as the master. Just as for the reverse proxy we used Consul to select. All backends would switch on promoting a replica to master.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Ensure you have a fast method to promote a database replica to a master. You don&apos;t want to be in a situation of having to fiddle with this. We had fully automated scripts to do the failover.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Ensure your app gracefully handles database failure of whatever it thinks the current master is. This is the trickiest bit in some cases, as you either need to make sure updates are idempotent, or you need to make sure updates during the switchover either reliably fail or reliably succeed. In the case I mentioned we were able to safely retry requests, but in many cases it&apos;ll be safer to just punt on true zero downtime migration assuming your setup can handle promotion of the new master fast enough (in our case the promotion of the new Postgres master took literally a couple of seconds, during which any failing updates would just translate to some page loads being slow as they retried, but if we hadn&apos;t been able to retry it&apos;d have meant a few seconds downtime).</li>
</ul>
</blockquote>
<blockquote>
<p>Once you have the new environment running and capable of handling requests (but using the database in the old environment):</p>
</blockquote>
<blockquote>
<ul>
<li>Reduce DNS record TTL.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Ensure the new backends are added to the reverse proxy. You should start seeing requests flow through the new backends and can verify error rates aren&apos;t increasing. This should be quick to undo if you see errors.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Update DNS to add the new environment reverse proxy. You should start seeing requests hit the new reverse proxy, and some of it should flow through the new backends. Wait to see if any issues.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Promote the replica in the new location to master and verify everything still works. Ensure whatever replication you need from the new master works. You should now see all database requests hitting the new master.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Drain connections from the old backends (remove them from the pool, but leave them running until they&apos;re not handling any requests). You should now have all traffic past the reverse proxy going via the new environment.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>Update DNS to remove the old environment reverse proxy. Wait for all traffic to stop hitting the old reverse proxy.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>When you&apos;re confident everything is fine, you can disable the old environment and bring DNS TTL back up.</li>
</ul>
</blockquote>
<blockquote>
<p>The precise sequencing is very much a question of preference - the point is you&apos;re just switching over and testing change by change, and through most of them you can go a step back without too much trouble. I tend to prefer ensuring you do changes that are low effort to reverse first. Need to keep in mind that some changes (like DNS) can take some time to propagate.</p>
</blockquote>
<blockquote>
<p>EDIT: You&apos;ll note most of this is basically to treat both sites as one large environment using a VPN to tie them together and ensure you have proper high availability. Once you do, the rest of the migration is basically just failing over.</p>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Swapping the annoying § key to ` (backtick) in OSX]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>When using a keyboard with Spanish/Mexico layout and English layout setting, the &#xA7; key is position in the place where the ` should be (left side of the 1 key).</p>
<p>This tells you how to fix that:</p>
<p><a href="https://ppolyzos.com/2020/10/09/swap-places-between-tilde-and-section-sign-%C2%A7-key-in-your-macbook-keyboard/">https://ppolyzos.com/2020/10/09/swap-places-between-tilde-and-section-sign-&#xA7;-key-in-your-macbook-keyboard/</a></p>
<p>Archive link in case</p>]]></description><link>http://baqueiro.com/swapping-the-annoying-ss-key-to-tilde-in-osx/</link><guid isPermaLink="false">62e80f0380883e0001faeeaf</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Mon, 28 Mar 2022 20:00:22 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>When using a keyboard with Spanish/Mexico layout and English layout setting, the &#xA7; key is position in the place where the ` should be (left side of the 1 key).</p>
<p>This tells you how to fix that:</p>
<p><a href="https://ppolyzos.com/2020/10/09/swap-places-between-tilde-and-section-sign-%C2%A7-key-in-your-macbook-keyboard/">https://ppolyzos.com/2020/10/09/swap-places-between-tilde-and-section-sign-&#xA7;-key-in-your-macbook-keyboard/</a></p>
<p>Archive link in case it goes away:<br>
<a href="https://archive.is/kjLoU">https://archive.is/kjLoU</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[NestJS + TypeORM = Doesn't work, very brittle.]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Just needed to write this out. I&apos;ve been working with some project which was decided to be implemented in NestJS. I have always been wary of using &quot;thick&quot; backend frameworks to implement applications, because I&apos;ve seen all the problems that arise when a &quot;</p>]]></description><link>http://baqueiro.com/nestjs-typeorm-doesnt-work-very-brittle/</link><guid isPermaLink="false">62e80f0380883e0001faeead</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Tue, 15 Feb 2022 23:49:19 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Just needed to write this out. I&apos;ve been working with some project which was decided to be implemented in NestJS. I have always been wary of using &quot;thick&quot; backend frameworks to implement applications, because I&apos;ve seen all the problems that arise when a &quot;toy program&quot; becomes a real-life production project using RoR, LoopBack and similar frameworks.</p>
<p>At some point someone convinced me to try NestJS. In <em>theory</em> it is different because it uses several standard libraries and it &quot;just&quot; provides a framework to glue them all.</p>
<p>The problem is that <em>that</em> glue is pretty brittle: As with all frameworks, once you have to deviate <em>a bit</em> from their intended use, you are screwed. Similarly, you <em>will</em> hit a little known bug that is hidden deep inside the framework... and because it is free, nobody will really care if you get blocked (though luck, fix it yourself... if you can/have-time dive into the code).  I&apos;ve got two examples so far:</p>
<h2 id="usingthetypeormlibrary">Using the TypeORM library</h2>
<p>We had implemented a good chunk of code in NestJS, and it was working OK. However, at some point we decided to implement the TypeORM library as it is described in NestJS documentation (nothing fancy, just one table using Postgres). While everything works when running the application, the tests are irremediably broken due to this bug:</p>
<p><a href="https://github.com/nestjs/typeorm/issues/321">https://github.com/nestjs/typeorm/issues/321</a></p>
<blockquote>
<p>TypeError: Right-hand side of &apos;instanceof&apos; is not an object when use absolute path in nestjs</p>
</blockquote>
<p>Apparently NestJS + TypeORM do not play well with the Jest framework. The Github issue specifically talks about &quot;using absolute paths&quot;, but this happened to us even when <strong>not</strong> using absolute paths. Of course getting &quot;support&quot; (which is free support because it&apos;s free software after all) did not help at all, and in this case, the NestJS team didn&apos;t even acknowledge the bug.</p>
<h2 id="usingarabbitmqmicroservice">Using a RabbitMQ Microservice</h2>
<p>The second issue happened when trying to use the &quot;sanctioned&quot; RabbitMQ micro-service configuration. I reported it in the following issue:</p>
<p><a href="https://github.com/nestjs/nest/issues/7196">https://github.com/nestjs/nest/issues/7196</a></p>
<p>Basically, a malformed JSON in a RabbitMQ message is not being caught as an exception where it should. The issue has been open for several months now in the &quot;needs triage&quot; state. Good thing I found a workaround!</p>
<h2 id="workarounds">Workarounds</h2>
<p>Because life must go on, I had to find workarounds for these two issues. Fortunately, workarounds were easy to do, but it seemed that I had to &quot;patch&quot; <em>my</em> code for problems happening in the NestJS framework.</p>
<h2 id="whatcomesnext">What comes next</h2>
<p>I would not use NestJS for any future project, nor I would recommend it for anyone. Sure, the prospect of not having to implement a lot of &quot;boilerplate&quot; code sounds good, but in this case, having to dig into some unknown codebase made me lose time that I could have been using for implementing real work.</p>
<p>It will be better to download one of the many NodeJS hexagonal-architecture boilerplates or MVC or NTier or whatever architecture you prefer, and then using standard libraries like Express, Sequelize, Bunny, etc to connect to services.  That way if something doesn&apos;t work, you will have complete control.</p>
<p>I still prefer thin frameworks.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Online Music Services should Provide Play/Like history in a Standard format.]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h3 id="anoldmansrant">An old man&apos;s rant</h3>
<p>Back in the day (20 years ago?) when online music services started (after the RIAA lost against MP3 piracy), one of the main features of <em>online</em> music players that you could not play something specific, but could get a mix based on artists that</p>]]></description><link>http://baqueiro.com/online-music-services-should-provide-play-like-history-in-a-standard-format/</link><guid isPermaLink="false">62e80f0380883e0001faeeac</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Mon, 06 Dec 2021 15:51:09 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h3 id="anoldmansrant">An old man&apos;s rant</h3>
<p>Back in the day (20 years ago?) when online music services started (after the RIAA lost against MP3 piracy), one of the main features of <em>online</em> music players that you could not play something specific, but could get a mix based on artists that you liked. One of the services that offered this was LastFM (way back in 2002). Additionally you had the ability to &quot;like&quot; the music that you have liked, and &quot;block&quot; any song you didn&apos;t; logging every song you played and every other song you skipped.</p>
<p>The problem is... that since that time, there have been a plethora of new services (Rhapsody, Rrdio, Deezer, Pandora, Tidal, Spotify, <s>Google Play</s>Youtube Music, among several more obscure others), and each of them saves in one way or another your list of played, skipped, loved and hated tracks, over and over.  Every one of them think that the way they use your play history to suggest you new songs is <em>the best one</em>; and most likely all of it sucks.</p>
<p>The way I see it, that data should be available for download in an open and standard format. So that you could <strong>a)</strong> Move it between music services if you like and <strong>b)</strong> Use any &quot;neutral&quot; third party application (maybe open source?) to analyze your play history and give you insights.</p>
<p>Coming back to LastFM, they tried to do something like that: You can connect a LastFM plugin in some of the services that will send your listened songs to LastFM. The problem is that <strong>a)</strong> It is not open (you cannot download your history form LastFM in a suitable format) and <strong>b)</strong> Not all services support it,  and <strong>c)</strong> Once you generate your data in LastFM, you can only listen it there.</p>
<h3 id="proposalforanfreeopenmusicstreaminghistorydataformat">Proposal for an Free, Open Music Streaming History Data format</h3>
<p>What I am proposing is simple. Come up with a simple JSON (or YAML if we feel brave) standard that allows one to store the behavioural data as you play your music in <em>any</em> service, including things like: &quot;loved this song&quot;,  &quot;skipped this song after N seconds&quot;, &quot;hated this song&quot;, etc.</p>
<p>This will give everyone the ability to really own their data, and will open the opportunity to improve recommendation algorithms by third parties (open source or commercial).</p>
<p>The format could be something as simple as this:</p>
<pre><code>listening_history.json 
-------
{
   exportDate: &quot;2022-01-01T20:35Z&quot;,
   listenedTracks: [
    {
       artistName: &quot;Metallica&quot;,
       albumName:  &quot;Ride the Lightning&quot;, 
       trackName:  &quot;For Whom the Bell Tolls&quot;,
       durationSeconds: 309,
       playData: {
          playTimeSeconds: 150,
          playScore: 1
       }
    },
    {
       artistName: &quot;Megadeth&quot;,
       albumName:  &quot;Youthanasia&quot;, 
       trackName:  &quot;Victory&quot;,
       durationSeconds: 327,
       playData: {
          playTimeSeconds: 327,
          playScore: -1
       }
    }
 ...
]
</code></pre>
<p>All fields are straightforward except maybe <code>playScore</code>, which would be used to capture the &quot;like&quot; and &quot;dislike&quot; buttons across different services (i.e. when someone clicks on a <em>like</em> button, the playScore would be +1; clicking on a &quot;skip/block&quot; button would set the playScore to -1; finally 0 would be a default value for when there&apos;s not reaction to the song).</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Checking on my cat whereabouts ... the geek way]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Recently we installed a cat door at home to allow our cat to go in and out as she pleases. After installing it, my wife innocently asked &quot;wouldn&apos;t it be cool if we could know when the cat goes out and in of the door?&quot;.  Of</p>]]></description><link>http://baqueiro.com/checking-on-my-cat-whereabouts-the-geek-way/</link><guid isPermaLink="false">62e80f0380883e0001faeeab</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Mon, 08 Nov 2021 16:54:21 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Recently we installed a cat door at home to allow our cat to go in and out as she pleases. After installing it, my wife innocently asked &quot;wouldn&apos;t it be cool if we could know when the cat goes out and in of the door?&quot;.  Of course I took that as a dare, and a weekend project was born.</p>
<p>For this project, I setup a system that senses whenever the cat moves through the door (using an arduino-like board and a move sensor), and sends a ping to an AWS lambda function. The Lambda function registers the date and time of the ping and writes the timestamp in a Goole Spreadsheet into a new row.</p>
<h2 id="thecatintroducinggarrita">The Cat (introducing Garrita)</h2>
<p>This is Garrita, the one that started everything. She loves going in and out of the house at her own pleasure. For those of you who don&apos;t speak Spanish, &quot;Garrita&quot; roughly translates to &quot;little claw&quot;.<br>
<img src="https://i.ibb.co/3NqXWjJ/image.png" alt="garrita" loading="lazy"></p>
<p>And this is the door we want to spy:<br>
<img src="https://i.ibb.co/6twX7NP/image.png" alt="the cat door" loading="lazy"></p>
<h2 id="thehardware">The hardware</h2>
<p>For this project I used the following hardware:</p>
<ul>
<li>An <a href="https://en.wikipedia.org/wiki/ESP32">ESP32</a> board which was around $200 MXN ($10 USD), <img src="https://i.ibb.co/m00XrpZ/image.png" alt="ESP32 board" loading="lazy"></li>
<li><a href="https://components101.com/sensors/hc-sr501-pir-sensor">HC SR501 PIR movement sensor</a> that goes for around $161 MXN ($7 USD) for 5 pieces <img src="https://i.ibb.co/W2f7Gph/image.png" alt="sensor" loading="lazy"></li>
<li>a bunch of <a href="https://arduinogetstarted.com/hardware/best-jumper-wires-for-arduino">jumper cables</a> to connect the components, costing around $90 MXN (~$4 USD) for 120 cables. <img src="https://i.ibb.co/qyZ3ngT/image.png" alt="Jumper cables" loading="lazy"></li>
</ul>
<p>Connecting the hardware was pretty straightforward. There are plenty of web sites that explain how to connect the PIR sensor into the ESP32 board. <a href="https://randomnerdtutorials.com/telegram-esp32-motion-detection-arduino/">This one</a> shows how to, as part of a nice project. Basically the PIR sensor has a positive (+), negative (-) and a data connector.  The negative connects into the ESP32 ground, the data connects into any of the ESP32 data pins, and the positive connects into the VIN pin (this is instead of the standard 3 Volt pin, because the PIR connector requires 5 volts, which are available through the VIN PIN).</p>
<p>After connecting everything this is how the hardware looks like. Pretty simple:<br>
<img src="https://i.ibb.co/xgW8Vkw/image.png" alt="all connected" loading="lazy"></p>
<h1 id="thesoftware">The Software</h1>
<h3 id="esp32boardsoftware">ESP32 Board Software</h3>
<p>For the software, I used the <a href="https://www.arduino.cc/en/software">Arduino IDE</a>.  And connected the ESP32 to my MacBook for flashing. Note that I had to install UART serial port drivers for the ESP32 to be seen by OSX. The drivers can be downloaded <a href="https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers">from this page</a>.</p>
<p>The software for the ESP32 is pretty simple, we setup an interrupt to listen to events when the PIR sensor detects movement, then we send a ping in the form of an HTTP GET request into the AWS API gateway lambda function that we will be using. Of course we must do all the Wifi initialization and other setup processes.</p>
<pre><code class="language-C">#include &quot;WiFi.h&quot;
#include &lt;stdio.h&gt;
#include &lt;HTTPClient.h&gt;
#include &lt;TimeLib.h&gt;

#define timeSeconds 10
#define MY_SSID &quot;yourWiFiSSID&quot;
#define PASSWORD &quot;YourWifiPassword&quot;
#include &quot;soc/rtc_wdt.h&quot;


#define MY_URL String(&quot;https://your-AWS-api-gateway-endpoint.aws.amazon.com/dev/event&quot;)
#define EVENT_DELAY_TIME 60
const int motionSensor = 27;

/* We use a flag to indicate when the sensor movement was detected because 
 * we have to send the HTTP request outside of the interrupt handler
*/
int sensor = 0;
// Setu
time_t lastEventTime = now() - EVENT_DELAY_TIME ;

// Performs a GET request to the endpoint appending the specified sensor
void do_request(String sensor) {
  String request_string =  MY_URL + String(&quot;?&quot;) +  sensor;
  HTTPClient http;
  http.begin(request_string);
  Serial.println(request_string);
  int httpCode = http.GET();  
    if (httpCode &gt; 0) { //Check for the returning code
        String payload = http.getString();
      }
    Serial.println(httpCode);
    http.end(); //Free the resources
}

// Checks if motion was detected, sets LED HIGH and starts a timer
void IRAM_ATTR detectsMovement() {
  Serial.println(&quot;Motiong detected&quot;);  
  // Interupt handler only raises the flag to send the sensor event
  time_t timeNow = now();

  // Wait at least 60 seconds between sending events
  Serial.println(timeNow - lastEventTime);    
  if (timeNow - lastEventTime &gt; EVENT_DELAY_TIME) {
    
    Serial.println(&quot;Sending event&quot;);  
    sensor = 1;
    lastEventTime = timeNow;
  } else {
      Serial.println(&quot;Delayig event dispatch&quot;);  
  }
}

void setup()
{  
    Serial.begin(115200);
    // Set WiFi to station mode and disconnect from an AP if it was previously connected
    WiFi.mode(WIFI_STA);
    WiFi.begin(MY_SSID, PASSWORD);
    if(WiFi.waitForConnectResult() != WL_CONNECTED) {
         Serial.println(&quot;Wifi Connection failed&quot;); 
    }
    WiFi.printDiag(Serial);
    
    Serial.println(&quot;Setup done&quot;);
    pinMode (LED_BUILTIN, OUTPUT);
    // Set LED to LOW
    digitalWrite(LED_BUILTIN, LOW);
    
    // PIR Motion Sensor mode INPUT_PULLUP
    pinMode(motionSensor, INPUT_PULLUP);
    // Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
    attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

      
}

void loop()
{
    // If the sensor flag is up then we send the request and reset the flag
    if (sensor &gt; 0) {
      do_request( sensor == 1 ? &quot;sensor_1&quot; : &quot;sensor_2&quot;);    
      sensor = 0;
    }
    // Wait a bit before looping
    delay(3000);

}
</code></pre>
<p>This code is also <a href="https://github.com/obaqueiro/esp32-devkit/blob/master/projects/motion_detector/esp32/esp32.ino">available on github</a>.</p>
<p>We do some simple logic to prevent sending multiple pings for the same movement detection event.  This is something that I have to improve in the future, doing a better noise reduction algorithm.</p>
<h3 id="serverlessawslambdahttpgateway">Serverless AWS/Lambda/HTTP-gateway</h3>
<p>To capture the pings from the ESP32 board, I setup a Serverless framework project using AWS Lambda and HTTP gateway technologies.  The full code is also available <a href="https://github.com/obaqueiro/esp32-devkit/tree/master/projects/motion_detector/serverless">on github</a>. But basically it mainly consists of a <code>serverless.yml</code> file defining the HTTP gateway, the lambda function, and the definition of some ENV variables to authenticate with Google Spreadshseets:</p>
<pre><code class="language-yaml">service: event-service

frameworkVersion: &quot;&gt;=1.1.0 &lt;=2.65.0&quot;

provider:
  name: aws
  runtime: nodejs14.x
  environment: ${file(.env.yml):}
  stage: dev
  region: us-east-1
  lambdaHashingVersion:  &quot;20201221&quot;
functions:
  eventSubmission:
    handler: api/event.submit
    memorySize: 128
    description: Submit Event for saving
    events:
      - http: 
          path: event
          method: get

</code></pre>
<p>The other main file is the <code>event.js</code> file, which receives the HTTP gateway GET request and appends a new row in a Google Spreadsheet with<br>
the current date and time (in my timezone):</p>
<pre><code>&apos;use strict&apos;;
const { google } = require(&apos;googleapis&apos;);


let sheets;

module.exports.submit = async (event, context, callback) =&gt; {

 const auth = authorize();
 sheets = google.sheets({ version: &apos;v4&apos;, auth });
 writeToSheet();
   const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: &apos;Event registered successfully&apos;,
      input: event,
    }),
  };
  callback(null, response);
};

function authorize() {
  const oAuth2Client = new google.auth.GoogleAuth({
    credentials: {
      client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
      private_key: process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/gm, &apos;\n&apos;)
    },
    scopes: [&apos;https://www.googleapis.com/auth/spreadsheets&apos;],
  });
  return oAuth2Client;
} 


async function writeToSheet() {

  let dateTime = new Date().toLocaleString(&quot;es-MX&quot;, { timeZone: &apos;America/Mexico_City&apos;});

  return sheets.spreadsheets.values.append({
    spreadsheetId: process.env.GOOGLE_SPREADSHEET_ID,
    range: &apos;data&apos;,
    resource: {
      values: [dateTime.split(&apos; &apos;)]
    },
    insertDataOption: &apos;INSERT_ROWS&apos;,
    valueInputOption: &apos;RAW&apos;
  });
}
</code></pre>
<h1 id="theresult">The Result</h1>
<p>Once all of that is setup, we  get a timestamp added into the spreadsheet every time Garrita passes the door.<br>
<img src="https://i.ibb.co/yksVc4R/image.png" alt="spreadsheet" loading="lazy">.</p>
<p>This is the ESP32 board with the connected sensor:</p>
<h1 id="futurework">Future work</h1>
<p>There&apos;s a bunch of things that I have to do to improve this, among them:</p>
<ul>
<li>Implement a better noise reduction algorithm in the board so that I get less false positive events triggered. Right now, some shadow movements trigger a ping.</li>
<li>Remove the <a href="https://en.wikipedia.org/wiki/Fresnel_lens">Fresnel lens</a> that sits on top of the PIR sensor, to focus its field of view only to the point where the cat moves through the door.</li>
<li>Place the whole hardware into a plastic enclosure for better presentation.</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A Search Engine that favors Text Websites]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I learned about this amazing search engine <a href="https://search.marginalia.nu/">https://search.marginalia.nu/</a> that favours text/information heavy websites and punishes flashy Web2/Web3 sites. The results are quite astounding and signal heavy. I needed to save it for future reference.</p>
<!--kg-card-end: markdown-->]]></description><link>http://baqueiro.com/a-search-engine-for/</link><guid isPermaLink="false">62e80f0380883e0001faeeaa</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Thu, 16 Sep 2021 15:06:08 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I learned about this amazing search engine <a href="https://search.marginalia.nu/">https://search.marginalia.nu/</a> that favours text/information heavy websites and punishes flashy Web2/Web3 sites. The results are quite astounding and signal heavy. I needed to save it for future reference.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Using Spare Disk Space as One Large Disk]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>This is a neat trick in Linux for when you have a bit (some GB) in different disks and want to use it to save large files. I use this for mining XCH (Chia Cryptocoin).</p>
<ol>
<li>First check the available space on each disk using <code>df -h</code>:</li>
</ol>
<pre><code>Filesystem      Size  Used Avail</code></pre>]]></description><link>http://baqueiro.com/using-spare-disk-space-as-one-large-disk/</link><guid isPermaLink="false">62e80f0380883e0001faeea9</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Sun, 01 Aug 2021 05:09:25 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>This is a neat trick in Linux for when you have a bit (some GB) in different disks and want to use it to save large files. I use this for mining XCH (Chia Cryptocoin).</p>
<ol>
<li>First check the available space on each disk using <code>df -h</code>:</li>
</ol>
<pre><code>Filesystem      Size  Used Avail Use% Mounted on
/dev/sdg        4.6T  4.5T   84G  99% /media/omar/SEA_5T_B
/dev/sdp2        30T   30T   68G 100% /media/omar/seagate1

</code></pre>
<p>Here we have 2 disks, one with 84G and another with 68G. The size of the file we want to store is more than 100GB. To do that, we will first create &quot;image&quot; files using the remaining space in each disk:</p>
<pre><code>truncate -s67G /media/omar/seagate1/67G.img
truncate -s83G /media/omar/SEA_5T_B/83G.img
</code></pre>
<p>Then, we create a loopback device with <code>losetup</code> for each disk:</p>
<pre><code>losetup --show --find /media/omar/seagate1/67G.img
losetup --show --find /media/omar/SEA_5T_B/83G.img
</code></pre>
<p>We must pay attention to the data returned by those two previous commands, as they will tell us the name of the loopback device (e.g. <code>/dev/loop1</code>).</p>
<p>As a next step, we are going to use <code>btrfs</code> to create a <code>single</code> filesystem from those 2 devices:</p>
<p><code>sudo mkfs.btrfs -d single /dev/loop1 /dev/loop2</code><br>
(Replace <code>/dev/loop1</code> and <code>/dev/loop2</code> with whatever you got from the <code>losetup</code> step)</p>
<p>Finally, we can mount our btrfs filesystem in some directory like:</p>
<p><code>sudo mount -t btrfs -o ssd,nodatacow,nodatasum,discard,nodiratime,discard=async,noatime /dev/loop1 loops/</code></p>
<p>In my case I added all those options to make access faster. With btrfs you can mount a several-disk filesystem by referring to one of the devices (like <code>/dev/loop1</code> in this case)</p>
<p>After mounting this device, we do a <code>df -h</code> again and should see our new disk mounted in the specified directory with the summed amount of space:</p>
<pre><code>Filesystem      Size  Used Avail Use% Mounted on
/dev/loop1      150G  3.8M  148G   1% /media/omar/loops
</code></pre>
<p>And that&apos;s it! we&apos;ve got 150G to use for large files!</p>
<p>Some references:</p>
<ul>
<li><a href="https://unix.stackexchange.com/a/368186/14790">https://unix.stackexchange.com/a/368186/14790</a></li>
<li><a href="https://btrfs.wiki.kernel.org/index.php/Using_Btrfs_with_Multiple_Devices#Multiple_devices">https://btrfs.wiki.kernel.org/index.php/Using_Btrfs_with_Multiple_Devices#Multiple_devices</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Path to Kueski 2.0]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Back in the day (5 years ago) I wrote a good blog post related to a migration the Engineering team did from a monolithic infrastructure into a &quot;microservice based&quot; architecture.</p>
<p>The original URL is <a href="https://nerds.kueski.com/the-path-to-kueski-2-0/">this</a>, but I&apos;ve seen it has gone down a couple of times</p>]]></description><link>http://baqueiro.com/the-path-to-kueski-2-0/</link><guid isPermaLink="false">62e80f0380883e0001faeea8</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Fri, 11 Jun 2021 22:48:23 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Back in the day (5 years ago) I wrote a good blog post related to a migration the Engineering team did from a monolithic infrastructure into a &quot;microservice based&quot; architecture.</p>
<p>The original URL is <a href="https://nerds.kueski.com/the-path-to-kueski-2-0/">this</a>, but I&apos;ve seen it has gone down a couple of times in the last days so I saved a copy of it in archive.is:</p>
<p><a href="https://archive.is/sveOx">The Path to Kueski 2.0 (Archive)</a>.</p>
<p>Another good blog post that is worth saving, is the <a href="https://archive.is/qyoAM">Better Logging (Archive)</a> post by <a href="https://www.linkedin.com/in/jorge-delrio/">Jorge del Rio</a> which has good ideas about logging in production environments.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Web Sequence Digrams]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>A nice script to generate sequence diagrams in an HTML page:</p>
<p>Taken from: <a href="https://unpkg.com/js-sequence-diagrams-autorenderer@1.0.1/index.js">https://unpkg.com/js-sequence-diagrams-autorenderer@1.0.1/index.js</a></p>
<pre><code>// JS-sequence-diagrams auto-renderer; renders a document using JS-sequence-diagrams (https://bramp.github.io/js-sequence-diagrams/)
// See readme.md for usage instructions
// Pretty inefficient, but it works:
const a = document.createElement(&apos;</code></pre>]]></description><link>http://baqueiro.com/web-sequence-digrams/</link><guid isPermaLink="false">62e80f0380883e0001faeea6</guid><dc:creator><![CDATA[Omar Baqueiro Espinosa]]></dc:creator><pubDate>Tue, 27 Apr 2021 23:00:32 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>A nice script to generate sequence diagrams in an HTML page:</p>
<p>Taken from: <a href="https://unpkg.com/js-sequence-diagrams-autorenderer@1.0.1/index.js">https://unpkg.com/js-sequence-diagrams-autorenderer@1.0.1/index.js</a></p>
<pre><code>// JS-sequence-diagrams auto-renderer; renders a document using JS-sequence-diagrams (https://bramp.github.io/js-sequence-diagrams/)
// See readme.md for usage instructions
// Pretty inefficient, but it works:
const a = document.createElement(&apos;script&apos;); a.setAttribute(&apos;src&apos;, &apos;https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js&apos;);
document.head.appendChild(a);
a.onload = () =&gt; {
  const b = document.createElement(&apos;script&apos;); b.setAttribute(&apos;src&apos;, &apos;https://cdnjs.cloudflare.com/ajax/libs/raphael/2.3.0/raphael.min.js&apos;);
  document.head.appendChild(b);
  b.onload = () =&gt; {
    const c = document.createElement(&apos;script&apos;); c.setAttribute(&apos;src&apos;, &apos;https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js&apos;);
    document.head.appendChild(c);
    c.onload = () =&gt; {
      const d = document.createElement(&apos;script&apos;); d.setAttribute(&apos;src&apos;, &apos;https://cdnjs.cloudflare.com/ajax/libs/js-sequence-diagrams/1.0.6/sequence-diagram-min.js&apos;);
      document.head.appendChild(d);
      d.onload = () =&gt; {
        const diagram = Diagram.parse(document.body.textContent);
        document.body.textContent = &quot;&quot;;
        const x = document.createElement(&apos;div&apos;);
        document.body.appendChild(x);
        diagram.drawSVG(x, {theme: &apos;simple&apos;});
      }
    }
  }
}
</code></pre>
<p>As an example usage, paste this into a file <code>test.html</code> and open it in your browser:</p>
<pre><code>Jon-&gt;Ygritte: Do you remember the cave?
Note right of Ygritte: Long silence...
Ygritte--&gt;Jon: We should have stayed in that cave
Jon-&gt;Ygritte: We&apos;ll go back there.
Ygritte--&gt;Jon: You know nothing Jon Snow


# Rendered using JS-sequence-diagrams (https://bramp.github.io/js-sequence-diagrams/) by the line below:
# &lt;script src=&quot;https://unpkg.com/js-sequence-diagrams-autorenderer@1.0.1/index.js&quot;&gt;&lt;/script&gt;
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>