<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Greg Nokes</title>
    <description>The latest articles on Forem by Greg Nokes (@tsykoduk).</description>
    <link>https://forem.com/tsykoduk</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%2F359245%2F0d4acd76-7219-439b-895a-6f448a01ff54.jpeg</url>
      <title>Forem: Greg Nokes</title>
      <link>https://forem.com/tsykoduk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tsykoduk"/>
    <language>en</language>
    <item>
      <title>Bringing Buddha into leadership</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Thu, 08 Jun 2023 21:34:05 +0000</pubDate>
      <link>https://forem.com/tsykoduk/bringing-buddha-into-leadership-4jn9</link>
      <guid>https://forem.com/tsykoduk/bringing-buddha-into-leadership-4jn9</guid>
      <description>&lt;p&gt;Leadership in tech isn't just about being the best coder in the room; it's also about creating an environment that encourages growth, promotes understanding, and fosters collaboration. How we lead impacts our team's performance, job satisfaction, and even our company's bottom line. That's why I believe it's important to continuously broaden our perspectives on leadership.&lt;/p&gt;

&lt;p&gt;In light of this, I wanted to share an article I recently published that explores a thought-provoking approach to leadership: &lt;a href="https://greg.nokes.name/2023/06/01/bringing-buddha-into-leadership/"&gt;Bringing Buddha into Leadership&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this piece, I delve into the principles of Buddhism and how they can be applied to leadership, especially in tech. The article discusses mindfulness, empathy, and compassion —qualities inherent in Buddhism and vital for effective leadership.&lt;/p&gt;

&lt;p&gt;The goal isn't to turn everyone into a Buddhist, but rather to open up new avenues for thought and discussion about how we can enhance our leadership skills. And while we typically don't associate ancient Eastern philosophies with modern tech workplaces, you may be surprised to see just how relevant and beneficial these principles can be.&lt;/p&gt;

&lt;p&gt;I hope this exploration can serve as a starting point for a broader conversation on what effective, compassionate leadership looks like in our industry. I'd love to hear your thoughts, experiences, and ideas on this topic.&lt;/p&gt;

&lt;p&gt;Let's build more than just software; let's build better environments, better teams, and better leaders!&lt;/p&gt;

</description>
      <category>leadership</category>
    </item>
    <item>
      <title>Reflections on a Decade at Heroku</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Tue, 14 Jun 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/tsykoduk/reflections-on-a-decade-at-heroku-29oi</link>
      <guid>https://forem.com/tsykoduk/reflections-on-a-decade-at-heroku-29oi</guid>
      <description>&lt;p&gt;At about 7:45am March 26th 2011, I walked up to the doors at Heroku for the first time. I was early because that’s what you did where I came from.&lt;/p&gt;

&lt;p&gt;The doors were locked. No one responded to the buzzer.&lt;/p&gt;

&lt;p&gt;So I went and got some really bad coffee at the &lt;a href="https://www.dnalounge.com"&gt;DNA Lounge&lt;/a&gt; next door, and waited. As I recall, I saw someone walk in at about 9, and followed them in. That’s how the next chapter in my life started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://greg.nokes.name/2022/06/14/a-decade-at-heroku/"&gt;Read More&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Time Machine for Salesforce Data, the Easy Way</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Fri, 16 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/building-a-time-machine-for-salesforce-data-the-easy-way-34dh</link>
      <guid>https://forem.com/heroku/building-a-time-machine-for-salesforce-data-the-easy-way-34dh</guid>
      <description>&lt;p&gt;So, you want to have every change made to the data in a Salesforce Object stored.. forever?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2020%2F10%2FIMG_0010_2.jpeg" 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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2020%2F10%2FIMG_0010_2.jpeg" alt="IMG 0010 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s explore how we used out of the box tools and a little coding to build a time machine, and what it looked like after I ran it on test data for several months.&lt;/p&gt;

&lt;p&gt;First about our test data environment: We are using a test org with randomly generated accounts, and opportunities associated with those accounts. We also have a service that runs every few minutes to make changes to a small, random subset of the accounts.&lt;/p&gt;

&lt;p&gt;The first step was to set up an collector app. This app will emit all of the changes as messages into Kafka.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a Heroku App in a Private Space&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcenter.heroku.com/articles/heroku-postgresql#provisioning-heroku-postgres" rel="noopener noreferrer"&gt;Add a Heroku Private Postgres Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;a href="https://devcenter.heroku.com/articles/getting-started-with-heroku-and-connect-without-local-dev" rel="noopener noreferrer"&gt;Heroku Connect&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Choose and Configure the objects to sync in Heroku Connect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We then needed to stream changes somewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;a href="https://devcenter.heroku.com/articles/kafka-on-heroku" rel="noopener noreferrer"&gt;Heroku Private Kakfa&lt;/a&gt; cluster to the app&lt;/li&gt;
&lt;li&gt;Configure the &lt;a href="https://devcenter.heroku.com/articles/heroku-data-connectors" rel="noopener noreferrer"&gt;Streaming Data Connector&lt;/a&gt; to point at the Heroku Connect tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We were then mirroring our Salesforce Data into Heroku Postgres, preforming CDC on the data, and streaming the changes into Kafka.&lt;/p&gt;

&lt;p&gt;We decided to use Node to listen to the event stream in Kakfa, and take the updates and insert them into a table on Postgres.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created another Heroku App in the Private Space&lt;/li&gt;
&lt;li&gt;Share the Kafka add on with the new app&lt;/li&gt;
&lt;li&gt;Add a Heroku Private Postgres database&lt;/li&gt;
&lt;li&gt;Create a table in the Postgres database - insuring that the validations would allow for duplicate values in things like &lt;code&gt;External_id__c&lt;/code&gt; and &lt;code&gt;sfid&lt;/code&gt; fields.&lt;/li&gt;
&lt;li&gt;Write code that listens to Kafka for events, and inserts the events as new records into the TARDIS ..um.. Postgres.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2020%2F10%2FIMG_0011_2.jpeg" 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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2020%2F10%2FIMG_0011_2.jpeg" alt="IMG 0011 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not only scalable to many objects in a single Salesforce org, it’s also scaleable to many Salesforce orgs - including Work.com, Service Cloud and SalesCloud. Not only can you easily provide visibility across orgs, but you are capturing all changes and you can report on how records have evolved over time, as well as how records relate to each other in diffrent orgs.&lt;/p&gt;

&lt;p&gt;What are the results now that we have all of this data flowing?&lt;/p&gt;

&lt;p&gt;Looking at the collector app. We have just over 50k accounts total, and we are using only 350 MB of data. Picking an account at random, we can see that we have the most up to date info for that account in Postgres.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="err"&gt;☯&lt;/span&gt; &lt;span class="n"&gt;heroku&lt;/span&gt; &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="c1"&gt;--app ggn-pg2k-test &lt;/span&gt;

&lt;span class="c1"&gt;--&amp;gt; Connecting to postgresql-rigid-10449&lt;/span&gt;
&lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;SSL&lt;/span&gt; &lt;span class="k"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cipher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ECDHE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;RSA&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AES256&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;GCM&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;SHA384&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;off&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;Type&lt;/span&gt; &lt;span class="nv"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;ggn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pg2k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_total_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'"salesforce"."account"'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt; 
&lt;span class="c1"&gt;----------------&lt;/span&gt;
 &lt;span class="mi"&gt;349&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ggn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pg2k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;salesforce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="k"&gt;count&lt;/span&gt;  
&lt;span class="c1"&gt;--------&lt;/span&gt;
 &lt;span class="mi"&gt;501646&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ggn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pg2k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sfid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score__c&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;salesforce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt; 
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;External_id&lt;/span&gt; &lt;span class="n"&gt;__c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'7f4acaad-6c77-4342-a533-83bb580aaf681586878813'&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score__&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

           &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sfid&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;score__c&lt;/span&gt; 
&lt;span class="c1"&gt;--------------------------+--------------------+----------&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Switching over to the time machine app, we have a total of 88,577,70 records on the same data source - so on average over 160 changes per record stored. Looking in detail at a single account, we can see a total of 21 changes on it.&lt;/p&gt;

&lt;p&gt;The total data storage for all of the records and changes took just over 4GB of space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="err"&gt;☯&lt;/span&gt; &lt;span class="n"&gt;heroku&lt;/span&gt; &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="c1"&gt;--app pg2k-gregs-drain&lt;/span&gt;

&lt;span class="c1"&gt;--&amp;gt; Connecting to postgresql-rugged-63570&lt;/span&gt;
&lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ubuntu&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pgdg16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;SSL&lt;/span&gt; &lt;span class="k"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cipher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ECDHE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;RSA&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AES256&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;GCM&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;SHA384&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;off&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;Type&lt;/span&gt; &lt;span class="nv"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;pg2k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gregs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;drain&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_total_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'"account"'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt; 
&lt;span class="c1"&gt;----------------&lt;/span&gt;
 &lt;span class="mi"&gt;4274&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pg2k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gregs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;drain&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;count&lt;/span&gt;  
&lt;span class="c1"&gt;---------&lt;/span&gt;
 &lt;span class="mi"&gt;8857770&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pg2k&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gregs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;drain&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sfid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score__c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; 
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;External_id__c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'7f4acaad-6c77-4342-a533-83bb580aaf681586878813'&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

           &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;score__c&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sfid&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;    
&lt;span class="c1"&gt;--------------------------+---------------------+----------+--------------------+---------&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5555551212&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;33351&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;967&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7974&lt;/span&gt; &lt;span class="n"&gt;x6487&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;34165&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;967&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7974&lt;/span&gt; &lt;span class="n"&gt;x6487&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;34699&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;967&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7974&lt;/span&gt; &lt;span class="n"&gt;x6487&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;35523&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;967&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7974&lt;/span&gt; &lt;span class="n"&gt;x6487&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;376946&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;967&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7974&lt;/span&gt; &lt;span class="n"&gt;x6487&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;971802&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;967&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7974&lt;/span&gt; &lt;span class="n"&gt;x6487&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1041819&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5555551212&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3006621&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;183&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4668&lt;/span&gt; &lt;span class="n"&gt;x379&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3006628&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;183&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4668&lt;/span&gt; &lt;span class="n"&gt;x379&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3006634&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;183&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4668&lt;/span&gt; &lt;span class="n"&gt;x379&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3006648&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5555551212&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5140095&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5140108&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5140114&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5140120&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5390250&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5620956&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7002900&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7031300&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7788503&lt;/span&gt;
 &lt;span class="n"&gt;Borer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Braun&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Gutmann&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;976&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9652&lt;/span&gt; &lt;span class="n"&gt;x047&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="n"&gt;f400001MDeYzAAL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7906202&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This pattern can be used for Salesforce objects via Heroku Connect, or any table that you have on Heroku Postgres. As you can see, it is super easy to set up - the only coding you have to do, is to create the listener who will grab all of the updates, and store them into your target database.&lt;/p&gt;

&lt;p&gt;This is really the power of the Heroku Platform - using it’s tools to reduce the amount of work needed to be done to accomplish powerful things, in data as well as in traditional apps.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>devops</category>
    </item>
    <item>
      <title>Condos and Lumberjacks, or Why PaaS?</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Wed, 16 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/condos-and-lumberjacks-or-why-paas-nlk</link>
      <guid>https://forem.com/heroku/condos-and-lumberjacks-or-why-paas-nlk</guid>
      <description>&lt;p&gt;I have been involved in and with various infrastructure as a service and platform as a service providers for well over 12 years now. Often I am asked to explain “why use a IaaS provider?” or “why use a PaaS?” or “Why use &lt;a href="https://www.heroku.com"&gt;Heroku&lt;/a&gt;?”.&lt;/p&gt;

&lt;p&gt;I wrote this out to try and explain these concepts in concrete terms.&lt;/p&gt;

&lt;p&gt;Lucky for me, a few co-workers decided to help me with art and video production help, and we were able to remove the “&lt;em&gt;with bad drawings&lt;/em&gt;” subtitle. Also we made a cool animation explaining this. You can see the &lt;a href="https://youtu.be/_N8Zf_nPZkQ"&gt;video here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to the awesome &lt;a href="https://www.linkedin.com/in/nicolecjohnson/"&gt;Nicole Johnson&lt;/a&gt;, Jennifer Hooper and the entire &lt;a href="https://www.heroku.com/podcasts"&gt;Heroku marketing team&lt;/a&gt; for taking my idea and making it shine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Icebergs and Lumberjacks
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;With bad drawings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS loves to say “&lt;em&gt;AWS is for builders&lt;/em&gt;” and I cannot agree more. AWS is for builders, but Heroku is for developers.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;And what does that even mean?&lt;/p&gt;

&lt;p&gt;And what is the difference?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2LKaMMyV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0100.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2LKaMMyV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0100.jpeg" alt="Iceberg above water" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Professional Development is hard. It is a skill that can take years to hone. The tools that developers use can be foreign to other folks. Things like “the CLI” or “IDE’s” or “git”. Like any other specialization developers speak their own language, and over the decades have developed tools that are aimed at their use case.&lt;/p&gt;

&lt;p&gt;Also the craft of development is hard. If you think about it, we are writing step by step instructions for computers to execute. We are pretty darn close to running at the bottom of the stack, right?&lt;/p&gt;

&lt;p&gt;Wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FF5PpjIs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0101.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FF5PpjIs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0101.jpeg" alt="Iceberg under water" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They say that 90% of the iceberg is underwater, and not visible. The same can be said about development. Writing code in Java, Ruby, Python or what have you is the &lt;em&gt;easy&lt;/em&gt; part. It’s the details that you do not see that cause you the most trouble.&lt;/p&gt;

&lt;p&gt;The 90% of the tasks that are invisible are what gets you. What sort of things are these?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QyW_ZMji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0102.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QyW_ZMji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0102.jpeg" alt="Whole Iceberg" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where the builders come in. They have to build the parts of the app that no one ever sees. They build the plumbing that the app runs on. Before you write a line of code, you have to acquire or build and configure a lot of things.&lt;/p&gt;

&lt;p&gt;Say, you want to have an application that simply serves HTML traffic and has part of it’s data connected to Salesforce. What would you need to &lt;em&gt;build&lt;/em&gt; to accomplish this?&lt;/p&gt;

&lt;p&gt;Well, at it’s most basic this is what you would have to build, before your developers even starting writing code!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_naPKCdZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/img_0107.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_naPKCdZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/img_0107.png" alt="Architecture Diagram" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This does not include things like autoscaling, auto-healing, multi-AZ failover, build environments, developer tooling, CI/CD pipelines and a host of other things that you will need.&lt;/p&gt;

&lt;p&gt;Oh you want to use Kuberntes (K8s)? That’s just as hard.&lt;/p&gt;

&lt;p&gt;Functions as a Service is getting closer to “Heroku easy” for certain use cases and certain architectures. For example, if your code can handle intermittent “cold start” delays, then FaaS might be for you. However it is not a silver bullet, and it does not solve every problem.&lt;/p&gt;

&lt;p&gt;If we think of it like needing a house, AWS is going to the lumber yard, and buying wood so that you can build a house. Even with K8s, EKS, Lambda, Docker, you still have to assemble, configure and maintain all of those things, and the interconnections between them.&lt;/p&gt;

&lt;p&gt;And, K8s, FaaS, Docker? They were born out of looking at how Heroku was and is doing things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="//12factor.net"&gt;12factor&lt;/a&gt;? Heroku wrote it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buildpacks.io"&gt;Buildpacks&lt;/a&gt;? Heroku invented them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git push heroku master&lt;/code&gt;? You guessed it. Invented at Heroku.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I started in the industry, we did not even have lumber yards. We had to cut down trees and get our lumber that way! We had to call Dell or HP or Gateway and have them send us servers. We then had to assemble those servers, and put them into racks. Next we had to install the operating system and any software packages that we needed.&lt;/p&gt;

&lt;p&gt;That’s on premises for you. The most control, and the most manual labor and time.&lt;/p&gt;

&lt;p&gt;AWS has elevated the game. AWS gives us a lumber store. And it is really awesome. You can go in, and find a vast array of different types and sizes of lumber, lots of connectors, electrical stuff and plumbing. You can pick and choose from a huge menu of services on AWS. However you have to choose the correct menu items, and then you have to configure them correctly, integrate them, and manage those services and integration.&lt;/p&gt;

&lt;p&gt;Oh and by the way, after you are done with all of that lumber and your house is built, who’s going to fix your house when something goes wrong with it?&lt;/p&gt;

&lt;p&gt;You.&lt;/p&gt;

&lt;p&gt;You will.&lt;/p&gt;

&lt;p&gt;When we say Heroku is easier to use then AWS this is exactly what we mean. Instead of buying lumber, you are buying a Condo with landscaping and maintenance included.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qckt6euO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0108.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qckt6euO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2020/09/IMG_0108.jpg" alt="Simple Architecture Diagram" width="640" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You buy the Condo and your interior decorators (developers) can get started on choosing the color of the tile, ordering couches and hanging the art.&lt;/p&gt;

&lt;p&gt;Heroku allows developers to skip the building, and get straight to development. It also manages those built parts for our customers. These two things allow Heroku customers to be able to focus on the development of the apps they want to build, and not focus time on building infrastructure to host their developers and code.&lt;/p&gt;

&lt;p&gt;What are your thoughts? Do you prefer to rack and stack, use IaaS or develop on a PaaS?&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Simplest Heroku Demo Ever</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Wed, 26 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/the-simplest-heroku-demo-ever-20h0</link>
      <guid>https://forem.com/heroku/the-simplest-heroku-demo-ever-20h0</guid>
      <description>&lt;p&gt;One of the things that I have done literally thousands of times over the last 9 years is show folks the power of Heroku. Over the years I have come up with, dare I say it, the simplest Heroku demo script ever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir directory
cd directory
git init . &amp;amp;&amp;amp; git checkout -b main
touch index.php
echo "&amp;lt;h1&amp;gt;this is a heroku app&amp;lt;/h1&amp;gt;" &amp;gt;&amp;gt; index.php
git add . &amp;amp;&amp;amp; git commit -m "first commit, yay"
heroku create
git push heroku main
heroku open

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

&lt;/div&gt;



&lt;p&gt;There you go - a few lines to create a new app, build environment, load balancers and everything else you need to run a highly scaleable, secured, web application. All thanks to the power of Heroku.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>devops</category>
      <category>gettingstarted</category>
    </item>
    <item>
      <title>How I post an article</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Fri, 21 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/how-i-post-an-article-4l34</link>
      <guid>https://forem.com/heroku/how-i-post-an-article-4l34</guid>
      <description>&lt;p&gt;I think that the workflow I have developed for this blog is pretty interesting, so I decided to write up how I currently approach it.&lt;/p&gt;

&lt;p&gt;The first step is to write an outline. I currently use &lt;a href="http://macromates.com" rel="noopener noreferrer"&gt;Textmate 2&lt;/a&gt;, but I am thinking about trying out &lt;a href="https://code.visualstudio.com" rel="noopener noreferrer"&gt;Visual Studio&lt;/a&gt;, as a few friends have been gushing about it. On my iPad I will use &lt;a href="https://workingcopyapp.com" rel="noopener noreferrer"&gt;Working Copy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Ftextmate-getting-started.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Ftextmate-getting-started.png" alt="Getting Started"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second step is to create a local branch for the article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Flocal-branch.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Flocal-branch.png" alt="Local Branch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The third step is to push the branch to Github and create a pull request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Fpush-to-github.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Fpush-to-github.png" alt="Push To Github"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Fopen-pr.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Fopen-pr.png" alt="Open Pr"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I have a &lt;a href="https://devcenter.heroku.com/articles/pipelines" rel="noopener noreferrer"&gt;pipeline&lt;/a&gt; set up on Heroku with review apps enabled, once the PR is opened on Github you will notice that Heroku automatically creates an app tied to that PR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Fpr-opened.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Fpr-opened.png" alt="Pr Opened"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Freview-app-created.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2Freview-app-created.png" alt="Review App Created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can hand out review app to folks that I want to get feedback from. Also, as I revise the app and push those changes to my PR, those changes automatically appear in the review app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2FMerge-PR.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2FMerge-PR.png" alt="Merge PR"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I am done with the article, I can push it into staging. I do this by simply merging the PR into my master branch. Once the PR is merged or deleted, the review app is disposed of.&lt;/p&gt;

&lt;p&gt;If the PR is merged into the master branch, my staging app detects the new code, and automatically deploys it. If I had unit tests written, I could hook up Heroku CI to automagically run my tests previous to the deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2FBuilding-Staging.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2FBuilding-Staging.png" alt="Building Staging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I am happy with the article, I can promote it into production.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2FPromote-to-Production.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%2Fgreg.nokes.name%2Fwp-content%2Fuploads%2F2018%2F02%2FPromote-to-Production.png" alt="Promote To Production"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is super nice about this workflow is when I want to do larger changes (for example, swapping out the theme) I can do that with out a fear of breaking the running app. When I used to host on Wordpress, I would often be making larger changes live on my site, with exciting results.&lt;/p&gt;

&lt;p&gt;You can read about how to create a pipeline on &lt;a href="https://devcenter.heroku.com/articles/pipelines" rel="noopener noreferrer"&gt;Heroku’s Devcenter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>ci</category>
      <category>cd</category>
    </item>
    <item>
      <title>How to Heroku</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Wed, 08 Apr 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/how-to-heroku-5h7k</link>
      <guid>https://forem.com/heroku/how-to-heroku-5h7k</guid>
      <description>&lt;p&gt;How do people who use the Heroku Platform actually use it? What is a day in the life of a Heroku user? What does it mean “to Heroku”? Read on to find out the answers to these and other questions….&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Universal Containers, Day 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It was Jane’s first day at Universal Containers (UC). Jane’s expertise was in Ruby on Rails, as she had just graduated from a bootcamp which taught “full stack development” using Rails.&lt;/p&gt;

&lt;p&gt;Jane was given the following user stories that she had to solve for on her first project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As a consumer of UC information I want to be able to find news and up to date information about the company.&lt;/li&gt;
&lt;li&gt;As a consumer of UC information I want to be able to search older information in a logical fashion&lt;/li&gt;
&lt;li&gt;As a consumer of UC information, I want to be able to view older blog posts in a time series fashion&lt;/li&gt;
&lt;li&gt;I want to be able to deliver this to a potentially very large group of people in an unauthenticated manner, with precise control over the UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Being a Rails developer, Jane started to formulate a plan based on Rails to serve content to a wide variety of users. She envisioned a rails app, backed by a PostgresSQL database. This would give her an abstraction from having to deal with coding things like an application web server, application request router, and layout engine. All of that and more comes out of the box with Rails!&lt;/p&gt;

&lt;p&gt;Jane fired up her tool of choice, Terminal.app, and got started:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pXVCURi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-terminal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pXVCURi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-terminal.png" alt="Uc Day 1 Terminal" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, Jane went to &lt;a href="http://www.heroku.com/"&gt;www.heroku.com&lt;/a&gt; and signed up for an account, and downloaded the Heroku CLI to her new machine.&lt;/p&gt;

&lt;p&gt;She then decided to set up a CI/CD Pipeline to help her with her development work, so she consulted the &lt;a href="http://devcenter.heroku.com/"&gt;devcenter&lt;/a&gt; and searched for CI/CD. She found the article on &lt;a href="https://devcenter.heroku.com/articles/heroku-ci"&gt;Heroku CI&lt;/a&gt;. That was half of the CI/CD problem. In the article about CI/CD, she found a link to &lt;a href="https://devcenter.heroku.com/articles/pipelines"&gt;Heroku Pipelines&lt;/a&gt; and that completed the equation for her.&lt;/p&gt;

&lt;p&gt;Using this she would be able to use automated unit tests on her code, and deploy working branches to automatically provisioned and managed development environments! She got to work.&lt;/p&gt;

&lt;p&gt;Her first task was to set up her first app. She decided to start with the staging environment, as the production app would be added after User Acceptance Testing (UAT)&lt;/p&gt;

&lt;p&gt;She returned to her trusty command line.&lt;/p&gt;

&lt;p&gt;She first needed to commit the initial framework of the Rails app to git. Jane knew that using git and Github was a best practice, and it would allow her to leverage advanced Github and Heroku integrations later.&lt;/p&gt;

&lt;p&gt;She used the following commands to insure that git was set up correctly, add all of the files, and then commit them locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ git status

~/Code/uc_blog ☯ git add .

~/Code/uc_blog ☯ git commit -m "First Commit"

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

&lt;/div&gt;



&lt;p&gt;She then created a private repository on Github to hold her code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ong0l6v6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-github-new-repo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ong0l6v6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-github-new-repo.png" alt="Uc Day 1 Github New Repo" width="800" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;She added the git remote and pushed her code to Github:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ git remote add origin git@github.com:tsykoduk/refactored-robot.git

~/Code/uc_blog ☯ git pull --allow-unrelated-histories origin master

~/Code/uc_blog ☯ git push origin master

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

&lt;/div&gt;



&lt;p&gt;Jane was then ready to deploy the skeleton of her application into Heroku. She knew that she would need a build environment to transform her code into an executable format, that she would need containers to run her code, she would need a container orchestrations system to manage the containers and insure they were self healing, and finally she would need load balancers to get web traffic to her app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ heroku login
heroku: Press any key to open up the browser to login or q to exit: 
Opening browser to https://cli-auth.heroku.com/auth/cli/browser/fed60
Logged in as jane

~/Code/uc_blog ☯ heroku create
Creating app... done, ⬢ murmuring-reef-21813
https://murmuring-reef-21813.herokuapp.com/ | https://git.heroku.com/murmuring-reef-21813.git

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

&lt;/div&gt;



&lt;p&gt;And that was all there was to that! She had just done all of the work needed to setup the environment to build, run and manage her application.&lt;/p&gt;

&lt;p&gt;Time to grab a cup of coffee!&lt;/p&gt;

&lt;p&gt;When Jane got back from meeting some of her new coworkers and getting some coffee, she put her headphones back on. She had a bit to do before lunch!&lt;/p&gt;

&lt;p&gt;She decided that she would first deploy the skeleton of the app, and then set up Pipelines and Review Apps, and then hook it all together with Github.&lt;/p&gt;

&lt;p&gt;Jane first headed over to the Elements Marketplace, as she knew that her application would need some backing services. She knew that she would need PostgresSQL, and a logging tool. She also chose to install a Redis instance, as she could. use that as a cache with Rails to speed up application performance. With that knowledge in hand, she switched back to her Terminal used the Heroku CLI to provision Postgres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ heroku addons:create heroku-postgresql:standard-0
Creating heroku-postgresql:standard-0 on ⬢ murmuring-reef-21813... $50/month
The database should be available in 3-5 minutes.
! CAUTION: The database will be empty. If upgrading, you can transfer
! data from another database with pg:copy.
Use `heroku pg:wait` to track status.
postgresql-defined-69446 is being created in the background. The app will restart when complete...
Use heroku addons:info postgresql-defined-69446 to check creation progress
Use heroku addons:docs heroku-postgresql to view documentation

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

&lt;/div&gt;



&lt;p&gt;And next up was Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ heroku addons:create heroku-redis:premium-0 
Creating heroku-redis:premium-0 on ⬢ murmuring-reef-21813... $15/month
Your add-on should be available in a few minutes.
redis-cubic-39558 is being created in the background. The app will restart when complete...
Use heroku addons:info redis-cubic-39558 to check creation progress
Use heroku addons:docs heroku-redis to view documentation

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

&lt;/div&gt;



&lt;p&gt;And finally Papertrail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ heroku addons:create papertrail:choklad
Creating papertrail:choklad on ⬢ murmuring-reef-21813... free
Welcome to Papertrail. Questions and ideas are welcome (support@papertrailapp.com). Happy logging!
Created papertrail-acute-05007 as PAPERTRAIL_API_TOKEN
Use heroku addons:docs papertrail to view documentation

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

&lt;/div&gt;



&lt;p&gt;That would give her new Rails app what it needed to be successful from the start!&lt;/p&gt;

&lt;p&gt;Now it was time to package up her code, send it to the build environment, and then move the assembled slug into a container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Code/uc_blog ☯ git push heroku master
Enumerating objects: 113, done.
Counting objects: 100% (113/113), done.
Delta compression using up to 8 threads
Compressing objects: 100% (94/94), done.
Writing objects: 100% (113/113), 148.28 KiB | 5.49 MiB/s, done.
Total 113 (delta 9), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
[...]
remote: -----&amp;gt; Launching...
remote: Released v8
remote: https://murmuring-reef-21813.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/murmuring-reef-21813.git
 * [new branch] master -&amp;gt; master

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

&lt;/div&gt;



&lt;p&gt;Her new app was up and running. She opened a web browser and browsed to the new app’s address:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tPwpxO-D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-old-rails.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tPwpxO-D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-old-rails.png" alt="Uc Day 1 Old Rails" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Her final task was to set up the pipeline and development environments. She knew that she would have to create a pipeline, integrate it with GitHub, and enable CI and Review Apps.&lt;/p&gt;

&lt;p&gt;She opened the dashboard, and selected the new app. She then selected Create a Pipeline. She then answered a few questions, and the pipeline was created for her.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2R5uxrjQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-new-pipeline.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2R5uxrjQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-new-pipeline.png" alt="Uc Day 1 New Pipeline" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next she clicked on the Enable Review Apps button, and selected a few options&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j4Q1KvQ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-review-app.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j4Q1KvQ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-review-app.png" alt="Uc Day 1 Review App" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, she clicked on Enable CI&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8FV7aPGU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-ci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8FV7aPGU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://greg.nokes.name/wp-content/uploads/2019/04/uc-day-1-ci.png" alt="Uc Day 1 Ci" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All done. In the course of a single morning, and on her first day at work, Jane had set up a Heroku App and all of the load balancers, build environments, containers that come with that allow someone to run an application on the internet. She had also configured and set up a full CI/CD pipeline including GitHub integration.&lt;/p&gt;

&lt;p&gt;Now she could roll up her sleeves, and get started building the app.&lt;/p&gt;

&lt;p&gt;But first, Lunch!&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>devops</category>
      <category>gettingstarted</category>
    </item>
    <item>
      <title>Setting up DHCPD on MacOS</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Sat, 28 Mar 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/tsykoduk/setting-up-isc-s-dhcpd-on-macos-3fna</link>
      <guid>https://forem.com/tsykoduk/setting-up-isc-s-dhcpd-on-macos-3fna</guid>
      <description>&lt;p&gt;One of the frustrations with my current network setup is that the router is very inflexible. For example, it would not allow me to set the Domain Name servers that I wanted to use for my internal devices. It would only deliver the DNS servers that my internet provider delivered to it. And who wants that?&lt;/p&gt;

&lt;p&gt;Well, since I have set up many DHCP servers in the past, I did not think this would be a problem. I have an older Mac that I could use, as it stays plugged into the network and is already operating as a Apple iCloud Cache server.&lt;/p&gt;

&lt;p&gt;I also wanted to be able to deliver IPv6 DNS servers to my machines as well.&lt;/p&gt;

&lt;p&gt;Well, it was not quite as easy as I thought it was going to be. I had to learn how to use &lt;code&gt;launchctl&lt;/code&gt; and &lt;code&gt;launchd&lt;/code&gt;. I come from a background of using &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;systemd&lt;/code&gt; so I &lt;em&gt;assumed&lt;/em&gt; this would be fairly easy.&lt;/p&gt;

&lt;p&gt;Here are the steps that I found to work on a machine running 10.15. I set up IPv4 first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install the DHCP server via brew.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ ☯ brew install isc-dhcp

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up the IPv4 plist and config files.&lt;/p&gt;

&lt;p&gt;The config file was pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/local/etc ☯ cat dhcpd.conf 
# dhcpd.conf
#

default-lease-time 600;
max-lease-time 7200;

# Use this to enble / disable dynamic dns updates globally.
#ddns-update-style none;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
#log-facility local7;

# My First Subnet
subnet 192.168.1.0 netmask 255.255.255.0 {
  range 192.168.1.10 192.168.1.200;
  option domain-name-servers 1.1.1.1, 1.0.0.1;
  option routers 192.168.1.1;
  option broadcast-address 192.168.1.255;

  # Let's let folks keep their IP's for a while
  default-lease-time 6000;
  max-lease-time 72000;
}

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

&lt;/div&gt;



&lt;p&gt;Next up was getting it installed as a service. The &lt;code&gt;brew&lt;/code&gt; stuff was unhelpful, and, honestly I wanted to play with the underlying plist files anyways.&lt;/p&gt;

&lt;p&gt;After some gnashing of teeth and frantic googling, I came across a helpful article by &lt;a href="https://stevendiver.com/2020/02/19/install-isc-dhcp-on-macos-catalina/"&gt;Steven Diver&lt;/a&gt; that outlined the syntax of the &lt;code&gt;org-isc-dhcpd.plist&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Anyway, this is what I came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Library/LaunchDaemons ☯ cat org.isc.dhcpd-6.plist 
&amp;lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"&amp;gt;
&amp;lt;plist version=\"1.0\"&amp;gt;
&amp;lt;dict&amp;gt;
     &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;org.isc.dhcpd&amp;lt;/string&amp;gt;
     &amp;lt;key&amp;gt;OnDemand&amp;lt;/key&amp;gt;
          &amp;lt;false/&amp;gt;
     &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
          &amp;lt;array&amp;gt;
          &amp;lt;string&amp;gt;/usr/local/sbin/dhcpd&amp;lt;/string&amp;gt;
          &amp;lt;string&amp;gt;-q&amp;lt;/string&amp;gt;
          &amp;lt;string&amp;gt;-f&amp;lt;/string&amp;gt;
          &amp;lt;/array&amp;gt;
     &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
          &amp;lt;true/&amp;gt;
     &amp;lt;key&amp;gt;ServiceDescription&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;ISC DHCP Server&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Next I figured out that I had to load the plist, and then it would hopefully start up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ ☯ sudo launchctl load /Library/LaunchDaemons/org.isc.dhcpd.plist

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

&lt;/div&gt;



&lt;p&gt;I could then use &lt;code&gt;sudo launchctl list |grep dhcp&lt;/code&gt; to see that the service was in fact started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ ☯ sudo launchctl list |grep dhcp
47891   -15 org.isc.dhcpd

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

&lt;/div&gt;



&lt;p&gt;I also rebooted the machine to insure that it would survive a reboot - no one wants their DHCP server vanishing, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next up was setting up IPv6.&lt;/p&gt;

&lt;p&gt;I did a bit of reading, and figured out that you would have to have 2 DHCPD services running, one for IPv4 and one for IPv6. I also learned a bit about &lt;a href="https://en.wikipedia.org/wiki/IPv6#Stateless_address_autoconfiguration_(SLAAC)"&gt;IPv6 and SLAAC&lt;/a&gt;. It seemed that I could keep using my ISP’s SLAAC to hand out proper addresses, and use DHCP to deliver additional information to the hosts, like a proper DNS server’s address.&lt;/p&gt;

&lt;p&gt;This is the config file that I came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/local/etc ☯ cat dhcpd6.conf 
# dhcpd6.conf

default-lease-time 600;
max-lease-time 7200;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
# log-facility local7;

# Subnet declaration

subnet6 2601:601:cc00:370::/64 {
        # keep it simple, just hand out DNS
        option dhcp6.name-servers 2606:4700:4700::1111, 2606:4700:4700::1001;
}

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

&lt;/div&gt;



&lt;p&gt;And the plist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Library/LaunchDaemons ☯ cat org.isc.dhcpd-6.plist 
&amp;lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"&amp;gt;
&amp;lt;plist version=\"1.0\"&amp;gt;
&amp;lt;dict&amp;gt;
     &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;org.isc.dhcpd-6&amp;lt;/string&amp;gt;
     &amp;lt;key&amp;gt;OnDemand&amp;lt;/key&amp;gt;
          &amp;lt;false/&amp;gt;
     &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
          &amp;lt;array&amp;gt;
          &amp;lt;string&amp;gt;/usr/local/sbin/dhcpd&amp;lt;/string&amp;gt;
          &amp;lt;string&amp;gt;-f&amp;lt;/string&amp;gt;
      &amp;lt;string&amp;gt;-6&amp;lt;/string&amp;gt;
          &amp;lt;string&amp;gt;-f&amp;lt;/string&amp;gt;
      &amp;lt;string&amp;gt;-cf&amp;lt;/string&amp;gt;&amp;lt;string&amp;gt;/usr/local/etc/dhcpd6.conf&amp;lt;/string&amp;gt;
      &amp;lt;string&amp;gt;-lf&amp;lt;/string&amp;gt;&amp;lt;string&amp;gt;/usr/local/var/dhcpd/dhcpd6.leases&amp;lt;/string&amp;gt;
          &amp;lt;/array&amp;gt;
     &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
          &amp;lt;true/&amp;gt;
     &amp;lt;key&amp;gt;ServiceDescription&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;ISC DHCP-6 Server&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;The only thing left was to load the plist and see if this all worked. Again, with the reboot test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ ☯ sudo launchctl list |grep dhcp
47891   -15 org.isc.dhcpd
48425   0   org.isc.dhcpd-6

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

&lt;/div&gt;



&lt;p&gt;I also restarted the network interface on another device, and watched the logs on the server to see if this all worked together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dhcpd: Information-request message from a0a0::a0a0:a0a0:a0a0:a0a0 port 546, transaction ID 0x158E3D00
dhcpd: Sending Reply to a0a0::a0a0:a0a0:a0a0:a0a0 port 546
dhcpd: DHCPREQUEST for 192.168.1.49 from a0:a0:a0:a0:a0:a0 via en6
dhcpd: DHCPACK on 192.168.1.49 to a0:a0:a0:a0:a0:a0 via en6

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

&lt;/div&gt;



&lt;p&gt;Presto, devices started to use the correct DNS servers.&lt;/p&gt;

</description>
      <category>dhcpd</category>
      <category>macos</category>
      <category>dns</category>
      <category>networking</category>
    </item>
    <item>
      <title>Reflections on 12 years of 100% remote work</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/tsykoduk/reflections-on-12-years-of-100-remote-work-3epd</link>
      <guid>https://forem.com/tsykoduk/reflections-on-12-years-of-100-remote-work-3epd</guid>
      <description>&lt;p&gt;I started working 100% remote in early 2008. Over the years I have learned quite a few lessons, and have really come to appreciate the lifestyle.&lt;/p&gt;

&lt;p&gt;I have worked as member of a majority remote company, as one of 7 people that made up the 10% of the company that was remote, and as a member of a fully remote team.&lt;/p&gt;

&lt;p&gt;Honestly, early on it was pretty tough. There were tons of interruptions, a lot of stress and almost no work/life balance. Over the years I have come to realize that working from home can bring isolation, withdrawal and a feeling of disconnection.&lt;/p&gt;

&lt;p&gt;I have a few go-to tactics that I make sure that I employ to combat these issues. As I discovered them over the years, my appreciation for remote work has increased to the point where I am not sure that I would take an office job in the future.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dedicated Space&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is one of the first things that I discovered. I needed to set up a dedicated work space, with a door that closes, comfortable furniture, a sound system, good keyboards, microphones, cameras, and all of the other essentials.&lt;/p&gt;

&lt;p&gt;I started years ago with a nice computer desk and a comfy chair in a spare bedroom.&lt;/p&gt;

&lt;p&gt;Over the years, I have moved from a chair, to a yoga ball, to a standing desk. I have switched to full time at a standing desk. That has made a big difference as well. When I need a short break, I can simply walk out of the office, and do a lap around the kitchen.&lt;/p&gt;

&lt;p&gt;I have some co-workers that go so far as install sound proofing in their offices, or build unattached offices in their back yards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dedicated Work Environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I personally have found that having a distraction free work environment is important. So I have two computers on my desk. One that is dedicated to work - email, coding, chats, and calls. And a machine that is dedicated to my personal stuff - personal email, social media, and music.&lt;/p&gt;

&lt;p&gt;I started with a work laptop and a 1st Gen Mac Mini as my personal computer. That was plugged into the speakers, and ran music, personal email etc. I had a KVM switch, and used that to swap my monitor, keyboard and mouse between the Mac Mini and my work laptop.&lt;/p&gt;

&lt;p&gt;Now I have a laptop riser, a big monitor, a Yeti Blue mic on a boom, and my personal iPad for music and distractions.&lt;/p&gt;

&lt;p&gt;A few years ago, I was forced to get a second cell phone for work, and that has been great. Now when I quit working for the day, I simply drop the work phone on the charger, and walk away. There is a lot less temptation to “just check my work email once before bed”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dedicated Hours&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having time set aside for “work hours”, and making sure that I take breaks is very important. It’s easy to get hyper focused on something and forget to eat, or spend the day in PJ’s by mistake.&lt;/p&gt;

&lt;p&gt;I make sure that I set aside time to make myself human, and that I dress for work. Does that mean buttoned down? No, I would wear a tee-shirt and jeans into the office, so working from home is no different.&lt;/p&gt;

&lt;p&gt;I also try hard to go work from a coffee shop a few times a week. Or at least drive to a coffee shop, get out of the car, stand in line and get something. Interaction with other humans is important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Video On&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also try and have meetings with my video on. It’s more for me then for anyone else. I try and make sure that my workspace and myself are presentable every day. Good back lighting is important. I know that since I face a window, I look better on sunny day then I do on cloudy ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Healthy Snacks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one took a few years and a few pounds before I got it. Having healthy lunches and snacks around the house is important. Not having boxes of Oreos is also important.&lt;/p&gt;

&lt;p&gt;I used to have pretty bland lunches. I have really tried to up my game. One of the things that helps is blocking out 45 minutes or so for lunch, so I have time to make something good, and not feel rushed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Water coolers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having some sort of “water cooler” chat system is important. Over the years, I have seen IRC, Campfire, HipChat, Slack and Quip Chat in this role. As long as everyone has a “safe-ish” space to talk about coffee, dogs, weather, space, or what ever crosses their mind then this need is met. I have seen gaming groups, music chatter, coffee talk and technology randomness.&lt;/p&gt;

&lt;p&gt;What’s important is a space for folks to talk with each other about work, work related, and not work related things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meetups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Meeting up with coworkers on the same team is vitally important as well. In the past I would spend a week every few months at HQ, to wander the halls, get face time with folks, and generally catch up.&lt;/p&gt;

&lt;p&gt;Team off sites are also important. Some of the best team building times that I have had were at off sites. It does not need to be something fancy. A lot of the time we would all head to the same conference, and make some time to catch up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeways&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In closing, working remote takes a bit of thought and work, but it’s a great life style. I can take the time that I would loose to a commute, and get caught up on news, brew a Chemex of coffee, or even sleep a little more. The disconnection can be scary at first, however with some adjustments the focus that you can gain can return large productivity gains.&lt;/p&gt;

&lt;p&gt;All it takes is discipline and a slightly diffrent way of looking at work/life balance.&lt;/p&gt;

</description>
      <category>remote</category>
    </item>
    <item>
      <title>Nginx Routing on Heroku</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Thu, 27 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/nginx-routing-on-heroku-567j</link>
      <guid>https://forem.com/heroku/nginx-routing-on-heroku-567j</guid>
      <description>&lt;p&gt;Sometimes, we want to be able to route requests based on URL paths easily, or have a front end app that serves data from private back end services. &lt;/p&gt;

&lt;p&gt;For example, if you have several services in a Private Space that serve HTML, you might not want to expose them to the internet, and rather have a routing app in front which will handle this. Or you might want to route your asset delivery to S3, and completely offload that processing from the local app server. Nginx is a powerful, fast and lightweight web server which can also operate as a reverse proxy. It also runs well on Heroku.&lt;/p&gt;

&lt;p&gt;For this example, we will set up 3 apps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ggn-nginx-router-test&lt;/li&gt;
&lt;li&gt;ggn-nginx-test-target&lt;/li&gt;
&lt;li&gt;ggn-nginx-test-blog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ggn-nginx-router-test&lt;/code&gt; will be running Nginx, with configs set to route requests back to the target app. &lt;code&gt;ggn-nginx-test-target&lt;/code&gt; will simply deliver HTML with the requestors HTML headers. &lt;code&gt;ggn-nginx-test-blog&lt;/code&gt; will simply be a second HTML target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup &lt;code&gt;nginx-test-target&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install PHP and Composer Locally (&lt;code&gt;brew install php &amp;amp;&amp;amp; brew install composer&lt;/code&gt; on a Mac)&lt;/p&gt;

&lt;p&gt;Set up your working directory in your code directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;☯ mkdir test-target
☯ cd test-target
☯ git init .
☯ heroku create ggn-nginx-test-target

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

&lt;/div&gt;



&lt;p&gt;Set up a &lt;code&gt;.gitignore&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;☯ cat .gitignore 
/vendor/

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

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;Procfile&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;☯ cat procfile
web: vendor/bin/heroku-php-apache2

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

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;composer.json&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;☯ cat composer.json 
{
  "require" : {
  },
  "require-dev": {
    "heroku/heroku-buildpack-php": "*"
  }
}

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

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;composer update&lt;/code&gt; and then create an &lt;code&gt;index.php&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;☯ cat index.php 
&amp;lt;h1&amp;gt;This is not a Blog&amp;lt;br /&amp;gt;
Requestor Headers:&amp;lt;/h1&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;?php 

foreach (getallheaders() as $name =&amp;gt; $value) {
    echo "$name: $value\n&amp;lt;br /&amp;gt;";
}
?&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setup &lt;code&gt;nginx-router-test&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up your working directory in your code directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;☯ mkdir router-target
☯ cd router-target
☯ git init .
☯ heroku create ggn-nginx-router-test

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

&lt;/div&gt;



&lt;p&gt;Add the nginx buildpack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;☯ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-nginx
Buildpack added. Next release on ggn-nginx-router-test will use heroku-community/nginx.
Run git push heroku master to create a new release using this buildpack.

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

&lt;/div&gt;



&lt;p&gt;Add a &lt;code&gt;Procfile&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;☯ cat Procfile
web: bin/start-nginx-solo

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

&lt;/div&gt;



&lt;p&gt;Add a Nginx config file. We use an &lt;code&gt;.erb&lt;/code&gt; file as the build pack will process it, and generate a finished config file out of it. It needs to go in &lt;code&gt;config/nginx.conf.erb&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;☯ cat config/nginx.conf.erb 
daemon off;
# Heroku dynos have at least 4 cores
worker_processes &amp;lt;%= ENV['NGINX_WORKERS'] || 4 %&amp;gt;;

events {
  use epoll;
  accept_mutex on;
  worker_connections &amp;lt;%= ENV['NGINX_WORKER_CONNECTIONS'] || 1024 %&amp;gt;;
}

http {
  gzip on;
  gzip_comp_level 2;
  gzip_min_length 512;

  server_tokens off;

  log_format main '$time_iso8601 - $status $request - client IP: $http_x_forwarded_for - &amp;lt;%= ENV['DYNO'] %&amp;gt; to $upstream_addr - upstream status: $upstream_status, upstream_response_time $upstream_response_time, request_time $request_time';
  access_log /dev/stdout main;
  error_log /dev/stdout notice;
  log_not_found on;

  include mime.types;
  default_type application/octet-stream;
  sendfile on;

  # Must read the body in 5 seconds.
  client_body_timeout &amp;lt;%= ENV['NGINX_CLIENT_BODY_TIMEOUT'] || 5 %&amp;gt;;

  upstream upstream_production {
    server ggn-nginx-test-target.herokuapp.com;
  }

  server {
    listen &amp;lt;%= ENV["PORT"] %&amp;gt;;
    server_name _;

    location / {
      set $upstream upstream_production;
      proxy_pass http://$upstream;
      proxy_set_header x-forwarded-host $host;
      proxy_set_header Host ggn-nginx-test-target.herokuapp.com;
    }

  }
}

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

&lt;/div&gt;



&lt;p&gt;You should now be able to hit &lt;a href="https://ggn-nginx-router-test.herokuapp.com"&gt;ggn-nginx-router-test.herokuapp.com&lt;/a&gt; and get the app living at &lt;a href="https://ggn-nginx-test-target.herokuapp.com"&gt;ggn-nginx-test-target.herokuapp.com&lt;/a&gt; delivered to you.&lt;/p&gt;

&lt;p&gt;Or you can simply deploy this repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup &lt;code&gt;nginx-test-blog&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up your working directory in your code directory and create a &lt;code&gt;.gitignore&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;☯ mkdir test-blog
☯ cd test-blog
☯ git init .
☯ heroku create ggn-nginx-test-blog


☯ cat .gitignore 
/vendor/

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

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;Procfile&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;☯ cat procfile
web: vendor/bin/heroku-php-apache2

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

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;composer.json&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;☯ cat composer.json 
{
  "require" : {
  },
  "require-dev": {
    "heroku/heroku-buildpack-php": "*"
  }
}

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

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;composer update&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;index.php&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;☯ cat index.php 
&amp;lt;h1&amp;gt;This is a Blog!&amp;lt;br /&amp;gt;
Requestor Headers:&amp;lt;/h1&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;?php 

foreach (getallheaders() as $name =&amp;gt; $value) {
    echo "$name: $value\n&amp;lt;br /&amp;gt;";
}
?&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Adding in additional routes is as simple as setting up new location blocks and their corresponding upstream blocks. For example you could add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  upstream upstream_blog {
    server ggn-nginx-test-blog.herokuapp.com;
  }

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

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    location /blog/ {
      set $upstream upstream_blog;
      proxy_pass http://$upstream/;
      proxy_set_header x-forwarded-host $host;
      proxy_set_header Host ggn-nginx-test-blog.herokuapp.com;
    }

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

&lt;/div&gt;



&lt;p&gt;Now when you visit &lt;a href="https://ggn-nginx-router-test.herokuapp.com/blog/"&gt;ggn-nginx-router-test.herokuapp.com/blog/&lt;/a&gt; you will get the contents of &lt;a href="https://ggn-nginx-router-blog.herokuapp.com/"&gt;ggn-nginx-router-blog.herokuapp.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the config directory of this &lt;a href="https://github.com/tsykoduk/Heroku-Nginx-Reverse-Proxy"&gt;repo on github&lt;/a&gt; is a sample &lt;code&gt;nginx.conf.erb&lt;/code&gt; which enables this example.&lt;/p&gt;

&lt;p&gt;In closing, we created three applications on Heroku. One was a proxy and router, and the other two were backing services. The proxy examines the URL, and based on the path, renders one of the two backing services. This is a small example of the many uses of Nginx on Heroku. More use cases include delivering static content and reverse proxying into a Private Space.&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>heroku</category>
    </item>
    <item>
      <title>Nginx as a static site server on Heroku</title>
      <dc:creator>Greg Nokes</dc:creator>
      <pubDate>Mon, 07 Oct 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/heroku/nginx-as-a-static-site-server-on-heroku-4pc8</link>
      <guid>https://forem.com/heroku/nginx-as-a-static-site-server-on-heroku-4pc8</guid>
      <description>&lt;p&gt;I was doing some performance testing on my blog, and while I was impressed, I felt like I could wring some more speed out of it. I decided to switch from Heroku’s &lt;a href="https://github.com/heroku/heroku-buildpack-static"&gt;Static Buildpack&lt;/a&gt; to a simple Nginx webserver using Heroku’s nice &lt;a href="https://github.com/heroku/heroku-buildpack-nginx"&gt;Nginx Buildpack&lt;/a&gt;. In theory this should be a little quicker and lighter.&lt;/p&gt;

&lt;p&gt;The first step was to get Nginx set up on the Heroku App. I had the static buildpack already, so I simply had to remove it and add the Nginx Buildpack.&lt;/p&gt;

&lt;p&gt;The next step is to modify the &lt;code&gt;Procfile&lt;/code&gt; to run Ngnix in Solo mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: bin/start-nginx-solo 

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

&lt;/div&gt;



&lt;p&gt;Then I created a &lt;code&gt;config&lt;/code&gt; directory and put my &lt;code&gt;nginx.conf.erb&lt;/code&gt; file in it. The Nginx buildpack will grab that file, process it, and then use it to run Nginx on the dyno.&lt;/p&gt;

&lt;p&gt;I found this config to work well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;daemon&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;;
&lt;span class="c"&gt;# Heroku dynos have at least 4 cores
&lt;/span&gt;&lt;span class="n"&gt;worker_processes&lt;/span&gt; &amp;lt;%= &lt;span class="n"&gt;ENV&lt;/span&gt;[&lt;span class="s1"&gt;'NGINX_WORKERS'&lt;/span&gt;] || &lt;span class="m"&gt;4&lt;/span&gt; %&amp;gt;;
&lt;span class="n"&gt;events&lt;/span&gt; {
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;epoll&lt;/span&gt;;
  &lt;span class="n"&gt;accept_mutex&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;
  &lt;span class="n"&gt;worker_connections&lt;/span&gt; &amp;lt;%= &lt;span class="n"&gt;ENV&lt;/span&gt;[&lt;span class="s1"&gt;'NGINX_WORKER_CONNECTIONS'&lt;/span&gt;] || &lt;span class="m"&gt;1024&lt;/span&gt; %&amp;gt;;
}

&lt;span class="n"&gt;http&lt;/span&gt; {
  &lt;span class="n"&gt;gzip&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_comp_level&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_min_length&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;;

  &lt;span class="n"&gt;server_tokens&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;;

  &lt;span class="n"&gt;log_format&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="s1"&gt;'$time_iso8601 - $status $request - client IP: $http_x_forwarded_for - &amp;lt;%= ENV['&lt;/span&gt;&lt;span class="n"&gt;DYNO&lt;/span&gt;&lt;span class="s1"&gt;'] %&amp;gt; to $upstream_addr - upstream status: $upstream_status, upstream_response_time $upstream_response_time, request_time $request_time'&lt;/span&gt;;
  &lt;span class="n"&gt;access_log&lt;/span&gt; /&lt;span class="n"&gt;dev&lt;/span&gt;/&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;;
  &lt;span class="n"&gt;error_log&lt;/span&gt; /&lt;span class="n"&gt;dev&lt;/span&gt;/&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="n"&gt;notice&lt;/span&gt;;
  &lt;span class="n"&gt;log_not_found&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;
  &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt;.&lt;span class="n"&gt;types&lt;/span&gt;;

  &lt;span class="n"&gt;default_type&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;/&lt;span class="n"&gt;octet&lt;/span&gt;-&lt;span class="n"&gt;stream&lt;/span&gt;;
  &lt;span class="n"&gt;sendfile&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;

  &lt;span class="c"&gt;# Must read the body in 5 seconds.
&lt;/span&gt;  &lt;span class="n"&gt;client_body_timeout&lt;/span&gt; &amp;lt;%= &lt;span class="n"&gt;ENV&lt;/span&gt;[&lt;span class="s1"&gt;'NGINX_CLIENT_BODY_TIMEOUT'&lt;/span&gt;] || &lt;span class="m"&gt;5&lt;/span&gt; %&amp;gt;;

  &lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;listen&lt;/span&gt; &amp;lt;%= &lt;span class="n"&gt;ENV&lt;/span&gt;[&lt;span class="s2"&gt;"PORT"&lt;/span&gt;] %&amp;gt;;

    &lt;span class="n"&gt;error_page&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; /&lt;span class="m"&gt;404&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;;
  &lt;span class="n"&gt;error_page&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt; /&lt;span class="m"&gt;403&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;;

    &lt;span class="n"&gt;port_in_redirect&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt;;

    &lt;span class="n"&gt;location&lt;/span&gt; / {
        &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;/;
    }
  }
}

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

&lt;/div&gt;



&lt;p&gt;The important parts are the &lt;code&gt;port_in_redirect off;&lt;/code&gt; and the &lt;code&gt;root _site/;&lt;/code&gt; directives. The &lt;code&gt;port_in_redirect&lt;/code&gt; tells Nginx to not embed it's port into any redirects, and &lt;code&gt;root&lt;/code&gt; tells Nginx where the static files are.&lt;/p&gt;

&lt;p&gt;Well, is it fast?&lt;/p&gt;

&lt;p&gt;I ran &lt;code&gt;ab&lt;/code&gt; on the site before and after, and there is a large improvement. Larger than I expected.&lt;/p&gt;

&lt;p&gt;The old configuration had a perc99 under load of 1.7 seconds per request, and the new has a perc99 of 0.6 seconds. More than a second per request faster!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old Config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level: 100
Time taken for tests: 7.284 seconds
Complete requests: 5000

Failed requests: 0
Total transferred: 6922000 bytes
HTML transferred: 6722000 bytes
Requests per second: 137.28 [#/sec] (mean)
Time per request: 728.426 [ms] (mean)
Time per request: 7.284 [ms] (mean, across all concurrent requests)
Transfer rate: 928.00 [Kbytes/sec] received

Connection Times (ms)
              min mean[+/-sd] median max
Connect: 307 449 233.8 380 1612
Processing: 103 204 84.1 212 1026
Waiting: 103 201 83.5 211 1026
Total: 424 653 247.2 591 1939

Percentage of the requests served within a certain time (ms)
  50% 591
  66% 627
  75% 658
  80% 670
  90% 822
  95% 1084
  98% 1738
  99% 1769
 100% 1939 (longest request)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New Config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level: 100
Time taken for tests: 26.680 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 34870000 bytes
HTML transferred: 33650000 bytes
Requests per second: 187.41 [#/sec] (mean)
Time per request: 533.592 [ms] (mean)
Time per request: 5.336 [ms] (mean, across all concurrent requests)
Transfer rate: 1276.36 [Kbytes/sec] received

Connection Times (ms)
              min mean[+/-sd] median max
Connect: 297 385 42.1 378 620
Processing: 102 136 33.6 122 641
Waiting: 102 133 30.5 120 641
Total: 424 520 51.2 506 986

Percentage of the requests served within a certain time (ms)
  50% 506
  66% 524
  75% 536
  80% 544
  90% 577
  95% 643
  98% 685
  99% 696
 100% 986 (longest request)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But what about SSL?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the things that I really liked about the Static Buildpack is that it is based on Rack, and I could use the &lt;code&gt;rack-ssl-enforcer&lt;/code&gt; gem to make sure that all requests were directed to the encrypted version of the site. I did some investigation and found the following stanza in the server block would work with the Heroku router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;if&lt;/span&gt; ($&lt;span class="n"&gt;http_x_forwarded_proto&lt;/span&gt; != &lt;span class="s2"&gt;"https"&lt;/span&gt;) {
  &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="m"&gt;301&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;://$&lt;span class="n"&gt;host&lt;/span&gt;$&lt;span class="n"&gt;request_uri&lt;/span&gt;;
}

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

&lt;/div&gt;



&lt;p&gt;A redirect would be preferable, however since we are behind the Heroku Router, a simple redirect seems to enter into an infinite redirect loop. The &lt;code&gt;if&lt;/code&gt; loop slows down processing considerably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SSL Redirect&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level: 100
Time taken for tests: 26.618 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 34870000 bytes
HTML transferred: 33650000 bytes
Requests per second: 187.85 [#/sec] (mean)
Time per request: 532.352 [ms] (mean)
Time per request: 5.324 [ms] (mean, across all concurrent requests)
Transfer rate: 1279.33 [Kbytes/sec] received

Connection Times (ms)
              min mean[+/-sd] median max
Connect: 305 385 40.6 381 1618
Processing: 101 134 31.4 123 471
Waiting: 101 131 28.0 120 469
Total: 422 519 48.5 510 1740

Percentage of the requests served within a certain time (ms)
  50% 510
  66% 523
  75% 533
  80% 544
  90% 569
  95% 596
  98% 630
  99% 704
 100% 1740 (longest request)

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

&lt;/div&gt;



&lt;p&gt;Still respectable, but you can see the impact of using the &lt;code&gt;if&lt;/code&gt; statement in the Nginx config. The perc99 is only a few milliseconds slower with the SSL redirect, but milliseconds count!&lt;/p&gt;

&lt;p&gt;I will continue to research and see if I can remove that &lt;code&gt;if&lt;/code&gt; statement, and find a cleaner way to implement this.&lt;/p&gt;

&lt;p&gt;One of the other uses for Nginx on Heroku is a on Dyno proxy and static file server. This experiment shows how efficient Nginx can be at hosting the static assets of a complex web application, like Ruby. Using Nginx as a front end to proxy requests, and serve static files offloads traffic from the language specific app server, and can improve overall performance of a web application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 10/10/19&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I quickly found out that I still needed the static buildpack in my development and staging environments. It’s used to build out the static assets that Nginx serves, after all.&lt;/p&gt;

&lt;p&gt;If you refer back to &lt;a href="https://greg-nokes-name.herokuapp.com/2018/02/26/how-i-do-it/"&gt;How I post an article&lt;/a&gt; you will know that I use a Heroku Pipeline to manage posting. The nice thing is that I don’t need the Static buildpack or Ruby on my production site, as the slug is promoted intact between the Staging and Production apps. So I have Ruby and the Static buildpack installed and running in staging, but only the Nginx buildpack associated with the production app.&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>heroku</category>
      <category>blog</category>
    </item>
  </channel>
</rss>
