<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.1">Jekyll</generator><link href="https://lukewaite.ca/feed.xml" rel="self" type="application/atom+xml" /><link href="https://lukewaite.ca/" rel="alternate" type="text/html" /><updated>2022-11-05T20:58:43+00:00</updated><id>https://lukewaite.ca/feed.xml</id><title type="html">Luke Waite</title><subtitle></subtitle><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><entry><title type="html">NVM Default Global Packages</title><link href="https://lukewaite.ca/posts/2018/01/17/nvm-default-global-packages.html" rel="alternate" type="text/html" title="NVM Default Global Packages" /><published>2018-01-17T10:45:00+00:00</published><updated>2018-01-17T10:45:00+00:00</updated><id>https://lukewaite.ca/posts/2018/01/17/nvm-default-global-packages</id><content type="html" xml:base="https://lukewaite.ca/posts/2018/01/17/nvm-default-global-packages.html">&lt;p&gt;Did you know that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nvm&lt;/code&gt; supports a list of &lt;a href=&quot;https://github.com/creationix/nvm/blob/master/README.md#default-global-packages-from-file-while-installing&quot;&gt;default global packages&lt;/a&gt; to include when installing
a new node version?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h3 id=&quot;default-global-packages-from-file-while-installing&quot;&gt;Default global packages from file while installing&lt;/h3&gt;

  &lt;p&gt;If you have a list of default packages you want installed every time you install a new version we support that too. You can add anything npm would accept as a package argument on the command line.&lt;/p&gt;

&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt; &lt;span class=&quot;c&quot;&gt;# $NVM_DIR/default-packages
&lt;/span&gt; 
 &lt;span class=&quot;n&quot;&gt;rimraf&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;inspect&lt;/span&gt;@&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;stevemao&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Personally, I use this to make sure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gulp-cli&lt;/code&gt; is installed in new node versions.&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="nvm" /><category term="nodejs" /><category term="npm" /><category term="development" /><category term="tips" /><summary type="html">Did you know that nvm supports a list of default global packages to include when installing a new node version? Default global packages from file while installing If you have a list of default packages you want installed every time you install a new version we support that too. You can add anything npm would accept as a package argument on the command line. # $NVM_DIR/default-packages rimraf object-inspect@1.0.2 stevemao/left-pad Personally, I use this to make sure gulp-cli is installed in new node versions.</summary></entry><entry><title type="html">Viewing the Logstash Dead Letter Queue in Kibana</title><link href="https://lukewaite.ca/posts/2017/10/13/viewing-logstash-dlq-in-kibana.html" rel="alternate" type="text/html" title="Viewing the Logstash Dead Letter Queue in Kibana" /><published>2017-10-13T09:00:00+00:00</published><updated>2017-10-13T09:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/10/13/viewing-logstash-dlq-in-kibana</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/10/13/viewing-logstash-dlq-in-kibana.html">&lt;p&gt;At &lt;a href=&quot;https://www.intouchinsight.com&quot;&gt;Intouch Insight&lt;/a&gt; our logging infrastructure is our holy
grail. The engineering team relies on it every day, so we need to keep
it up to snuff. I was lucky enough to be able to update our ELK cluster
this week to 5.6 - a huge upgrade from our previous stack running ES 2.3
and Kibana 4.&lt;/p&gt;

&lt;p&gt;One of the features I was most looking forward to was the &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/current/dead-letter-queues.html&quot;&gt;dead letter
queue&lt;/a&gt; that was &lt;a href=&quot;https://www.elastic.co/blog/logstash-lines-2017-05-16&quot;&gt;introduced in Logstash 5.5&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;The documentation surrounding the usage of the dead letter queue mostly
revolves around &lt;a href=&quot;https://www.elastic.co/guide/en/logstash/5.6/dead-letter-queues.html#dlq-example&quot;&gt;re-processing rejected events&lt;/a&gt;. I wasn’t
particularly interested in that use case; I just wanted to be able to
&lt;strong&gt;easily see when events were rejected&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; {
    &lt;span class=&quot;n&quot;&gt;dead_letter_queue&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;/usr/share/logstash/data/dead_letter_queue&quot;&lt;/span&gt;
    }
}

&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; {
    &lt;span class=&quot;c&quot;&gt;# First, we must capture the entire event, and write it to a new
&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# field; we&apos;ll call that field `failed_message`
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;ruby&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;event.set(&apos;failed_message&apos;, event.to_json())&quot;&lt;/span&gt;
    }

    &lt;span class=&quot;c&quot;&gt;# Next, we prune every field off the event except for the one we&apos;ve
&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# just created. Note that this does not prune event metadata.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;prune&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;whitelist_names&lt;/span&gt; =&amp;gt; [ &lt;span class=&quot;s2&quot;&gt;&quot;^failed_message$&quot;&lt;/span&gt; ]
    }

    &lt;span class=&quot;c&quot;&gt;# Next, convert the metadata timestamp to one we can parse with a
&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# date filter. Before conversion, this field is a Logstash::Timestamp.
&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# http://www.rubydoc.info/gems/logstash-core/LogStash/Timestamp
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;ruby&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;event.set(&apos;timestamp&apos;, event.get(&apos;[@metadata][dead_letter_queue][entry_time]&apos;).toString())&quot;&lt;/span&gt;
    }

    &lt;span class=&quot;c&quot;&gt;# Apply the date filter.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; =&amp;gt; [ &lt;span class=&quot;s2&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;ISO8601&quot;&lt;/span&gt; ]
    }

    &lt;span class=&quot;c&quot;&gt;# Pull useful information out of the event metadata provided by the dead
&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# letter queue, and add it to the new event.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;mutate&lt;/span&gt; {
        &lt;span class=&quot;n&quot;&gt;add_field&lt;/span&gt; =&amp;gt; {
            &lt;span class=&quot;s2&quot;&gt;&quot;message&quot;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;%{[@metadata][dead_letter_queue][reason]}&quot;&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;plugin_id&quot;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;%{[@metadata][dead_letter_queue][plugin_id]}&quot;&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;plugin_type&quot;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;%{[@metadata][dead_letter_queue][plugin_type]}&quot;&lt;/span&gt;
        }
    }
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;how-it-looks&quot;&gt;How it looks&lt;/h3&gt;
&lt;table class=&quot;image&quot;&gt;
	&lt;caption align=&quot;bottom&quot;&gt;Dead Letter Queue - Logs&lt;/caption&gt;
	&lt;tr&gt;&lt;td&gt;&lt;img src=&quot;/images/2017-10-13-dlq-kibana/logs-list.png&quot; alt=&quot;Dead Letter Queue - Logs&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;table class=&quot;image&quot;&gt;
	&lt;caption align=&quot;bottom&quot;&gt;Dead Letter Queue - Detail&lt;/caption&gt;
	&lt;tr&gt;&lt;td&gt;&lt;img src=&quot;/images/2017-10-13-dlq-kibana/logs-detail.png&quot; alt=&quot;Dead Letter Queue - Detail&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;I’d love to rewrite this as a codec that could be applied to the input,
but that’s currently not possible as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dead_letter_queue&lt;/code&gt; input plugin
&lt;a href=&quot;https://github.com/logstash-plugins/logstash-input-dead_letter_queue/issues/15&quot;&gt;doesn’t make any calls to the codec specified in it’s config&lt;/a&gt;.&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="logstash" /><category term="monitoring" /><category term="kibana" /><category term="dead-letter-queue" /><summary type="html">At Intouch Insight our logging infrastructure is our holy grail. The engineering team relies on it every day, so we need to keep it up to snuff. I was lucky enough to be able to update our ELK cluster this week to 5.6 - a huge upgrade from our previous stack running ES 2.3 and Kibana 4. One of the features I was most looking forward to was the dead letter queue that was introduced in Logstash 5.5. The Problem The documentation surrounding the usage of the dead letter queue mostly revolves around re-processing rejected events. I wasn’t particularly interested in that use case; I just wanted to be able to easily see when events were rejected. The Solution 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 input { dead_letter_queue { path =&amp;gt; &quot;/usr/share/logstash/data/dead_letter_queue&quot; } } filter { # First, we must capture the entire event, and write it to a new # field; we&apos;ll call that field `failed_message` ruby { code =&amp;gt; &quot;event.set(&apos;failed_message&apos;, event.to_json())&quot; } # Next, we prune every field off the event except for the one we&apos;ve # just created. Note that this does not prune event metadata. prune { whitelist_names =&amp;gt; [ &quot;^failed_message$&quot; ] } # Next, convert the metadata timestamp to one we can parse with a # date filter. Before conversion, this field is a Logstash::Timestamp. # http://www.rubydoc.info/gems/logstash-core/LogStash/Timestamp ruby { code =&amp;gt; &quot;event.set(&apos;timestamp&apos;, event.get(&apos;[@metadata][dead_letter_queue][entry_time]&apos;).toString())&quot; } # Apply the date filter. date { match =&amp;gt; [ &quot;timestamp&quot;, &quot;ISO8601&quot; ] } # Pull useful information out of the event metadata provided by the dead # letter queue, and add it to the new event. mutate { add_field =&amp;gt; { &quot;message&quot; =&amp;gt; &quot;%{[@metadata][dead_letter_queue][reason]}&quot; &quot;plugin_id&quot; =&amp;gt; &quot;%{[@metadata][dead_letter_queue][plugin_id]}&quot; &quot;plugin_type&quot; =&amp;gt; &quot;%{[@metadata][dead_letter_queue][plugin_type]}&quot; } } } How it looks Dead Letter Queue - Logs Dead Letter Queue - Detail Next steps I’d love to rewrite this as a codec that could be applied to the input, but that’s currently not possible as the dead_letter_queue input plugin doesn’t make any calls to the codec specified in it’s config.</summary></entry><entry><title type="html">PHPStorm Tips - Go to Implementation</title><link href="https://lukewaite.ca/posts/2017/08/21/phpstorm-go-to-implementation.html" rel="alternate" type="text/html" title="PHPStorm Tips - Go to Implementation" /><published>2017-08-21T22:00:00+00:00</published><updated>2017-08-21T22:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/08/21/phpstorm-go-to-implementation</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/08/21/phpstorm-go-to-implementation.html">&lt;p&gt;Laravel is a great framework; it’s easy to use and extend, and makes liberal
use of Interfaces so that you can write your own implementations and provide
them to the IoC Container.&lt;/p&gt;

&lt;div style=&quot;float:right&quot; class=&quot;video-container&quot;&gt;
        &lt;iframe width=&quot;100%&quot; height=&quot;100%&quot; src=&quot;https://www.youtube.com/embed/C0ZExLjMawo&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Unfortunately, the downside to this is navigability. Many times, your IDE detects
that a variable has either been type hinted, or has code comments indicating that
it is storing a particular Interface, rather than a concrete class, and this
sometimes makes navigation difficult.&lt;/p&gt;

&lt;p&gt;Allow me to introduce you to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Go to Implementation (CMD-OPT-B)&lt;/code&gt; in PHPStorm -
just one additional key press away from the most commonly used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Go to Declaration (CMD-B)&lt;/code&gt;
It’ll allow you to select a specific implementation of whatever abstract method
you’ve got a reference to.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;sample&quot;&gt;Sample&lt;/h2&gt;

&lt;p&gt;The following screenshot shows the utility of being able to drill down past
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdapterInterface&lt;/code&gt; into a specific implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt; inside of
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Filesystem::put()&lt;/code&gt; method.&lt;/p&gt;

&lt;table class=&quot;image&quot;&gt;
	&lt;caption align=&quot;bottom&quot;&gt;Go to Implementation - Filesystem.php&lt;/caption&gt;
	&lt;tr&gt;&lt;td&gt;&lt;img src=&quot;/images/2017-08-21-phpstorm-go-to-implementation/go-to-implementation-filesystem.php.png&quot; alt=&quot;Go to Implementation - Filesystem.php&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="phpstorm" /><category term="tip" /><category term="laravel" /><category term="ide" /><summary type="html">Laravel is a great framework; it’s easy to use and extend, and makes liberal use of Interfaces so that you can write your own implementations and provide them to the IoC Container. Unfortunately, the downside to this is navigability. Many times, your IDE detects that a variable has either been type hinted, or has code comments indicating that it is storing a particular Interface, rather than a concrete class, and this sometimes makes navigation difficult. Allow me to introduce you to Go to Implementation (CMD-OPT-B) in PHPStorm - just one additional key press away from the most commonly used Go to Declaration (CMD-B) It’ll allow you to select a specific implementation of whatever abstract method you’ve got a reference to.</summary></entry><entry><title type="html">AWS Aurora &amp;amp; CloudWatch Alarms</title><link href="https://lukewaite.ca/posts/2017/08/21/aws-aurora-monitoring.html" rel="alternate" type="text/html" title="AWS Aurora &amp;amp; CloudWatch Alarms" /><published>2017-08-21T22:00:00+00:00</published><updated>2017-08-21T22:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/08/21/aws-aurora-monitoring</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/08/21/aws-aurora-monitoring.html">&lt;p&gt;Today I learned an interesting lesson. If you’re using AWS Aurora, don’t set your alarms (CPU, memory, etc) on
an individual instance. The reason? Aurora instances, by their very nature, are somewhat transient:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If you have an issue with a given primary, Aurora may promote another instance to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WRITER&lt;/code&gt; role.&lt;/li&gt;
  &lt;li&gt;If you need to perform an in-place update, you can sometimes do so by creating a new set of instances, and promoting those manually.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In either case, you’ll either lose your alarms completely, if you forget to re-create them, or your alarms
for your primary instance will no longer be on your primary.&lt;/p&gt;

&lt;p&gt;It turns out that the metrics Aurora publishes also include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DBClusterIdentifier&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt; dimensions.&lt;/p&gt;

&lt;p&gt;If you choose that dimension group, then you can view metrics and set alarms based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WRITER&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;READER&lt;/code&gt; roles,
for whatever cluster you desire. This is much more fool-proof than setting alarms per-instance.&lt;/p&gt;

&lt;table class=&quot;image&quot;&gt;
	&lt;caption align=&quot;bottom&quot;&gt;Metrics by DBClusterIdentifier and Role&lt;/caption&gt;
	&lt;tr&gt;&lt;td&gt;&lt;img src=&quot;/images/2017-09-15-aurora-cloudwatch/select-cluster-role.png&quot; alt=&quot;Metrics by DBClusterIdentifier and Role&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="aws" /><category term="cloudwatch" /><category term="alarm" /><category term="monitoring" /><category term="aurora" /><summary type="html">Today I learned an interesting lesson. If you’re using AWS Aurora, don’t set your alarms (CPU, memory, etc) on an individual instance. The reason? Aurora instances, by their very nature, are somewhat transient: If you have an issue with a given primary, Aurora may promote another instance to your WRITER role. If you need to perform an in-place update, you can sometimes do so by creating a new set of instances, and promoting those manually. In either case, you’ll either lose your alarms completely, if you forget to re-create them, or your alarms for your primary instance will no longer be on your primary. It turns out that the metrics Aurora publishes also include DBClusterIdentifier and Role dimensions. If you choose that dimension group, then you can view metrics and set alarms based on the WRITER or READER roles, for whatever cluster you desire. This is much more fool-proof than setting alarms per-instance. Metrics by DBClusterIdentifier and Role</summary></entry><entry><title type="html">Bash One-Liner: Retry Laravel Failed Jobs, Filtered by Date</title><link href="https://lukewaite.ca/posts/2017/08/17/bash-oneliner-laravel-retry-todays-failed-jobs.html" rel="alternate" type="text/html" title="Bash One-Liner: Retry Laravel Failed Jobs, Filtered by Date" /><published>2017-08-17T22:00:00+00:00</published><updated>2017-08-17T22:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/08/17/bash-oneliner-laravel-retry-todays-failed-jobs</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/08/17/bash-oneliner-laravel-retry-todays-failed-jobs.html">&lt;p&gt;We all have those moments when Queue jobs fail. Sometimes it’s a bad deploy,
others it’s an upstream service that’s taken a poop. Sometimes, we need to
retry failed jobs, but can’t just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;artisan queue retry:all&lt;/code&gt;, because maybe we
haven’t done a cleanup of failed jobs lately.&lt;/p&gt;

&lt;h3 id=&quot;oops&quot;&gt;Oops…&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span class=&quot;gp&quot;&gt;worker@5986f48e91d6:/var/www/app#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;php artisan queue:failed | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 2017-08-17
&lt;span class=&quot;go&quot;&gt;+------+------------+--------------------------------------------------+-------------------------+---------------------+
| ID   | Connection | Queue                                            | Class                   | Failed At           |
+------+------------+--------------------------------------------------+-------------------------+---------------------+
| 3094 | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-08-17 21:14:07 |
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;...
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;| 851  | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-08-17 03:35:08 |
| 850  | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:35:08 |
| 849  | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:27:09 |
| 848  | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:26:37 |
| 847  | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:20:38 |
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;...
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;| 262  | sqs        | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-03-21 10:33:19 |
+------+------------+--------------------------------------------------+-------------------------+---------------------+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;!--more--&gt;

&lt;p&gt;In my case today, I was running some maintenance jobs overnight, when I
overworked a legacy database and caused about ~2500 of 1m+ jobs to timeout.
Not a big problem, but the jobs needed to be retried.&lt;/p&gt;

&lt;p&gt;A colleague watching me do it, informed me that last time they did something like this
(I sincerely hope it wasn’t 2500 failed jobs) they typed all the numbers in manually!&lt;/p&gt;

&lt;h2 id=&quot;so-down-to-the-point-the-one-liner&quot;&gt;So, down to the point, the one-liner:&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;php artisan queue:retry &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;php artisan queue:failed | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;2017-08-17 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $2}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;lets-break-that-down&quot;&gt;Let’s break that down&lt;/h3&gt;

&lt;h4 id=&quot;basics&quot;&gt;Basics&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$( ... )&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html&quot; target=&quot;_blank&quot;&gt;Command substitution&lt;/a&gt; allows you to insert the results of the contents into your command&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;|&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;The classic &lt;a href=&quot;http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-4.html&quot; target=&quot;_blank&quot;&gt;bash pipe&lt;/a&gt; sends the output of one command to the input of the next&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-commands-used&quot;&gt;The commands used&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php artisan queue:retry $( ... )&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;artisan queue:retry&lt;/code&gt;, we’ll end up giving it a list of IDs for the jobs to retry&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php artisan queue:failed&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Get the listing of failed jobs&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep 2017-08-17&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Search for the date we want to filter by in each line&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk &apos;{print $2}&apos;&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;awk will by default split on spaces, so print id which is in the second captured group (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;|&lt;/code&gt; from the beginning of the table will be in the first position)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;other-uses&quot;&gt;Other uses&lt;/h2&gt;
&lt;p&gt;Change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt;, and you can filter by a lot more than date.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep -v&lt;/code&gt; will inverse filter, so you can run everything &lt;em&gt;except&lt;/em&gt; a given date&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep &amp;lt;job name&amp;gt;&lt;/code&gt; - filter only specific classes&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep &amp;lt;queue name&amp;gt;&lt;/code&gt; - filter specific queues, or connections&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="bash-one-liner" /><category term="laravel" /><category term="queue" /><category term="retry" /><summary type="html">We all have those moments when Queue jobs fail. Sometimes it’s a bad deploy, others it’s an upstream service that’s taken a poop. Sometimes, we need to retry failed jobs, but can’t just artisan queue retry:all, because maybe we haven’t done a cleanup of failed jobs lately. Oops… worker@5986f48e91d6:/var/www/app# php artisan queue:failed | grep -v 2017-08-17 +------+------------+--------------------------------------------------+-------------------------+---------------------+ | ID | Connection | Queue | Class | Failed At | +------+------------+--------------------------------------------------+-------------------------+---------------------+ | 3094 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-08-17 21:14:07 | ... | 851 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-08-17 03:35:08 | | 850 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:35:08 | | 849 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:27:09 | | 848 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:26:37 | | 847 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-07-28 11:20:38 | ... | 262 | sqs | https://sqs.us-east-1.amazonaws.com/123/my-queue | App\Jobs\MyImportantJob | 2017-03-21 10:33:19 | +------+------------+--------------------------------------------------+-------------------------+---------------------+</summary></entry><entry><title type="html">Finding Files in S3 (without a known prefix)</title><link href="https://lukewaite.ca/posts/2017/08/03/s3-find-files.html" rel="alternate" type="text/html" title="Finding Files in S3 (without a known prefix)" /><published>2017-08-03T21:00:00+00:00</published><updated>2017-08-03T21:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/08/03/s3-find-files</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/08/03/s3-find-files.html">&lt;p&gt;S3 is a fantastic storage service. We use it all over the place, but sometimes
it can be hard to find what you’re looking for in buckets with massive
data sets. Consider the following questions:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What happens when you know the file name, but perhaps not the full prefix
(path) of the file?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I hit this in production today, which is the motive of the blog post. The
next question is what I thought might be a useful example, when I wanted
to extrapolate what I’d learned to other use cases.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How do you find files modified on specific dates, regardless of prefix?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!--more--&gt;

&lt;p&gt;Typically, this is when things start to get difficult. As a starting point,
I use &lt;a href=&quot;https://www.panic.com/transmit/&quot; target=&quot;_blank&quot;&gt;Transmit&lt;/a&gt; - it’s a fantastic tool. However, it starts to fall
down when you need to either deal with “folders” that contain many tens of
thousands of documents, or when you need to look for things that could be
in multiple folders.&lt;/p&gt;

&lt;p&gt;Both of the above questions can be answered relatively easily by using the
&lt;a href=&quot;http://docs.aws.amazon.com/cli/latest/reference/index.html#options&quot; target=&quot;_blank&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--query&lt;/code&gt; parameter of the aws cli&lt;/a&gt;. You can pass to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--query&lt;/code&gt; any
&lt;a href=&quot;http://jmespath.org/&quot; target=&quot;_blank&quot;&gt;JMESPath&lt;/a&gt; query.&lt;/p&gt;

&lt;h4 id=&quot;find-file-by-partial-name&quot;&gt;Find file by partial name&lt;/h4&gt;

&lt;p&gt;For this example, we will search for a file name containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1018441&lt;/code&gt;. I
have slightly redacted some bits of the path as this is a query of
production data.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;aws s3api list-objects &lt;span class=&quot;nt&quot;&gt;--bucket&lt;/span&gt; &amp;lt;bucket-name&amp;gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Contents[?contains(Key, &apos;1018441&apos;)]&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LastModified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2017-05-31T20:36:28.000Z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ETag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;b861daa5cc3775f38519f5de6566cbe7&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;StorageClass&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;clients/&amp;lt;client name&amp;gt;/programs/0ced4d20939fe16978df9e6d8f8985ad/1018441-synth.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;DisplayName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Size&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;27032&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LastModified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2017-07-27T19:38:47.000Z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ETag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;829ba43ee2bca9e20f7165ef7e7f055b&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;StorageClass&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;clients/&amp;lt;client-name&amp;gt;/programs/8c1bb9a738f64c9219d8eda292f90394/1018441-synth.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;DisplayName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Size&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;29381&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LastModified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2017-07-27T19:05:55.000Z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ETag&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;70f187a8bbbd2d5c2c54844c1b6da3a6&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;StorageClass&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cache/1018441.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;DisplayName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Size&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12718&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this case, we can quickly see that when we were expecting only two files
by this name (a cache, and a processed file) we have two different processed
files! Here’s our problem.&lt;/p&gt;

&lt;h4 id=&quot;find-files-modified-on-a-given-date&quot;&gt;Find files modified on a given date&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;aws s3api list-objects &lt;span class=&quot;nt&quot;&gt;--bucket&lt;/span&gt; &amp;lt;bucket name&amp;gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Contents[?contains(LastModified) &amp;gt; &apos;2017-08-03&apos;)]&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;find-files-modified-between-given-times&quot;&gt;Find files modified between given times&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;aws s3api list-objects &lt;span class=&quot;nt&quot;&gt;--bucket&lt;/span&gt; &amp;lt;bucket name&amp;gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Contents[?LastModified &amp;gt; &apos;2017-08-03T23&apos; &amp;amp;&amp;amp; LastModified &amp;lt; &apos;2017-08-03T23:15&apos;]&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Technically, the ability to perform a comparison on a date is not part of
the official spec for JMESPath, but I found a GitHub issue which describes
that &lt;a href=&quot;https://github.com/jmespath/jmespath.py/issues/124&quot; target=&quot;_blank&quot;&gt;many users rely on the ability to do just this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That issue was closed by &lt;a href=&quot;https://github.com/jamesls/jmespath/commit/b893fc2bdd52fdd227a8cec3b7a26574de813e67&quot; target=&quot;_blank&quot;&gt;this commit&lt;/a&gt; which mentions that it
will be added explicitly to the spec:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The spec doesn’t officially support string types yet, but enough people
are relying on this behavior that it’s been added back.  This should
eventually become part of the official spec.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;caveats&quot;&gt;Caveats&lt;/h3&gt;
&lt;p&gt;When you use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--query&lt;/code&gt; option, it’s all done as post-processing, on your
local system. Nothing is done remotely by s3, it’s essentially the same as
piping the output to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq&lt;/code&gt;, &lt;a href=&quot;https://github.com/jmespath/jp&quot; target=&quot;_blank&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jp&lt;/code&gt;&lt;/a&gt; or another filtering program. This means if
you have many 100s of thousands, or millions of objects, or a slow connection
it will not be a fast process.&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="aws" /><category term="s3" /><category term="search" /><summary type="html">S3 is a fantastic storage service. We use it all over the place, but sometimes it can be hard to find what you’re looking for in buckets with massive data sets. Consider the following questions: What happens when you know the file name, but perhaps not the full prefix (path) of the file? I hit this in production today, which is the motive of the blog post. The next question is what I thought might be a useful example, when I wanted to extrapolate what I’d learned to other use cases. How do you find files modified on specific dates, regardless of prefix?</summary></entry><entry><title type="html">NewRelic Instrumentation of the Elasticsearch PHP SDK</title><link href="https://lukewaite.ca/posts/2017/07/15/php-newrelic-instrumentation-elasticseach.html" rel="alternate" type="text/html" title="NewRelic Instrumentation of the Elasticsearch PHP SDK" /><published>2017-07-15T09:00:00+00:00</published><updated>2017-07-15T09:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/07/15/php-newrelic-instrumentation-elasticseach</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/07/15/php-newrelic-instrumentation-elasticseach.html">&lt;p&gt;We run Elasticsearch in production, fronted by an API which abstracts away complex queries and
presents our APIs which consume the data a consistent interface. It came to my attention
recently that we had no visibility in NewRelic on external transaction time going to ES.&lt;/p&gt;

&lt;p&gt;In a nutshell, the problem turned out to be that the elasticsearch-php-sdk uses &lt;a href=&quot;https://github.com/guzzle/RingPHP&quot;&gt;RingPHP&lt;/a&gt;
as a transport, which NewRelic doesn’t support.&lt;/p&gt;

&lt;p&gt;I’ve written a &lt;a href=&quot;https://github.com/lukewaite/ringphp-guzzle-handler&quot;&gt;RingPHP Guzzle Adapter&lt;/a&gt; which the user can
&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/_configuration.html#_configure_the_http_handler&quot;&gt;pass to elasticsearch&lt;/a&gt; and quickly gain instrumentation.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$guzzle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GuzzleHttp\Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$guzzleHandler&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LukeWaite\RingPhpGuzzleHandler\GuzzleHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$guzzle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Elasticsearch\ClientBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$guzzleHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;table class=&quot;image&quot;&gt;
	&lt;caption align=&quot;bottom&quot;&gt;NewRelic Before and After Instrumentation&lt;/caption&gt;
	&lt;tr&gt;&lt;td&gt;&lt;img src=&quot;/images/2017-07-15-newrelic-elasticsearch/newrelic-instrumentation.png&quot; alt=&quot;NewRelic Before and After Instrumentation&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="newrelic" /><category term="php" /><category term="instrumentation" /><category term="elasticsearch" /><summary type="html">We run Elasticsearch in production, fronted by an API which abstracts away complex queries and presents our APIs which consume the data a consistent interface. It came to my attention recently that we had no visibility in NewRelic on external transaction time going to ES. In a nutshell, the problem turned out to be that the elasticsearch-php-sdk uses RingPHP as a transport, which NewRelic doesn’t support. I’ve written a RingPHP Guzzle Adapter which the user can pass to elasticsearch and quickly gain instrumentation. $guzzle = new GuzzleHttp\Client(); $guzzleHandler = new LukeWaite\RingPhpGuzzleHandler\GuzzleHandler($guzzle); $client = Elasticsearch\ClientBuilder::create() -&amp;gt;setHandler($guzzleHandler) -&amp;gt;build(); NewRelic Before and After Instrumentation</summary></entry><entry><title type="html">Laravel Caching for AWS Credentials</title><link href="https://lukewaite.ca/posts/2017/04/29/laravel-aws-cache-adapter.html" rel="alternate" type="text/html" title="Laravel Caching for AWS Credentials" /><published>2017-04-29T16:00:00+00:00</published><updated>2017-04-29T16:00:00+00:00</updated><id>https://lukewaite.ca/posts/2017/04/29/laravel-aws-cache-adapter</id><content type="html" xml:base="https://lukewaite.ca/posts/2017/04/29/laravel-aws-cache-adapter.html">&lt;p&gt;We’ve seen occasionally poor performance on the AWS EC2 Metadata API when using IAM roles at &lt;a href=&quot;https://www.intouchinsight.com&quot;&gt;Intouch&lt;/a&gt; which got
me thinking. Why does the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-pdp-sdk&lt;/code&gt; need to hit the EC2 Metadata API during every request? Well, it turns out, it’s
simple. If you don’t explicitly give the sdk a cache interface, then it won’t use one!&lt;/p&gt;

&lt;p&gt;I’ve just published the initial release of my &lt;a href=&quot;https://github.com/lukewaite/laravel-aws-cache-adapter&quot;&gt;laravel-aws-cache-adapter&lt;/a&gt; plugin on &lt;a href=&quot;https://packagist.org/packages/lukewaite/laravel-aws-cache-adapter&quot;&gt;packagist&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;If you pass an instance of the &lt;a href=&quot;http://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.CacheInterface.html&quot;&gt;CacheInterface&lt;/a&gt; class through as your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials&lt;/code&gt; setting when you
instantiate the aws-sdk, it’ll use that interface to cache STS tokens returned via the metadata API when granting your
application permissions via either EC2 IAM Roles or ECS Task IAM Roles. Simple!&lt;/p&gt;

&lt;h3 id=&quot;feedback&quot;&gt;Feedback&lt;/h3&gt;
&lt;p&gt;How has this worked for you? Found a problem? Let me know and open an &lt;a href=&quot;https://github.com/lukewaite/laravel-aws-cache-adapter/issues&quot;&gt;issue&lt;/a&gt;!&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="aws" /><category term="laravel" /><category term="iam" /><summary type="html">We’ve seen occasionally poor performance on the AWS EC2 Metadata API when using IAM roles at Intouch which got me thinking. Why does the aws-pdp-sdk need to hit the EC2 Metadata API during every request? Well, it turns out, it’s simple. If you don’t explicitly give the sdk a cache interface, then it won’t use one! I’ve just published the initial release of my laravel-aws-cache-adapter plugin on packagist.</summary></entry><entry><title type="html">AWS Batch as a Laravel Queue - Stable Release</title><link href="https://lukewaite.ca/aws/laravel/batch/queue/2017/04/09/laravel-queue-aws-batch-stable.html" rel="alternate" type="text/html" title="AWS Batch as a Laravel Queue - Stable Release" /><published>2017-04-09T20:47:00+00:00</published><updated>2017-04-09T20:47:00+00:00</updated><id>https://lukewaite.ca/aws/laravel/batch/queue/2017/04/09/laravel-queue-aws-batch-stable</id><content type="html" xml:base="https://lukewaite.ca/aws/laravel/batch/queue/2017/04/09/laravel-queue-aws-batch-stable.html">&lt;p&gt;Following up my &lt;a href=&quot;/aws/laravel/batch/queue/2017/04/01/laravel-queue-aws-batch.html&quot;&gt;previous post&lt;/a&gt; about the release of my &lt;a href=&quot;https://github.com/lukewaite/laravel-queue-aws-batch&quot;&gt;laravel-queue-aws-batch&lt;/a&gt; plugin, I am pleased
to announce that I have tagged two stable versions for release.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Version 1.0 has been tagged supporting Laravel 5.1, 5.2, and 5.3.&lt;/p&gt;

&lt;p&gt;Version 2.0 has been tagged supporting Laravel 5.4.&lt;/p&gt;

&lt;h3 id=&quot;feedback&quot;&gt;Feedback&lt;/h3&gt;
&lt;p&gt;How has this worked for you? Found a problem? Let me know and open an [issue][issues]!&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="aws" /><category term="laravel" /><category term="batch" /><category term="queue" /><summary type="html">Following up my previous post about the release of my laravel-queue-aws-batch plugin, I am pleased to announce that I have tagged two stable versions for release.</summary></entry><entry><title type="html">AWS Batch as a Laravel Queue</title><link href="https://lukewaite.ca/aws/laravel/batch/queue/2017/04/01/laravel-queue-aws-batch.html" rel="alternate" type="text/html" title="AWS Batch as a Laravel Queue" /><published>2017-04-01T11:50:00+00:00</published><updated>2017-04-01T11:50:00+00:00</updated><id>https://lukewaite.ca/aws/laravel/batch/queue/2017/04/01/laravel-queue-aws-batch</id><content type="html" xml:base="https://lukewaite.ca/aws/laravel/batch/queue/2017/04/01/laravel-queue-aws-batch.html">&lt;p&gt;We use Laravel for all of our APIs at &lt;a href=&quot;https://www.intouchinsight.com&quot;&gt;Intouch Insight&lt;/a&gt;, so when &lt;a href=&quot;https://aws.amazon.com/batch/&quot;&gt;AWS Batch&lt;/a&gt; was released, I started
wondering about backing our Laravel Queues with AWS Batch. This seemed like the perfect opportunity to give back,
since I’m sure others are looking at Batch with the same interest that I am. A few evenings of playing around, and
here we are.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;I’ve just published the initial release of my &lt;a href=&quot;https://github.com/lukewaite/laravel-queue-aws-batch&quot;&gt;laravel-queue-aws-batch&lt;/a&gt; plugin on &lt;a href=&quot;https://packagist.org/packages/lukewaite/laravel-queue-aws-batch&quot;&gt;packagist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Initially the plugin has been built for Laravel 5.1, since we use the LTS releases. &lt;del&gt;Support for 5.4 will be coming, or
quite possibly could already be working, but I need to review what has changed in the underlying queue classes before
making that promise.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;If you review the &lt;a href=&quot;https://github.com/lukewaite/laravel-queue-aws-batch&quot;&gt;implementation&lt;/a&gt;, you’ll see that I based my work off the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DatabaseQueue&lt;/code&gt;
class, as we need a place to store jobs before batch can process them. Serializing the entire job payload and passing that
as a string through the job submission to batch seemed like it could be error prone, to say the least.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;Tests are needed, and will be coming.&lt;/del&gt; I’d love to hear feedback, or receive pull requests for improvements.&lt;/p&gt;

&lt;h3 id=&quot;update---stable-releases-are-now-available&quot;&gt;Update - Stable Releases are now available&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/aws/laravel/batch/queue/2017/04/09/laravel-queue-aws-batch-stable.html&quot;&gt;Stable releases are now available&lt;/a&gt; for Laravel 5.1
through to Laravel 5.4.&lt;/p&gt;

&lt;h3 id=&quot;feedback&quot;&gt;Feedback&lt;/h3&gt;
&lt;p&gt;How has this worked for you? Found a problem? Let me know and open an &lt;a href=&quot;https://github.com/lukewaite/laravel-queue-aws-batch/issues&quot;&gt;issue&lt;/a&gt;!&lt;/p&gt;</content><author><name>Luke Waite</name><email>lwaite@gmail.com</email></author><category term="aws" /><category term="laravel" /><category term="batch" /><category term="queue" /><summary type="html">We use Laravel for all of our APIs at Intouch Insight, so when AWS Batch was released, I started wondering about backing our Laravel Queues with AWS Batch. This seemed like the perfect opportunity to give back, since I’m sure others are looking at Batch with the same interest that I am. A few evenings of playing around, and here we are.</summary></entry></feed>