<?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: Cosimo Streppone</title>
    <description>The latest articles on DEV Community by Cosimo Streppone (@cosimo).</description>
    <link>https://dev.to/cosimo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F393735%2Fee7ada2d-5ea9-4b61-ba60-eaf633b1a685.png</url>
      <title>DEV Community: Cosimo Streppone</title>
      <link>https://dev.to/cosimo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cosimo"/>
    <language>en</language>
    <item>
      <title>On the Oct 19th AWS us-east-1 outage</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Fri, 31 Oct 2025 07:05:36 +0000</pubDate>
      <link>https://dev.to/cosimo/on-the-oct-19th-aws-us-east-1-outage-2bc0</link>
      <guid>https://dev.to/cosimo/on-the-oct-19th-aws-us-east-1-outage-2bc0</guid>
      <description>&lt;p&gt;These will be personal comments on the text that AWS put out after the October 19th 2025 us-east-1 outage, which you can find here: &lt;a href="https://aws.amazon.com/message/101925/" rel="noopener noreferrer"&gt;https://aws.amazon.com/message/101925/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Important premises before starting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;While I’ve been working on web operations for a long time, I have never dealt with services as big as AWS, and also I don’t know anything about how they operate internally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I have the utmost respect for AWS SREs and engineers that had to deal with this outage, so this is in no way intended to downplay the quality of the services or the recovery work done there. On the contrary…&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s start.&lt;/p&gt;

&lt;p&gt;At Kahoot!, the first signal that something was wrong was Slack being slow or unresponsive in our, Central Europe, morning. We didn’t know that AWS or the us-east-1 region was involved in any of it. As we realized Slack was not operating correctly, a few of us sent test messages in our backup channel on a Google Chat room &lt;em&gt;“&lt;/em&gt;? &lt;em&gt;SRE Team”&lt;/em&gt;, used a handful of times over a few years. It can be cumbersome to establish a backup channel when Slack is suddenly down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 1&lt;/strong&gt;: establish and document your backup comms channel if and when the primary fails.&lt;/p&gt;

&lt;p&gt;In my case, I had another problem. My Firefox install had started acting up, in the special way Firefox fails when you have used snap to install it. I will spare you my thoughts on snap itself, none of which are positive. After 15 minutes of head scratching, I realized the issue might be with Firefox and not related to the AWS outage, and restarted my browser.&lt;/p&gt;

&lt;p&gt;The impact on our infrastructure has been minimal. We’ve seen a few AWS API calls fail, but essentially nothing else. Our EC2 instances and AutoScalingGroups in us-east-1 have been up and running with no issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s always DNS! Except when it isn’t…
&lt;/h2&gt;

&lt;p&gt;Many cited the &lt;em&gt;“It’s always DNS!”&lt;/em&gt; meme. My guess is that they probably haven’t read the AWS text. This was not a DNS failure. It was the system AWS designed to update those “hundreds of thousands” of DynamoDB DNS records, that failed due to a race condition. More on that below.&lt;/p&gt;

&lt;p&gt;Is that a sign that few people take the time to read things through?&lt;/p&gt;

&lt;h2&gt;
  
  
  Outage text commentary follows
&lt;/h2&gt;

&lt;p&gt;If you haven’t, I’d suggest reading &lt;a href="https://surfingcomplexity.blog/2025/10/25/quick-thoughts-on-the-recent-aws-outage/" rel="noopener noreferrer"&gt;Lorin Hochstein’s blog about the outage&lt;/a&gt;. I won’t be mentioning any of Lorin’s points here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Engineering teams for impacted AWS services were immediately engaged and began to investigate. By 12:38 AM on October 20, our engineers had identified DynamoDB’s DNS state as the source of the outage.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;11:48 PM to 12:38 AM means 50 minutes from when the issue started to detection. That seems … quite a lot of time. I don’t know the details, of course. My guess is that DynamoDB is so core to most AWS services and so reliable that it’s hard to imagine it could have issues. This is also confirmed by the fact that “key internal tooling” depends on DynamoDB, meaning it must be very rare that it’d be down or unavailable.&lt;/p&gt;

&lt;p&gt;Makes me think of those times when some component, script or cronjob that had been reliably working for years, seems to be the one failing. You’re thinking: “No way, it can’t be THAT! It’s been working perfectly fine for at least 5 years!”. And yet, something this time has changed that caused the failure. Such cases happen. I’ve been smacked in the face a few times :-)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;By 2:25 AM, all DNS information was restored, and all global tables replicas were fully caught up by 2:32 AM. Customers were able to resolve the DynamoDB endpoint and establish successful connections as cached DNS records expired between 2:25 AM and 2:40 AM.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can conclude that no DNS records for us-east-1 DynamoDB endpoints were available between 11:48 PM and (partially) 2:40 AM. The DynamoDB endpoint hostname for the &lt;code&gt;us-east-1&lt;/code&gt; region is &lt;code&gt;dynamodb.us-east-1.amazonaws.com&lt;/code&gt;. Negative DNS lookups, which I take to be &lt;code&gt;NXDOMAIN&lt;/code&gt; responses in this case, are to be cached by resolvers. This can be a problematic if the TTL for such negative responses is high. In case of the DynamoDB regional DNS zone, this is currently set to 5 (five) seconds.&lt;/p&gt;

&lt;p&gt;To understand this, let’s look at the SOA record for the us-east-1 DNS zone, which controls how long DNS responses are cached for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dig us-east-1.amazonaws.com SOA +noall +answer
us-east-1.amazonaws.com. 579 IN SOA dns-external master.amazon.com. root.amazon.com. 22365 180 60 2592000 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last number in the SOA record is the minimum TTL, &lt;a href="https://www.rfc-editor.org/rfc/rfc2308" rel="noopener noreferrer"&gt;used as TTL for negative responses&lt;/a&gt;. Hence any lookup for &lt;code&gt;dynamodb.us-east-1.amazonaws.com&lt;/code&gt; that returned an &lt;code&gt;NXDOMAIN&lt;/code&gt; response (record not existing) would be cached for just 5 seconds. I’m wondering whether AWS have changed this value after the outage, as otherwise the time for clients to recover during the incident would have been much shorter. Wondering what sort of loads this imposes on their DNS serving infrastructure…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 2&lt;/strong&gt;: if you have particularly critical services, verify that the negative DNS response TTL you advertise in your SOA records is appropriately set, so that clients can recover quickly when DNS records are restored. Five seconds might be a bit extreme for anyone except huge companies, also because it can impose a tremendous load on the DNS infrastructure. Something like 60s might be more appropriate for mere mortals. YMMV.&lt;/p&gt;

&lt;h2&gt;
  
  
  EC2
&lt;/h2&gt;

&lt;p&gt;DynamoDB being the backbone of many internal AWS services meant that EC2 was also impacted. I won’t reiterate here how or why. Instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Existing EC2 instances that had been launched prior to the start of the event remained healthy and did not experience any impact for the duration of the event.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s what we saw. Our existing EC2 instances in us-east-1 kept running with no issues. We were also “lucky” that our AutoScalingGroups didn’t initiate any scale-in or scale-out events, being night time in us-east-1. Those would have probably failed, at least the scale-out ones, as launching new instances was one of the impacted operations.&lt;/p&gt;

&lt;p&gt;Other more “complex” services were impacted. Our AWS usage is relatively basic, we don’t use esoteric services or configurations, so we weren’t affected by the EC2 issues during the outage. Keeping things simple has been advantageous in this case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 3&lt;/strong&gt;: Simple, “boring” infrastructure choices can be surprisingly resilient. Complex service configurations and dependencies increase your surface area for cascading failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  DropletWorkflow Manager
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Each DWFM manages a set of droplets within each Availability Zone and maintains a lease for each droplet currently under management. This lease allows DWFM to track the droplet state, ensuring that all actions from the EC2 API or within the EC2 instance itself, such as shutdown or reboot operations originating from the EC2 instance operating system, result in the correct state changes within the broader EC2 systems.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As part of maintaining this lease, each DWFM host has to check in and complete a state check with each droplet that it manages every few minutes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting at 11:48 PM PDT on October 19, these DWFM state checks began to fail as the process depends on DynamoDB and was unable to complete.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While this did not affect any running EC2 instance, it did result in the droplet needing to establish a new lease with a DWFM before further instance state changes could happen for the EC2 instances it is hosting.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Between 11:48 PM on October 19 and 2:24 AM on October 20, leases between DWFM and droplets within the EC2 fleet slowly started to time out.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At 2:25 AM PDT, with the recovery of the DynamoDB APIs, DWFM began to re-establish leases with droplets across the EC2 fleet. Since any droplet without an active lease is not considered a candidate for new EC2 launches, the EC2 APIs were returning “insufficient capacity errors” for new incoming EC2 launch requests.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This description of the inner workings of the &lt;em&gt;DropletWorkflow Manager&lt;/em&gt; is quite fascinating. From my point of view, DWFM was designed really well to effectively fail in such a graceful way. Being defensive is a useful trait in systems design. This is an excellent example of “failing open” design philosophy. The system degraded gracefully rather than causing widespread instance failures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;After attempting multiple mitigation steps, at 4:14 AM engineers throttled incoming work and began selective restarts of DWFM hosts to recover from this situation. Restarting the DWFM hosts cleared out the DWFM queues, reduced processing times, and allowed droplet leases to be established.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The oldest trick in the book! A well-placed server restart can help :-) I did this regularly years ago, then started to prefer trying to understand the actual failure at hand, before kicking the server and thus sometimes preventing further observations to understand the cause of the fault. Sometimes it’s still a viable way to get out of trouble, even in AWS apparently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Network Load Balancers
&lt;/h2&gt;

&lt;p&gt;NLBs being based on EC2 instances, they were impacted by the outage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Our monitoring systems detected this at 6:52 AM, and engineers began working to remediate the issue.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means 80 minutes from the first NLB issues to detection at the monitoring layer. That’s an indication that the recovery work was either quite challenging, or just that it took that long to notice. It would be again very interesting to know what exactly happened during that time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other AWS Services
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;By 2:24 AM, service operations recovered except for SQS queue processing, which remained impacted because an internal subsystem responsible for polling SQS queues failed and did not recover automatically. We restored this subsystem at 4:40 AM and processed all message backlogs by 6:00 AM.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One aspect I haven’t seen mentioned anywhere else is the fact that with all these different subsystems, we can assume &lt;strong&gt;many&lt;/strong&gt; SREs must have been on deck to deal with this outage. Given that, the coordination work will have been absolutely massive and critical as well. No doubt it would have been extremely fascinating to observe how this coordination went on, and how the different teams communicated and collaborated to get things back up and running. Maybe it was a single team of three-five people instead? It was night time in US, so who knows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Inbound callers experienced busy tones, error messages, or failed connections. Both agent-initiated and API-initiated outbound calls failed. Answered calls experienced prompt playback failures, routing failures to agents, or dead-air audio.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was the first time I read the term “dead-air audio”. A &lt;a href="https://en.wikipedia.org/wiki/Dead_air" rel="noopener noreferrer"&gt;detour to Wikipedia&lt;/a&gt; was definitely worth it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Customers with IAM Identity Center configured in N. Virginia (us-east-1) Region were also unable to sign in using Identity Center.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortunately, our IAM Identity Center is in a different region, so we weren’t impacted there either. I certainly don’t envy teams who were shut off from access to the AWS console. Our observability systems also weren’t affected, but I can see how losing console access and perhaps also losing your observability platform could be a completely paralyzing situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 4&lt;/strong&gt;: Spend some time pondering how not only your own systems, but also the 3rd party systems you depend on would react to an AWS outage. Would they make you unable to react? Can you do something about it?&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Finally, as we continue to work through the details of this event across all AWS services, we will look for additional ways to avoid impact from a similar event in the future, and how to further reduce time to recovery.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AWS message clearly wasn’t meant to be a post-mortem. With that said, there are zero mentions of a human element anywhere in the outage. Perhaps because the DNS automation was … automation, and no manual intervention caused the issues. I would have appreciated learning more about the human factors involved in any case. For example, what challenges the teams faced in identifying that DynamoDB DNS records were missing? Perhaps this is more material for an AWS-internal post-mortem, that people might still be working on.&lt;/p&gt;

&lt;p&gt;Multiple DNS Enactor instances applying potentially outdated plans simultaneously seems (in hindsight, clearly) quite risky? It’s always easy to criticize after the fact, but since we’re missing such a huge amount of context and background, the only thing we can do is speculate and also learn from this, as much as we can. If you have, or know where to get more insights into AWS internals, reach out and let me know!&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.streppone.it/cosimo/blog/2025/10/on-the-oct-19th-aws-us-east-1-outage/" rel="noopener noreferrer"&gt;On the Oct 19th AWS us-east-1 outage&lt;/a&gt; appeared first on &lt;a href="https://www.streppone.it/cosimo/blog" rel="noopener noreferrer"&gt;Random hacking&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>operations</category>
      <category>aws</category>
      <category>outage</category>
      <category>webops</category>
    </item>
    <item>
      <title>How to pin a specific apt package version</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Mon, 10 Feb 2025 09:59:06 +0000</pubDate>
      <link>https://dev.to/cosimo/how-to-pin-a-specific-apt-package-version-1jbp</link>
      <guid>https://dev.to/cosimo/how-to-pin-a-specific-apt-package-version-1jbp</guid>
      <description>&lt;p&gt;I’d like to pin a specific package, say &lt;code&gt;redis-server&lt;/code&gt;, to a specific version, in my case &lt;code&gt;7.0.*&lt;/code&gt;, and that seems straight-forward to do with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package: redis-server
Pin: version 7.0.*
Pin-Priority: 1001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I would also like to have &lt;code&gt;apt&lt;/code&gt; &lt;strong&gt;fail&lt;/strong&gt; when 7.0.* is not available, either because there are only newer versions available, f.ex. 7.2.* or 7.4.* or perhaps because only older versions like 6.* are available.&lt;/p&gt;

&lt;p&gt;I can’t seem to find a way to achieve that. I’ve read various resources only, consulted &lt;code&gt;man 5 apt_preferences&lt;/code&gt;, but I’m still not sure how to.&lt;/p&gt;

&lt;p&gt;I tried combining the previous pinning rule to another one with priority &lt;code&gt;-1&lt;/code&gt;, as in the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package: redis-server
Pin: release *
Pin-Priority: -1

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

&lt;/div&gt;



&lt;p&gt;But that seems to make all versions unavailable unfortunately. Here’s what I’m seeing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ apt-cache policy redis-server
redis-server:
  Installed: (none)
  Candidate: 5:7.0.15-1build2
  Version table:
     5:7.0.15-1build2 500
        500 [http://no.archive.ubuntu.com/ubuntu](http://no.archive.ubuntu.com/ubuntu) noble/universe amd64 Packages

$ cat &amp;gt; /etc/apt/preferences.d/redis-server
Package: redis-server
Pin: version 7.0.15*
Pin-Priority: 1001

Package: redis-server
Pin: release *
Pin-Priority: -1

$ apt-cache policy redis-server
redis-server:
  Installed: (none)
  Candidate: (none)
  Version table:
     5:7.0.15-1build2 -1
        500 [http://no.archive.ubuntu.com/ubuntu](http://no.archive.ubuntu.com/ubuntu) noble/universe amd64 Packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I expected this configuration to provide an available candidate, since one exists (7.0.15), but that doesn’t work.&lt;/p&gt;

&lt;p&gt;Note that a successful outcome for me is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define a target wanted version, f.ex. &lt;code&gt;redis-server=7.0.*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;provide an apt preferences.d file such that:

&lt;ul&gt;
&lt;li&gt;when any 7.0.* versions are available, apt will install that version&lt;/li&gt;
&lt;li&gt;when no 7.0.* versions are available, apt will &lt;strong&gt;fail&lt;/strong&gt; installing nothing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;A bad outcome is when redis-server is installed, but with a package version that &lt;strong&gt;does not&lt;/strong&gt; match what I had specified as requirement (hence, different from 7.0.*).&lt;/p&gt;

&lt;p&gt;This is on Ubuntu 24.04, although there is nothing specific to 24.04 or Ubuntu here I would think.&lt;/p&gt;

&lt;p&gt;Any ideas?&lt;/p&gt;

&lt;p&gt;Posted on stackoverflow, let’s see! &lt;a href="https://unix.stackexchange.com/questions/790837/how-to-pin-an-apt-package-to-a-version-and-fail-if-its-not-available" rel="noopener noreferrer"&gt;https://unix.stackexchange.com/questions/790837/how-to-pin-an-apt-package-to-a-version-and-fail-if-its-not-available&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; based on the stackoverflow feedback, it seems that the solution wasn’t far off.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package: redis-server
Pin: version 5:7.0.15*
Pin-Priority: 1001

Package: redis-server
Pin: release *
Pin-Priority: -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I needed to prepend the version with the &lt;code&gt;"5:&lt;/code&gt;“.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.streppone.it/cosimo/blog/2025/02/how-to-pin-a-specific-apt-package-version/" rel="noopener noreferrer"&gt;How to pin a specific apt package version&lt;/a&gt; appeared first on &lt;a href="https://www.streppone.it/cosimo/blog" rel="noopener noreferrer"&gt;Random hacking&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>apt</category>
      <category>debian</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>TIL: Styling Obsidian text paragraphs</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Mon, 23 Dec 2024 12:15:12 +0000</pubDate>
      <link>https://dev.to/cosimo/til-styling-obsidian-text-paragraphs-54ak</link>
      <guid>https://dev.to/cosimo/til-styling-obsidian-text-paragraphs-54ak</guid>
      <description>&lt;p&gt;TIL that it’s possible to style &lt;a href="https://obsidian.md/" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt; text paragraphs in a way that allows me to focus on the actual text content and not on the fact that I need to add an artificial line break every time I type a paragraph :-)&lt;/p&gt;

&lt;p&gt;Obsidian can use CSS snippets to style the application itself and the text/markdown content. The CSS snippets need to be saved in &lt;code&gt;&amp;lt;vault_directory&amp;gt;/snippets/whatever.css&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
This is how to get that “natural” book-like spacing between paragraphs, and avoid adding spurious line breaks in the markdown code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.cm-contentContainer {
  line-height: 1.70rem;
}

.markdown-source-view.mod-cm6 .cm-content &amp;gt; .cm-line {  
  padding-bottom: 12px !important;  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, the values for main line-height and padding will depend on your particular screen and font settings. In my case I use a screen tilted in vertical position for writing and coding, and my font of choice is the beautiful Berkeley Graphics’s &lt;a href="https://berkeleygraphics.com/typefaces/berkeley-mono/" rel="noopener noreferrer"&gt;Berkeley Mono&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>development</category>
      <category>todayilearned</category>
      <category>css</category>
      <category>obsidian</category>
    </item>
    <item>
      <title>My experience at SREcon EMEA 2022</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Mon, 07 Nov 2022 10:36:10 +0000</pubDate>
      <link>https://dev.to/cosimo/my-experience-at-srecon-emea-2022-188i</link>
      <guid>https://dev.to/cosimo/my-experience-at-srecon-emea-2022-188i</guid>
      <description>&lt;p&gt;A few weeks ago I attended &lt;a href="https://www.usenix.org/conference/srecon22emea/"&gt;SREcon&lt;/a&gt; in Amsterdam. Here’s some sparse thoughts about it, with no pretense of being exhaustive or coherent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Back
&lt;/h2&gt;

&lt;p&gt;There is only a handful of conferences I attended where I felt “at home”. Going back in time, &lt;a href="http://www.streppone.it/cosimo/blog/2010/10/surge-2010-scalability-conference-in-baltimore-usa-day-2/"&gt;Surge&lt;/a&gt; was the first one, then came &lt;a href="http://www.streppone.it/cosimo/blog/2012/01/my-experience-at-velocity-europe-2011-in-berlin/"&gt;Velocity&lt;/a&gt;. I’m adding &lt;a href="https://www.usenix.org/conference/srecon22emea/"&gt;SREcon&lt;/a&gt; to that list. It definitely felt like I was among people that speak the same language and have similar breadth and depth of expertise, and yet it is somewhat strange at the same time.&lt;/p&gt;

&lt;p&gt;As I see it, there’s at least three “tiers” for such a big and niche conference. The FAANG folks, the tiny company with a sysadmin or devops or two, and then the big ocean of mid-sized companies, where people like us are. Our SRE team is four people and we manage a service with millions of monthly users. Needless to say, we have a lot on our plate :-)&lt;/p&gt;

&lt;p&gt;I came to SREcon after a hiatus from conferences for some years. After a while, conferences tend to become self-referential and people start talking about the same things over and over again. I wanted to understand how things had changed in our field, what were people talking the most about, get some fresh perspectives and perhaps connect with people from other companies. What prompted me to do this was &lt;a href="https://youtu.be/7Ktzu0qvS6c?t=771"&gt;Niall Murphy tearing the SRE bible book apart.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Question of SRE Identity
&lt;/h2&gt;

&lt;p&gt;This year’s &lt;a href="https://www.usenix.org/conference/srecon22emea/call-for-participation"&gt;conference topic&lt;/a&gt; was &lt;strong&gt;&lt;em&gt;“What could SRE be?”&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
No surprise, then, that a good portion of the talks were about what I refer to as &lt;em&gt;the question of identity for SREs&lt;/em&gt;. We have seen the same happen — and a lot more during all these years — for the DevOps movement.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What could SRE be&lt;/em&gt;, then? According to some presentations, one would conclude that whatever SRE is, it’s no longer what Google intended, it’s not what anyone else thinks it is either, it’s just what &lt;strong&gt;you&lt;/strong&gt; think it is: a subjectivist view.&lt;/p&gt;

&lt;p&gt;Among the Usenix slack conversations, there was a lot of chit-chat about SRE identity. My personal contribution was the following meme:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/6ygfxs.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gaidoYFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/6ygfxs.jpg" alt="" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other funny memes that were shared:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/image_from_ios.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L_4PYLxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/image_from_ios-181x300.jpg" alt="" width="181" height="300"&gt;&lt;/a&gt; &lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/image.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--llU0TVsJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/image-300x300.png" alt="" width="300" height="300"&gt;&lt;/a&gt; &lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/image-1.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pWyRi5Xo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/image-1-300x296.png" alt="" width="300" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An interesting fact I learned during the conference is that the &lt;a href="https://sre.google/sre-book/table-of-contents/"&gt;Google SRE book&lt;/a&gt; was written by &lt;a href="https://www.linkedin.com/feed/update/urn:li:ugcPost:6996742200055197697?updateEntityUrn=urn%3Ali%3Afs_updateV2%3A%28urn%3Ali%3AugcPost%3A6996742200055197697%2CFEED_DETAIL%2CEMPTY%2CDEFAULT%2Cfalse%29"&gt;assembling contributions from the best teams at Google&lt;/a&gt;, picking out their respective best practices. Paradoxically, this implies that &lt;strong&gt;the SRE book is not representative of how even Google itself does SRE&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt;If you also consider that, at the time the SRE book was published (2016), Google employed about 1,200 people in the various SRE teams, the only possible conclusion is…&lt;strong&gt;if you are not Google, there is &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/suriar-chocolate"&gt;likely very little that you can apply to your everyday&lt;/a&gt; mere-mortal-SRE life.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before you think I’m exaggerating, such conclusion was claimed by (ex-)Google engineers themselves, for example in &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/hidalgo"&gt;Alex Hidalgo’s “Diamonds under Pressure” talk&lt;/a&gt; and (in my opinion) in one of the best talks of the conference, &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/stolarsky"&gt;Emil Stolarsky’s Unified Theory of SRE&lt;/a&gt;. Another entertaining presentation in the same vein was &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/shafer"&gt;Andrew Clay Shafer’s “SRE as She Is Spoke”&lt;/a&gt;. Andrew expressed this thesis that &lt;em&gt;“progress [on the SRE journey] stops when the needs are met”&lt;/em&gt;, which seems a reasonable and pragmatic approach.&lt;br&gt;&lt;br&gt;
The videos are not up yet, but they should be in a few weeks.&lt;/p&gt;

&lt;p&gt;Alongside to the “subjectivist” view, there were other talks, which could be classified as &lt;em&gt;systems thinking&lt;/em&gt;, that focused on the more general and broad aspects of what SREs do, how to handle complex systems, human factors, etc… Among the best IMO were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first plenary session on Day 1, &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/maguire"&gt;“Knowledge and Power: A Sociotechnical Systems Discussion on the Future of SRE”&lt;/a&gt; by Laura Maguire and Lorin Hochstein. I’ve been following &lt;a href="https://surfingcomplexity.blog/"&gt;Lorin’s excellent blog, Surfing Complexity&lt;/a&gt; for some years now.&lt;/li&gt;
&lt;li&gt;Laura Nolan’s &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/nolan-sre"&gt;“What SRE Could Be: Systems Reliability Engineering”&lt;/a&gt;. Lots of material to go deeper into systems thinking. This was brilliant, I think Laura raised the level of the conversation with this talk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What else?
&lt;/h2&gt;

&lt;p&gt;The question of SRE identity accounted for a notable part of the talks, but thankfully not all. It’s good to pause and reflect on our role, but personally that’s not why I was interested in SREcon, not primarily at least. What I like are the deep technical talks, where I get to know more about how other companies &lt;strong&gt;actually do the stuff we call SRE.&lt;/strong&gt; Given my past conference experience, I expected &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/janardhan"&gt;Facebook/Meta’s talk to be somewhat disappointing&lt;/a&gt;, and it was. While some details of how Meta is structured were shared, and are always interesting, I expected a bit more on how the incident actually happened.&lt;/p&gt;

&lt;p&gt;I loved Effie Mouzeli’s talk on how to make teams resilient, &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/mouzeli"&gt;“Is Our Team as Resilient as Our Systems?”&lt;/a&gt;. We naturally focus on systems, but &lt;strong&gt;teams are a crucial part of the equation&lt;/strong&gt;. My team and I have had to work on this a lot in the past years, and I’m hoping to share more about this soon. I felt this talk had a lot of good insights, some of which we’ve also applied over time.&lt;/p&gt;

&lt;p&gt;Another talk that deserves a mention is &lt;a href="https://www.usenix.org/conference/srecon22emea/presentation/sinjakli"&gt;Chris Sinjakli’s reflection&lt;/a&gt; on broadening the scope of how we work on reliability for our systems. This is sometimes difficult to do when toil is a big part of our jobs. Luckily it’s not for our team, not anymore at least, so this talk felt very relevant to me, and I recommend it.&lt;/p&gt;

&lt;p&gt;I couldn’t attend some of the talks due to the two parallel tracks. I hope to catch-up when slides and videos will be published later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about the hallway track?
&lt;/h2&gt;

&lt;p&gt;In general, people say that conferences are most useful because of the casual conversations you can have in the hallways. While I somewhat agree with that, the opportunities to have conversations vary depending on the type of person you are, and the people you meet, of course. My impression is that while some people at SREcon were happy to have conversations, most were likewise happy to be left alone, which is fair enough :-)&lt;br&gt;&lt;br&gt;
Just to say that it was really nice to meet people and chat, and almost all I talked to knew &lt;a href="https://kahoot.com"&gt;Kahoot!&lt;/a&gt; directly and were happy to share details about what they’re doing and equally interested in what we’re doing.&lt;/p&gt;

&lt;p&gt;In some of these conversations I’ve been trying to motion for more concrete, down to earth, talks on how smaller companies like ours do SRE. It’s ok to aspire or be interested in how Google runs, but you come away with absolutely zero information that’s useful to your work life. Possibly there’s a downside even: people going home thinking they have to do whatever Google does (see chapters above) so ultimately… &lt;strong&gt;let’s give less importance to the Googles of the world, please!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Besides the hallway track, there was a nice “sidewalk” track. We walked around the city, 15 km a day on average — you gotta track those SLOs… — and I also managed to snap some nice pictures of Amsterdam at sunrise and sunset.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/DSC_0723-small.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Zpew1tu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/DSC_0723-small-1024x630.jpg" alt="" width="880" height="541"&gt;&lt;/a&gt;&lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/DSC_0884-HDR-small.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vuvgle64--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/DSC_0884-HDR-small-1024x684.jpg" alt="" width="880" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Venue and Organization
&lt;/h2&gt;

&lt;p&gt;Loved all of it, honestly the best conference I’ve ever been to. The venue was spectacular, there was plenty of space, slides were clearly visible on screen, and the food was awesome! We also used one of the available meeting rooms to participate in our own company hackaton after the conference finished, until they kicked us out. Here’s a sneak peek of what our team was working on:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/kahoot-earth-day-night.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n4PT0OpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.streppone.it/cosimo/blog/wp-content/uploads/2022/11/kahoot-earth-day-night-1024x761.png" alt="" width="880" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope to return to SREcon next year in Dublin. By then, I’d love to see more not-Google, not-Meta, etc… talks on the program. Perhaps we (or you!) should think about presenting too, why not?&lt;/p&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>srecon</category>
    </item>
    <item>
      <title>Failed to connect to the host via SSH on Ubuntu 22.04</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Wed, 05 Oct 2022 16:02:48 +0000</pubDate>
      <link>https://dev.to/cosimo/failed-to-connect-to-the-host-via-ssh-on-ubuntu-2204-3odl</link>
      <guid>https://dev.to/cosimo/failed-to-connect-to-the-host-via-ssh-on-ubuntu-2204-3odl</guid>
      <description>&lt;p&gt;If you have just upgraded to Ubuntu 22.04, and you suddenly experience either errors when trying to ssh into hosts, or when running &lt;a href="https://docs.ansible.com/ansible/latest/index.html"&gt;ansible&lt;/a&gt; or again when running the ansible provisioner building a &lt;a href="https://packer.io"&gt;packer&lt;/a&gt; image, this is probably going to be useful for you.&lt;/p&gt;

&lt;p&gt;In my case I was trying to build an AWS EC2 image via packer and the ansible provisioner, and I had this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;amazon-ebs.aws: Failed to connect to the host via ssh: Unable to negotiate with 127.0.0.1 port
amazon-ebs.aws: 40015: no matching host key type found. Their offer: ssh-rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your problem is that you simply can't connect via SSH to a host from your Ubuntu 22.04 host, then &lt;a href="https://duckduckgo.com/?q=ssh+PubkeyAcceptedKeyTypes+ssh-rsa"&gt;look it up&lt;/a&gt;, there are a lot of people in the same boat.&lt;/p&gt;

&lt;p&gt;The proposed solution is to add this snippet to either your &lt;code&gt;/etc/ssh/ssh_config&lt;/code&gt; or &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PubkeyAcceptedKeyTypes +ssh-rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or just for some specific hosts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host host.example.com
    PubkeyAcceptedKeyTypes +ssh-rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the case of ansible connecting to a host, or packer launching ansible connecting to a host, this needs an additional step or two.&lt;/p&gt;

&lt;p&gt;For ansible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible --ssh-extra-args="-o PubkeyAcceptedKeyTypes=+ssh-rsa"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For packer with ansible provisioning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build {
  sources = ["sources.amazon-ebs.aws"]
  provisioner "ansible" {
    ansible_env_vars = [
      ...
      "ANSIBLE_SSH_ARGS='-o PubkeyAcceptedKeyTypes=+ssh-rsa -o HostkeyAlgorithms=+ssh-rsa'"
    ]
    playbook_file       = "..."
    galaxy_file         = "..."
    ...
    extra_arguments     = "${concat(local.default_ansible_extra_args, var.ansible_extra_args)}"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Background info on the cause of this issue: &lt;a href="https://ikarus.sg/rsa-is-not-dead/"&gt;https://ikarus.sg/rsa-is-not-dead/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope I don't need to come back to this for a while :-)&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>packer</category>
      <category>ubuntu</category>
      <category>devops</category>
    </item>
    <item>
      <title>Text Clustering using Deep Learning language models</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Wed, 04 May 2022 09:15:26 +0000</pubDate>
      <link>https://dev.to/cosimo/text-clustering-using-deep-learning-language-models-15nm</link>
      <guid>https://dev.to/cosimo/text-clustering-using-deep-learning-language-models-15nm</guid>
      <description>&lt;p&gt;I had a ton of fun working on this small project, involving nlp, the most classic clustering algorithm of all (k-means), and a touch of deep learning.&lt;/p&gt;

&lt;p&gt;It all started with the thought: what if... we could do X? How would we do it? What it would take to do it? "Doing it" involved a lot of research and learning on my part, on NLP but also on how to ship such big models to production and make them work reasonably fast.&lt;/p&gt;

&lt;p&gt;Some of that experience I have &lt;a href="https://dev.to/cosimo/deploying-large-deep-learning-models-in-production-goe"&gt;already written about&lt;/a&gt;, but this new article just published today explains the more high-level view of what was done and how.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kahoot.com/tech-blog/text-clustering-using-deep-learning-language-models/"&gt;https://kahoot.com/tech-blog/text-clustering-using-deep-learning-language-models/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>machinelearning</category>
      <category>clustering</category>
      <category>nlp</category>
    </item>
    <item>
      <title>Deploying Large Deep Learning Models in Production</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Thu, 26 Aug 2021 19:07:27 +0000</pubDate>
      <link>https://dev.to/cosimo/deploying-large-deep-learning-models-in-production-goe</link>
      <guid>https://dev.to/cosimo/deploying-large-deep-learning-models-in-production-goe</guid>
      <description>&lt;p&gt;Most deep learning or machine learning (ML) articles and tutorials focus on how to build, train and evaluate a model. The model deployment stage is rarely covered in detail, even though it is just as important if not fundamental part of a ML system. In other words, how do we take a working ML model from a jupyter notebook to a production ML-powered API?&lt;/p&gt;

&lt;p&gt;I hope more and more practitioners will cover the deployment aspect of ML models. For now, I can offer my own experience about how I approached this problem, hoping this will be useful to some of you out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a useful ML model
&lt;/h2&gt;

&lt;p&gt;How to create a useful ML model is the part of the work I won’t cover in this post. :-)&lt;/p&gt;

&lt;p&gt;I assume that you already have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a model or pipeline that is either pre-trained or that you have trained yourself&lt;/li&gt;
&lt;li&gt;a model based on PyTorch, though most of the information here will probably help with any ML framework&lt;/li&gt;
&lt;li&gt;some idea on how to make your model available as a RESTful API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  First step: defining a simple API
&lt;/h2&gt;

&lt;p&gt;The rest of this article will use Python as a programming language, for various reasons, the most important being that the ML model is based on PyTorch. In my specific case, the problem I worked on was text clustering.&lt;/p&gt;

&lt;p&gt;Given a set of sentences, the API should output a list of clusters. A cluster is a group of sentences that have a similar meaning, or as similar as possible. This task is usually referred to with the term &lt;em&gt;“semantic similarity”&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
Here’s an example. Given the sentences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Dog Walking: 10 Simple Steps”&lt;/li&gt;
&lt;li&gt;“The Secrets of Dog Walking”&lt;/li&gt;
&lt;li&gt;“Why You Need To Dog Walking”&lt;/li&gt;
&lt;li&gt;“The Art of Dog Walking”&lt;/li&gt;
&lt;li&gt;“The Joy of Dog Walking”&lt;/li&gt;
&lt;li&gt;“Public Speaking For The Modern Age”,&lt;/li&gt;
&lt;li&gt;“Learn The Art of Public Speaking”&lt;/li&gt;
&lt;li&gt;“Master The Art of Public Speaking”&lt;/li&gt;
&lt;li&gt;“The Best Way To Public Speaking”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API should return the following clusters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cluster 1 = (“Dog Walking: 10 Simple Steps”, “The Secrets of Dog Walking”, “Why You Need To Dog Walking”, “The Art of Dog Walking”, “The Joy of Dog Walking”)&lt;/li&gt;
&lt;li&gt;Cluster 2 = (“Public Speaking For The Modern Age”, “Learn The Art of Public Speaking”, “Master The Art of Public Speaking”, “The Best Way To Public Speaking”)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The model
&lt;/h2&gt;

&lt;p&gt;I plan to describe the details of the specific model and algorithm I used in a future post. For now, the important aspect is that this model can be loaded in memory with some function we define as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model = get_model()

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

&lt;/div&gt;



&lt;p&gt;This model will likely be a very large in-memory object. We only want to load it once in our backend process and use it throughout the request lifecycle, possibly for more than just one request. A typical model will take a long time to load. Ten seconds or more is not unheard of, and we can’t afford to load it for every request. It would make our service terribly slow and unusable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple Python backend module
&lt;/h2&gt;

&lt;p&gt;Last year I discovered &lt;a href="https://fastapi.tiangolo.com"&gt;FastAPI&lt;/a&gt;, and I immediately liked it. It’s easy to use, intuitive and yet flexible. It allowed me to quickly build up every aspect of my service, including its documentation, auto-generated from the code.&lt;/p&gt;

&lt;p&gt;FastAPI provides a well-structured base to build upon, whether you are just starting with Python or you are already an expert. It encourages use of type hints and model classes for each request and response. Even if you have no idea what these are, just follow along FastAPI’s good defaults and you will likely find this way of working quite neat.&lt;/p&gt;

&lt;p&gt;Let’s build our service from scratch. I usually start from a python &lt;em&gt;virtualenv&lt;/em&gt;, an isolated python environment where you can install your dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;virtualenv --python /usr/bin/python3.8 .venv
source .venv/bin/activate

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

&lt;/div&gt;



&lt;p&gt;If you are not familiar with virtualenv, there are many tutorials you can read online.&lt;br&gt;&lt;br&gt;
Next step, we write our requirements file, with all the python modules we need to run our project. Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# --- requirements.txt
fastapi~=0.61.1

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

&lt;/div&gt;



&lt;p&gt;Save the file as &lt;code&gt;requirements.txt&lt;/code&gt;. You can install the modules with &lt;code&gt;pip&lt;/code&gt;. There are plenty of guides on how to get pip on your system if you don’t have it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt

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

&lt;/div&gt;



&lt;p&gt;Doing so will install FastAPI. Let’s create our backend now. Copy the following skeleton API into a &lt;code&gt;main.py&lt;/code&gt; file. If you prefer, you can clone the FastAPI template published at &lt;a href="https://github.com/cosimo/fastapi-ml-api"&gt;https://github.com/cosimo/fastapi-ml-api&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from typing import Optional

from fastapi import FastAPI

app = FastAPI()
model = get_model()

@app.post("/cluster")
def cluster():
return {"Hello": "World"}

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

&lt;/div&gt;



&lt;p&gt;You can run this service with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn main:app --reload

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

&lt;/div&gt;



&lt;p&gt;You’ll notice right away that any changes to the code will trigger a reload of the server: if you are using the production ML model, the model own load time will quickly become a nuisance. I haven’t managed to solve this problem yet. One approach I could see working is to either mock the model results if possible, or use a lighter model for development.&lt;/p&gt;

&lt;p&gt;Invoking uvicorn in this way is recommended for development. For production deployments, FastAPI’s docs &lt;a href="https://fastapi.tiangolo.com/deployment/manually/"&gt;recommend using gunicorn&lt;/a&gt; with the uvicorn workers. I haven’t looked into other options in depth. There might be better ways to deploy a production service. For now this has proven to be reliable for my needs. I did have to tweak gunicorn’s configuration to my specific case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running our service with gunicorn
&lt;/h2&gt;

&lt;p&gt;The gunicorn start command looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gunicorn -c gunicorn_conf.py -k uvicorn.workers.UvicornWorker --preload main:app

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

&lt;/div&gt;



&lt;p&gt;Note the arguments to gunicorn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-k&lt;/code&gt; tells gunicorn to use a specific worker class&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main:app&lt;/code&gt; instructs gunicorn to load the main module and use &lt;code&gt;app&lt;/code&gt; (in this case the FastAPI instance) as the application code that all workers should be running&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--preload&lt;/code&gt; causes gunicorn to change the worker startup procedure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Preloading our application
&lt;/h2&gt;

&lt;p&gt;Normally gunicorn would create a number of workers, and then have each worker load the application code. The &lt;a href="https://docs.gunicorn.org/en/stable/settings.html"&gt;&lt;code&gt;--preload&lt;/code&gt; option&lt;/a&gt; inverts the sequence of operations by loading the application instance first and then forking all worker processes. Because of how &lt;code&gt;fork()&lt;/code&gt; works, each worker process will be a copy of the main gunicorn process and will share (part of) the same memory space.&lt;/p&gt;

&lt;p&gt;Making our ML model part of the FastAPI application (or making our model load when the FastAPI application is first created) will cause our &lt;code&gt;model&lt;/code&gt; variable to be “shared” across all processes!&lt;/p&gt;

&lt;p&gt;The effect of this change is massive. If our model, once loaded into memory, occupies 1 Gb of RAM, and we want to run 4 gunicorn workers, the net gain is 3 Gb of memory that we will have available for other uses. In a container-based deployment, it is especially important to keep the memory usage low. Reclaiming 75% of the total memory that would otherwise be used is an excellent result.&lt;/p&gt;

&lt;p&gt;I don’t know enough details about PyTorch models or Python itself to understand how this sharing keeps being valid across the process lifetime. I believe that modifying the model in any way will cause copy-on-write operations and ultimately the model variable to be copied in each process memory space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complications
&lt;/h2&gt;

&lt;p&gt;Turns out we don’t get this advantage for free. There are a few complications with having a PyTorch model shared across different processes. The &lt;a href="https://pytorch.org/docs/stable/notes/multiprocessing.html"&gt;PyTorch documentation&lt;/a&gt; covers them in detail, even though I’m not sure I did in fact understand all of it.&lt;/p&gt;

&lt;p&gt;In my project I tried several approaches, without success:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;pytorch.multiprocessing&lt;/code&gt; in the &lt;code&gt;gunicorn&lt;/code&gt; configuration module&lt;/li&gt;
&lt;li&gt;modify &lt;code&gt;gunicorn&lt;/code&gt; itself (!) to use &lt;code&gt;pytorch.multiprocessing&lt;/code&gt; to load the model. I did it just as a prototype, but even then… bad idea&lt;/li&gt;
&lt;li&gt;investigate alternative worker models instead of prefork. I don’t remember the results of this investigation, but they must have been unsuccessful&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;/dev/shm&lt;/code&gt; (Linux shared memory tmpfs) as a filesystem where to store the Pytorch model file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Solution?
&lt;/h2&gt;

&lt;p&gt;The approach I ended up using is the following.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gunicorn&lt;/code&gt; must create the FastAPI application to start it, so I loaded the model (as a global) when creating the FastAPI application, and verified the model was loaded before that, and only loaded once.&lt;/p&gt;

&lt;p&gt;I added the &lt;code&gt;preload_app = True&lt;/code&gt; option to gunicorn’s configuration module.&lt;/p&gt;

&lt;p&gt;I limited the amount of workers (my tests showed 3 to work best for my use case), and limited the amount of requests each gunicorn worker will serve. I used &lt;code&gt;max_requests = 50&lt;/code&gt;. I limited the amount of requests because I noticed a sudden increase in memory usage in each worker regularly some minutes after startup. I couldn’t trace it back to something specific, so I used this dirty workaround.&lt;/p&gt;

&lt;p&gt;Another tweak was to allow the gunicorn workers to start up in a longer than default time, otherwise they would be killed and respawned by gunicorn’s own watchdog as they were taking too long to load the ML model on startup. I used a timeout of 60 seconds instead of the default 30.&lt;/p&gt;

&lt;p&gt;The most difficult problem to troubleshoot was workers suddenly stopping and not serving any more requests after a short while. I solved that by not using &lt;code&gt;async&lt;/code&gt; on my FastAPI application methods. Other people &lt;a href="https://github.com/tiangolo/fastapi/issues/2425#issuecomment-738042275"&gt;have reported this solution not working for them&lt;/a&gt;… This remains to be understood.&lt;/p&gt;

&lt;p&gt;Lastly, when loading the Pytorch model, I used the &lt;code&gt;.eval()&lt;/code&gt; and &lt;a href="https://pytorch.org/docs/stable/multiprocessing.html"&gt;&lt;code&gt;.share_memory()&lt;/code&gt;&lt;/a&gt; methods on it, before returning it to the FastAPI application. This is happening just on first load.&lt;/p&gt;

&lt;p&gt;For example, this is how my model loading looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def load_language_model() -&amp;gt; SentenceTransformer:
    language_model = SentenceTransformer(SOME_MODEL_NAME)
    language_model.eval()
    language_model.share_memory()

    return language_model

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

&lt;/div&gt;



&lt;p&gt;The value returned by this method is assigned to a global loaded before the FastAPI application instance is created.&lt;/p&gt;

&lt;p&gt;I doubt this is &lt;em&gt;the&lt;/em&gt; way to do things, but I did not find any clear guide on how to do this. Information about deploying production models seems quite scarce, if you remember the premise to this post.&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;preload_app = True&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Load the ML model before the FastAPI (or wsgi) application is created&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.eval()&lt;/code&gt; and &lt;a href="https://pytorch.org/docs/stable/multiprocessing.html"&gt;&lt;code&gt;.share_memory()&lt;/code&gt;&lt;/a&gt; if your model is PyTorch-based&lt;/li&gt;
&lt;li&gt;Limit the amount of workers/requests&lt;/li&gt;
&lt;li&gt;Increase the worker start timeout period&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read on for other tips about dockerization of all this. But first…&lt;/p&gt;

&lt;h2&gt;
  
  
  Gunicorn configuration
&lt;/h2&gt;

&lt;p&gt;Here’s more or less all the customizations needed for the gunicorn configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Preload the FastAPI application, so we can load the PyTorch model
# in the parent gunicorn process and share its memory with all the workers
preload_app = True

# Limit the amount of requests a single worker will handle, so as to
# curtail the increase in memory usage of each worker process
max_requests = 50

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bundling model and application in a Docker container
&lt;/h2&gt;

&lt;p&gt;Your choice of deployment target might be different. What I used for our production environment is a &lt;code&gt;Dockerfile&lt;/code&gt;. It’s easily applicable as a development option but also good for production in case you deploy to a platform like &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; like I did.&lt;/p&gt;

&lt;p&gt;Initially I tried to build a Dockerfile with everything I needed. I kept the PyTorch model file as binary in the git repository. The binary was larger than 500Mb, and that required the use of &lt;a href="https://git-lfs.github.com/"&gt;git-lfs&lt;/a&gt; at least for Github repositories. I found that to be a problem when trying to build Docker containers from Github Actions. I couldn’t easily reconstruct the git-lfs objects at build time. Another shortcoming of this approach is that the large model file makes the docker container context huge, increasing build times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two stage Docker build
&lt;/h2&gt;

&lt;p&gt;In cases like this, splitting the Docker build in two stages can help. I decided to bundle the large model binary into a first stage Docker container, and then build up my application layer on top as stage two.&lt;/p&gt;

&lt;p&gt;Here’s how it works in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# --- Dockerfile.stage1

# https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8

# Install PyTorch CPU version
# https://pytorch.org/get-started/locally/#linux-pip
RUN pip3 install torch==1.7.0+cpu torchvision==0.8.1+cpu torchaudio==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html

# Here I'm using sentence_transformers, but you can use any library you need
# and make it download the model you plan using, or just copy/download it
# as appropriate. The resulting docker image should have the model bundled.
RUN pip3 install sentence_transformers==0.3.8
RUN python -c 'from sentence_transformers import SentenceTransformer; model = SentenceTransformer("")'

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

&lt;/div&gt;



&lt;p&gt;Build and push this container image to your docker container registry as &lt;code&gt;stage1&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;After that, you can build your stage2 docker image starting from the stage1 image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# --- Dockerfile
FROM $(REGISTRY)/$(PROJECT):stage1

# Gunicorn config uses these env variables by default
ENV LOG_LEVEL=info
ENV MAX_WORKERS=3
ENV PORT=8000

# Give the workers enough time to load the language model (30s is not enough)
ENV TIMEOUT=60

# Install all the other required python dependencies
COPY ./requirements.txt /app
RUN pip3 install -r /app/requirements.txt

COPY ./config/gunicorn_conf.py /gunicorn_conf.py
COPY ./src /app
# COPY ./tests /tests

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

&lt;/div&gt;



&lt;p&gt;You may need to increase the runtime shared memory to be able to load the ML model in a preload scenario.&lt;br&gt;&lt;br&gt;
If that’s the case, or if you get errors on model load when running your project in Docker or Kubernetes, you need to run docker with &lt;code&gt;--shm-size=1.75G&lt;/code&gt; for example, or any suitable amount of memory for your own model, as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --shm-size=1.75G --rm &amp;lt;command&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;The equivalent directive for a helm chart to deploy in Kubernetes is (WARNING: POSSIBLY MANGLED YAML AHEAD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  ...
  template:
    ...
    spec:
      volumes:
        - name: modelsharedmem
          emptyDir:
            sizeLimit: "1750Mi"
            medium: "Memory"
      containers:
        - name: {{ .Chart.Name }}
          ...
          volumeMounts:
            - name: modelsharedmem
              mountPath: /dev/shm
          ...

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Makefile to bind it all together
&lt;/h2&gt;

&lt;p&gt;I like to add a &lt;code&gt;Makefile&lt;/code&gt; to my projects, to create a memory of the commands needed to start a server, run tests or build containers. I don’t need to use brain power to memorize any of that, and it’s easy for colleagues to understand what commands are used for which purpose.&lt;/p&gt;

&lt;p&gt;Here’s my sample Makefile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# --- Makefile
PROJECT=myproject
BRANCH=main
REGISTRY=your.docker.registry/project

.PHONY: docker docker-push start test

start:
    ./scripts/start.sh

# Stage 1 image is used to avoid downloading 2 Gb of PyTorch + nlp models
# every time we build our container
docker-stage1:
    docker build -t $(REGISTRY)/$(PROJECT):stage1 -f Dockerfile.stage1 .
    docker push $(REGISTRY)/$(PROJECT):stage1

docker:
    docker build -t $(REGISTRY)/$(PROJECT):$(BRANCH) .

docker-push:
    docker push $(REGISTRY)/$(PROJECT):$(BRANCH)

test:
    JSON_LOGS=False ./scripts/test.sh

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other observations
&lt;/h2&gt;

&lt;p&gt;I had initially opted for Python 3.7, but I tried upgrading to Python 3.8 because of a comment on a related FastAPI issue on Github, and in my tests I found that Python 3.8 uses slightly less memory than Python 3.7 over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;p&gt;I published a sample repository to get started with a project like the one I just described: &lt;a href="https://github.com/cosimo/fastapi-ml-api"&gt;https://github.com/cosimo/fastapi-ml-api&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And these are the links to issues I either followed or commented on while researching my solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/fastapi/issues/596"&gt;https://github.com/tiangolo/fastapi/issues/596&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tiangolo/fastapi/issues/2425"&gt;https://github.com/tiangolo/fastapi/issues/2425&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pytorch/pytorch/issues/49555"&gt;https://github.com/pytorch/pytorch/issues/49555&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pytorch/pytorch/issues/16943"&gt;https://github.com/pytorch/pytorch/issues/16943&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, please let me know if you have any feedback on any of this, or you are doing things differently, etc... Thanks!&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>python</category>
      <category>pytorch</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>The Perl echo chamber, marketing and … is Perl really dying?</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Sun, 20 Jun 2021 19:29:52 +0000</pubDate>
      <link>https://dev.to/cosimo/the-perl-echo-chamber-marketing-and-is-perl-really-dying-4cp9</link>
      <guid>https://dev.to/cosimo/the-perl-echo-chamber-marketing-and-is-perl-really-dying-4cp9</guid>
      <description>&lt;p&gt;Recently I came across &lt;a href="https://twitter.com/OvidPerl/status/1406653683006386180?s=20"&gt;this tweet from Curtis/Ovid&lt;/a&gt;, which references &lt;a href="https://www.nntp.perl.org/group/perl.perl5.porters/2021/06/msg260597.html"&gt;longer post&lt;/a&gt; about a proposal to integrate &lt;a href="https://github.com/Ovid/Cor/wiki"&gt;a better, more modern object-oriented “system”&lt;/a&gt; (Corinna) in Perl 5.&lt;/p&gt;

&lt;p&gt;The proposal itself is &lt;strong&gt;not&lt;/strong&gt; what I’d like to address here. I haven’t followed Corinna’s evolution. I believe it goes in a positive direction for the language, FWIW.&lt;/p&gt;

&lt;p&gt;From that original tweet, a comment from &lt;a href="https://twitter.com/consttype"&gt;Rafael&lt;/a&gt; followed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[…] but I’m still wondering what are the real factors that make companies seek an exit strategy from Perl 5. Who makes this kind of expensive decision, and why? I suspect obscure OO syntax is not a major one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is what I replied with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is indicative of the fundamental problem in the Perl echo chamber. Some people still have no idea why companies are moving away from Perl. If you want to hear the perspective from someone who has seen this happen in multiple companies, let me know :-)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sorry for this premise, but I was afraid what follows would make no sense otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Perl dying today?
&lt;/h2&gt;

&lt;p&gt;First of all, I don’t think “ is dying” is a useful question to ask, nor it is indicative of anything particularly interesting. I’m sure everyone reading this will have encountered plenty of “C is dying”, “Java is dying” or similar, and yet, C and Java are still being used everywhere. In one sense, no language really dies ever. In Perl’s situation, things are slightly different though, as (I believe) Python slowly conquered Perl’s space over time.&lt;/p&gt;

&lt;p&gt;What does it mean for a language to die, or to be dead?&lt;/p&gt;

&lt;p&gt;From an &lt;strong&gt;end user point of view&lt;/strong&gt; , let’s say a random programmer employed in a company or freelance, a language could be dying if a task they want to accomplish using that language is hard because there are no supporting libraries for it (think CPAN or PyPi), or the libraries are so old they don’t work anymore. That situation surely conveys the idea that the language is not in use anymore, or very few people must be using that language. One would expect that a common task in 2021 must be easy to accomplish with a language worth using in 2021.&lt;/p&gt;

&lt;p&gt;What about a &lt;strong&gt;company&lt;/strong&gt; ‘s point of view? The reality is that companies don’t have an opinion on languages, only people do. Teams do have an opinion on languages. The group dynamics inside a team influence what languages are acceptable for current and new projects.&lt;/p&gt;

&lt;p&gt;Is Perl dying then?&lt;/p&gt;

&lt;h2&gt;
  
  
  My experience
&lt;/h2&gt;

&lt;p&gt;Some years ago I was a &lt;a href="https://metacpan.org/author/COSIMO"&gt;fairly active member of the Perl community&lt;/a&gt;, I attended and presented at various Perl conferences around Europe, talking about my experience using Perl at a few &lt;a href="https://www.over-log.it/"&gt;small&lt;/a&gt; and &lt;a href="https://opera.com"&gt;large&lt;/a&gt; companies.&lt;/p&gt;

&lt;p&gt;I remember picking up Perl for the first time based on a suggestion from my manager back then. He gave me a hard copy print-out of the whole of Perl 5.004 man pages, and said: “We are going to use this language. It’s amazing, take some time to study it and we’ll start!”. This was 1998, and I had such a fantastic time :-). I was such a noob, but Perl was amazing. It could do everything you needed and then some, and it was easy and simple. The language was fast already back then, and it got faster over time. At that point in time, I was working in a very small company, we were three people initially, and we ended up writing a complete web framework from scratch that is still in use today, after more than 20 years. If that’s not phenomenal, I don’t know what is. It’d be cool to talk about this framework: it was more advanced than anything that’s ever been done even considering it’s 2021… a story for another time.&lt;/p&gt;

&lt;p&gt;And by the way, we were running our Perl code on *anything*, and I mean anything, Windows PCs, Linux, Netware and even AS/400, a limited subset of it at least, at a time when Java’s “write once, run everywhere” was just an empty marketing promise. Remember this was the time of Netscape Navigator and Java applets. Ramblings, I know, but perhaps useful to understand where things have gone wrong.&lt;/p&gt;

&lt;p&gt;In 2007, I left my job in Italy and moved to Norway to work for Opera Software. Back then, Opera’s browser was still running the Presto engine, and a little department inside Opera was in charge of web services. That’s where I was headed. Most services there were written in Perl. Glorious times for me, I would learn an awful lot there, meet a lot of skilled developers. Soon after I started working there, 2007, some colleagues were already making fun of Perl. It’s a “write-only language”, “not meant for serious stuff”, “lack of web frameworks”, etc… Those were the times when Python frameworks started to emerge, some of which would eventually disappear. I remember a few colleagues strongly arguing to move to this Python framework called &lt;a href="https://www.pylonsproject.org/"&gt;Pylons&lt;/a&gt;, and then eventually to Django.&lt;/p&gt;

&lt;p&gt;I believe this general attitude towards Perl originated from different factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal preference towards other languages and/or dislike towards Perl&lt;/li&gt;
&lt;li&gt;the desire to be working with the latest “hip” framework or language&lt;/li&gt;
&lt;li&gt;the discomfort of maintaining an aging codebase with problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These factors exist and are legitimate reasons to want to move away from any language or framework. I’m not saying they are justified, but I do understand why people wanted that. In our field, I have seen it’s quite common to try and avoid the objective difficulties of maintaining a legacy project, going the greener way of an overly optimistic rewrite, which normally ends in tears.&lt;/p&gt;

&lt;p&gt;Throughout the years, I noticed other contributing factors to the progressive abandonment of Perl, even in companies like Opera.&lt;br&gt;&lt;br&gt;
I’ll mention two that I experienced directly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Outdated or non existent supporting libraries&lt;/li&gt;
&lt;li&gt;Teams composition&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There was a time a few years ago, when CPAN was awesome, the best language support system in existence and every other language community was envying it. CPAN pretty much was selling Perl by itself. In my case, the libraries on CPAN educated me and made me adopt a testing culture that no other language (in my knowledge) had before Perl. Today, seeing npm modules being installed without running tests makes me uncomfortable :-)&lt;/p&gt;

&lt;p&gt;Then over time (years) a shift happened. You would search on CPAN for a library that would help you with a common task and you wouldn’t find anything, or you would only find quick hacks that didn’t really work properly. In my case, I remember the first example of that being OAuth2. If I had to speculate, I would say this is a product of many elements, one of which is the average age of Perl programmers getting higher.&lt;/p&gt;

&lt;p&gt;Another related shift I remember from those years is companies publishing their APIs/SDKs started dismissing Perl, at first relying on some CPAN module to eventually appear, then completely omitting Perl support. In the beginning, we politely complained to those companies, trying to make a point, but unfortunately there was no turning back. These days almost no SDK comes with a Perl component.&lt;/p&gt;

&lt;p&gt;The second major aspect I have experienced is related to teams. In 2012 I was tasked with writing my first ever greenfield project, entirely from scratch, a project that would turn out to be one of the things I’m most proud of, &lt;strong&gt;Opera Discover&lt;/strong&gt; , an online news recommendation system for the Opera browser, still working today! A team of three veteran engineers (myself included) was assembled, and there and then, we were faced with a decision: what language should we use for this?&lt;/p&gt;

&lt;p&gt;While I was most experienced in Perl and knew Python a little, the other two colleagues didn’t know Perl. They had experience in C++ mostly, as this was Opera after all. We were chosen not based on our programming language expertise, rather (I suppose) based on our capability to tackle such a big and complex project. While I could have proposed that the project be written in Perl, in good conscience I knew that choice was not viable. Django was readily available and could provide a wide range of functionality we actually needed. No alternative in the Perl world could come close to such a good value proposition. The fact that Python was (like Perl had been for me!) a very accessible choice, simple to pick up, easily installed on any Linux system, and with plenty of solid up-to-date libraries, made the choice obvious.&lt;/p&gt;

&lt;p&gt;With the Discover project, I started learning Python properly as a day-to-day programming language. I remember being horrified (and making fun of) the httplib2/httplib3 situation initially. Then I learned about the requests module and forgot all about it. This is to say, Python also has its quirks of course. The disastrous Python 2 vs Python 3 decision in the Python community caused a lot of grief and uncertainty for people (Perl could have learned something from that…). Nowadays, that’s a non-argument, everything runs on Python 3 and if you still haven’t moved, you will soon.&lt;/p&gt;

&lt;p&gt;In general, having learned Python quite well, my mindset with regards to programming and my job changed completely. I’m not a Perl programmer. I’m not a Python programmer either. I can use different tools whenever they are more suited to what I need to do. In fact, in my last four years I have written software in NodeJS and Java of all things… I used to despise and make fun of Java, but I had never worked on any professional project before. While I do maintain that Java has some horrible aspects, contrary to my expectations, I have enjoyed working with it, it has an efficient runtime, awesome threading, solid libraries and debugging/inspection tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;While I do understand Ovid’s point about wanting to keep the business going, and enjoying Perl as a language, I have personally moved on many years ago. I still use Perl for the occasional script when it’s convenient, but for other use cases, like web APIs, I prefer Python and FastAPI, PyTorch for machine learning, etc.. so my conclusion is that it’s the libraries and the ecosystem that drive language use, and &lt;strong&gt;not the language itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A better OO system will unfortunately do nothing for Perl (in my opinion at least). Better marketing will without a doubt do nothing for Perl. As if a prettier website could change the situation and the aspects I talked about… it can’t! The situation we have in front of us in 2021 is the result of technological and social changes started at least a decade ago.&lt;/p&gt;

&lt;p&gt;I realize this may be an incoherent post. Sorry about that, I tried to write it right away or it would have probably never come out.&lt;br&gt;&lt;br&gt;
If you have questions or comments, let me know and I’ll try to address them if I can.&lt;/p&gt;

&lt;p&gt;Most importantly, I do not wish to convince anyone that what I wrote is true. It is simply my experience. If there’s one thing I wish people would take from it, it’s to move away from the thought of yourself being a “X Programmer” and broaden your horizons and set of tools available to you. It was a tremendously positive move for myself, one I wished I had done before.&lt;/p&gt;

&lt;p&gt;Peace.&lt;/p&gt;

</description>
      <category>development</category>
      <category>languages</category>
      <category>marketing</category>
      <category>perl</category>
    </item>
    <item>
      <title>How to setup kubectl from scratch with existing clusters</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Mon, 29 Mar 2021 11:05:25 +0000</pubDate>
      <link>https://dev.to/cosimo/how-to-setup-kubectl-from-scratch-with-existing-clusters-2fm8</link>
      <guid>https://dev.to/cosimo/how-to-setup-kubectl-from-scratch-with-existing-clusters-2fm8</guid>
      <description>&lt;p&gt;If you are just starting to use kubectl or Kubernetes, before you can interact with the Kubernetes clusters, you will need to setup gcloud and kubectl. This quick post explains how to do that. It could also be of use if your kubectl stopped working for unknown reasons, which it might do from time to time.&lt;/p&gt;

&lt;p&gt;I wrote this quick post because all the instructions you get online assume you are working with a toy project and want to create your test kubernetes cluster. That is not what most people need in my opinion, so I thought I'd write this. Hopefully this is useful to at least another person out there :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step by step instructions
&lt;/h2&gt;

&lt;p&gt;Setup GCloud SDK if you haven’t yet. Follow the instructions here: &lt;a href="https://cloud.google.com/sdk/docs/install"&gt;https://cloud.google.com/sdk/docs/install&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typing &lt;code&gt;gcloud --version&lt;/code&gt; should output something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud --version
Google Cloud SDK 291.0.0
alpha 2020.05.01
beta 2020.05.01
bq 2.0.57
core 2020.05.01
gsutil 4.50
kubectl 2020.05.01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;code&gt;kubectl&lt;/code&gt; by following instructions here: &lt;a href="https://kubernetes.io/docs/tasks/tools/"&gt;https://kubernetes.io/docs/tasks/tools/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typing &lt;code&gt;kubectl --version&lt;/code&gt; should output something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.5", 
  GitCommit:"6b1d87acf3c8253c123756b9e61dac642678305f", GitTreeState:"clean",
  BuildDate:"2021-03-18T01:10:43Z", GoVersion:"go1.15.8", Compiler:"gc",
  Platform:"linux/amd64"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Backup any existing &lt;code&gt;~/.kube/config&lt;/code&gt; file you might have from before and move it to a temporary directory.&lt;/p&gt;

&lt;p&gt;Type the following command to fetch the kubernetes configuration for your existing clusters:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud container clusters get-credentials &amp;lt;cluster-name&amp;gt; --region &amp;lt;region&amp;gt; --project &amp;lt;kubernetes-project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and repeat that for each project and cluster you want to manage.&lt;/p&gt;

&lt;p&gt;If the step above fails, you likely don’t have the necessary permissions to access the desired kubernetes cluster. Ask someone to help if possible :-)&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;~/.kube/config&lt;/code&gt; file will now contain references to clusters configuration. Each cluster will have an endpoint URL, like &lt;code&gt;https://11.22.33.44&lt;/code&gt;. If you access your kubernetes or work environment via a VPN, ensure your routes are setup to access those endpoints via the VPN itself, or your kubectl command won’t be able to connect your Kubernetes clusters!&lt;/p&gt;

&lt;p&gt;You can confirm that everything is working by using a sample command like &lt;code&gt;kubectl get pods&lt;/code&gt;. That command should return a list of pod names, like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get pods
NAME                           READY   STATUS    RESTARTS   AGE
somestuff-7bbd5fd8bf-bb27k   1/1     Running   0          36d
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You can now do some real damage with random kubectl commands. Have fun! :-)&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>kubectl</category>
      <category>devops</category>
    </item>
    <item>
      <title>pgtop – a top clone for PostgreSQL</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Wed, 09 Dec 2020 10:18:06 +0000</pubDate>
      <link>https://dev.to/cosimo/pgtop-a-top-clone-for-postgresql-319</link>
      <guid>https://dev.to/cosimo/pgtop-a-top-clone-for-postgresql-319</guid>
      <description>&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%2Fi%2Fuqvwca4a1dwzgghxhibk.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%2Fi%2Fuqvwca4a1dwzgghxhibk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://metacpan.org" rel="noopener noreferrer"&gt;meta::cpan&lt;/a&gt; records, the &lt;a href="https://metacpan.org/release/COSIMO/pgtop-0.02" rel="noopener noreferrer"&gt;first release of pgtop&lt;/a&gt; is dated April 26, 2005, which makes this little software &lt;strong&gt;more than 15 years old&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Back then I had just found out about the brilliant &lt;a href="http://jeremy.zawodny.com/mysql/mytop/" rel="noopener noreferrer"&gt;mytop&lt;/a&gt; by Jeremy Zawodny, and my day-to-day experience being on Postgres, IIRC version 6.5.3, I decided to try and “convert” mytop to Postgres.&lt;/p&gt;

&lt;p&gt;Being quite naive, I thought the endeavour would be much easier than it really was. I’m glad I started though, which is why pgtop exists in the first place. It’s not the only one either. I seem to remember a few similar pgtop projects by other programmers.&lt;/p&gt;

&lt;p&gt;After using MySQL and Percona Server for many years, due to a new job, I have gone back to Postgres, version 9.5 and 10 at this time. In recent months, I have done some work to improve performance of our database queries, and remembered writing and using pgtop years before.&lt;/p&gt;

&lt;p&gt;Since I lost(*) the original sources, I tried the pgtop version I last uploaded to CPAN, 0.05, dated 2008. It did work, in the sense that &lt;strong&gt;I could run the same perl code unmodified, a great testament to Perl as language and as runtime&lt;/strong&gt;. It didn’t work because the underlying Postgres meta tables that were used in version 6 changed their schema in the 10-12 years since :-)&lt;/p&gt;

&lt;p&gt;I spent some time to adapt the metadata queries to work with recent Postgres versions, and was slightly amused by the quality of my 15 year old code… The best feeling about this little tool was to &lt;strong&gt;rediscover how useful a few dozen lines of code can be&lt;/strong&gt;. The service provider monitoring helps, but doesn’t even come close to the level of detail pgtop can provide.&lt;/p&gt;

&lt;p&gt;After getting pgtop to work again, I quickly added a few more useful features. I was pleased by the efficiency with which I could work on this tool, considering its age.&lt;/p&gt;

&lt;p&gt;So far I added just what was strictly necessary to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updated pgtop to the current decade. Now requires perl &amp;gt;= 5.014&lt;/li&gt;
&lt;li&gt;Fixed to work with Postgres &amp;gt;= 9.0&lt;/li&gt;
&lt;li&gt;Added a sample Dockerfile to build and run pgtop as Docker container&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;--config&lt;/code&gt; option, to load arbitrary config files. This is useful if you want to monitor several databases at once, for example in a tmux session. The config file supports all the options that are available on the command line.&lt;/li&gt;
&lt;li&gt;Implemented a query killer command, activated pressing K to kill at once all queries slower than a given threshold, in seconds. This is useful if the database is overwhelmed by a lot of slow queries. I don’t recommend using it, &lt;strong&gt;particularly if it involves killing UPDATE or INSERT queries&lt;/strong&gt; , but it can be quite useful.&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;--slow_threshold option&lt;/code&gt;, to consider queries slow if they have been running for longer than the given value (in seconds). Now the tool highlights slow queries in bold yellow, and logs all the slow queries to a &lt;code&gt;pgtop.log&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;--slack_webhook&lt;/code&gt; option, to automatically notify a slack channel if a query crosses the slow threshold runtime value. All the information about the slow query including the SQL will be included in the slack message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please let me know if you give it a try! :-)&lt;/p&gt;

&lt;p&gt;Download here: &lt;a href="https://metacpan.org/release/COSIMO/pgtop-0.11" rel="noopener noreferrer"&gt;https://metacpan.org/release/COSIMO/pgtop-0.11&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>operations</category>
      <category>postgres</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>I tried dev.to and I do understand now</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Fri, 10 Jul 2020 09:54:20 +0000</pubDate>
      <link>https://dev.to/cosimo/i-tried-dev-to-and-i-do-understand-now-3bmb</link>
      <guid>https://dev.to/cosimo/i-tried-dev-to-and-i-do-understand-now-3bmb</guid>
      <description>&lt;p&gt;I have been programming professionally for more than 20 years now.&lt;/p&gt;

&lt;p&gt;I wasn't familiar with this community. I decided to give it a go about a month ago, imported a few of my old blog posts, added some new ones, longer posts, quick ones, etc...&lt;/p&gt;

&lt;p&gt;My posts are not popular by any means, I usually write about niche backend development stuff that's not very fashionable :-)&lt;/p&gt;

&lt;p&gt;The one article that gave me the opportunity to approach dev.to was &lt;a href="https://dev.to/cosimo/five-tips-to-be-a-more-effective-command-line-user-44e2"&gt;Five tips to be more effective on the command line&lt;/a&gt;, which I &lt;strong&gt;published here for the first time&lt;/strong&gt;. It probably got picked up in some feed and a user posted it on reddit a few days later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That made me understand the whole point of this community&lt;/strong&gt; and why people like it. The same exact post got thousands of views on reddit and mostly positive reactions but also &lt;strong&gt;a few scolding and rude comments&lt;/strong&gt; from people that have not even read the whole article, since they were complaining about stuff I did explain.&lt;/p&gt;

&lt;p&gt;Here on dev.to, I got barely a few hundred views, but polite and relevant comments from people that had clearly appreciated and read through all, suggesting alternatives or simply their own view on the approaches expressed in the post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I like how welcoming and polite this community feels like.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Jumping in and out of your editor too much?</title>
      <dc:creator>Cosimo Streppone</dc:creator>
      <pubDate>Fri, 12 Jun 2020 14:35:39 +0000</pubDate>
      <link>https://dev.to/cosimo/jumping-in-and-out-of-your-editor-too-much-2305</link>
      <guid>https://dev.to/cosimo/jumping-in-and-out-of-your-editor-too-much-2305</guid>
      <description>&lt;p&gt;Quick post! Hopefully useful as it's been for me.&lt;/p&gt;

&lt;p&gt;Have you ever been caught in the loop of &lt;strong&gt;editing a file&lt;/strong&gt; in vim (or any other editor), and then &lt;strong&gt;testing your changes&lt;/strong&gt; quickly from the command line, and doing it over and over for dozens of times?&lt;/p&gt;

&lt;p&gt;I have, many times. Sometimes you don't even need to do that. For example, vim allows you to invoke a command line tool with &lt;code&gt;:!program-name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Still, sometimes one falls into that vicious &lt;em&gt;edit-run-edit&lt;/em&gt; cycle without thinking too much. If that's your case, here's a simple tip you can try. &lt;em&gt;Note: you need to be on the console for this to work&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When you're done with your changes, save the file and then use &lt;code&gt;CTRL + Z&lt;/code&gt; to suspend the editor process and drop to the command line, where you will have your command waiting for you already.&lt;/p&gt;

&lt;p&gt;Run the command, and then use &lt;code&gt;fg&lt;/code&gt; to return to the editor process exactly as you left it.&lt;/p&gt;

&lt;p&gt;The edit-run cycle will be a lot faster.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fg&lt;/code&gt; (foreground) and &lt;code&gt;bg&lt;/code&gt; (background) are useful commands to "control the flow" of the processes you launch from the command line.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>vim</category>
      <category>linux</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
