<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jake Casto</title>
    <description>The latest articles on DEV Community by Jake Casto (@jake).</description>
    <link>https://dev.to/jake</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%2F17041%2Fa775a4a5-eac0-46fd-a1f0-695c39f626a0.jpeg</url>
      <title>DEV Community: Jake Casto</title>
      <link>https://dev.to/jake</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jake"/>
    <language>en</language>
    <item>
      <title>Signal-driven health monitoring for HNSW indices w/ pgvector</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Mon, 08 Dec 2025 18:47:52 +0000</pubDate>
      <link>https://dev.to/jake/signal-driven-health-monitoring-for-hnsw-indices-w-pgvector-1hcl</link>
      <guid>https://dev.to/jake/signal-driven-health-monitoring-for-hnsw-indices-w-pgvector-1hcl</guid>
      <description>&lt;p&gt;Vector search in PostgreSQL has come a long way, and pgvector makes it simple:&lt;br&gt;&lt;br&gt;
insert embeddings, create an HNSW index, and run ANN queries.  &lt;/p&gt;

&lt;p&gt;At least for a while.&lt;/p&gt;

&lt;p&gt;As our system scaled across stores, regions, and product volumes, something became clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;HNSW is not “fire and forget.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Indexes age. They bloat. They fragment. They silently lose recall.&lt;br&gt;&lt;br&gt;
When they degrade, search results degrade too — sometimes gradually, sometimes catastrophically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For months, we assumed our approach was fine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rebuild every night at 1 AM
&lt;/li&gt;
&lt;li&gt;Rebuild after imports
&lt;/li&gt;
&lt;li&gt;Rebuild after new embeddings
&lt;/li&gt;
&lt;li&gt;Rebuild everything, always
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It felt &lt;em&gt;safe&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
It was &lt;strong&gt;absolutely not&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  When Scheduled Rebuilds Turned Into a Problem
&lt;/h2&gt;

&lt;p&gt;Our first approach was &lt;strong&gt;periodic instead of condition-based&lt;/strong&gt; — every night, every tenant, every index.&lt;/p&gt;

&lt;p&gt;And here's what happened:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What we saw&lt;/th&gt;
&lt;th&gt;Why it was bad&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;We rebuilt indexes with &lt;strong&gt;zero new writes&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Massive waste of compute + IO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large tenants saw &lt;strong&gt;daily index decay&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Recall dropped, latency increased&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crash mid-rebuild left index &lt;strong&gt;invalid but present&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;PostgreSQL stopped using it entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replicas &lt;strong&gt;diverged from primaries&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;ANN returned different results per region&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The worst case was the &lt;strong&gt;INVALID index scenario&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A Postgres restart happened &lt;strong&gt;during&lt;/strong&gt; a CONCURRENT index build. During this operation the index is created first as &lt;code&gt;INVALID&lt;/code&gt;, then validated after &lt;em&gt;two full table scans&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
A crash meant the scans never completed — leaving a corrupt index that still appeared valid.&lt;/p&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An index that &lt;strong&gt;looks present but is unusable&lt;/strong&gt;, silently ruining search until someone notices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That incident forced a rethink.&lt;/p&gt;

&lt;p&gt;We didn’t need &lt;strong&gt;scheduled rebuilding&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
We needed &lt;strong&gt;index health monitoring&lt;/strong&gt; — rebuild only when necessary.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Does “Healthy” Mean for an HNSW Index?
&lt;/h2&gt;

&lt;p&gt;B-trees give you bloat %, page churn, dead tuples.&lt;br&gt;&lt;br&gt;
HNSW is different — it’s a navigable multi-layer graph whose health depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how many vectors were replaced without pruning&lt;/li&gt;
&lt;li&gt;how many nodes reference embeddings that no longer exist&lt;/li&gt;
&lt;li&gt;whether graph layers remain navigable under memory pressure&lt;/li&gt;
&lt;li&gt;whether the index fits in cache vs spilling to disk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is &lt;strong&gt;no single fragmentation metric.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;we built one.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Three-Metric Health Model
&lt;/h2&gt;

&lt;p&gt;We generate one score per index using:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Detects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dead tuple ratio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Graph churn, stale nodes, recall degradation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache hit ratio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Memory residency vs disk traversal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bytes per vector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HNSW structural bloat / unnecessary overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If an index inflates, falls out of cache, or accumulates dead references — &lt;strong&gt;ANN quality drops.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL already exposes everything we need.&lt;/p&gt;


&lt;h3&gt;
  
  
  📊 Metric 1 — Dead Tuple Ratio
&lt;/h3&gt;

&lt;p&gt;Dead tuples = deleted or overwritten embeddings still referenced inside the graph.&lt;br&gt;&lt;br&gt;
They increase hop count and reduce recall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dead_tuple_ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n_dead_tup&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n_live_tup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Threshold: &lt;strong&gt;10–15% is worth rebuilding.&lt;/strong&gt;&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="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;relname&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_dead_tup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_live_tup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;n_dead_tup&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;GREATEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_live_tup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;dead_tuple_ratio_pct&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_all_tables&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your_embedding_table_name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  📊 Metric 2 — Cache Hit Ratio
&lt;/h3&gt;

&lt;p&gt;Most performance issues weren't degraded structure —&lt;br&gt;&lt;br&gt;
they were &lt;strong&gt;cache eviction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If HNSW pages fall out of shared_buffers → traversal becomes disk-bound → planner may stop using the index.&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="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;indexrelid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;idx_blks_hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;idx_blks_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;idx_blks_hit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;GREATEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx_blks_hit&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;idx_blks_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cache_hit_ratio_pct&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_statio_user_indexes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;indexrelid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%hnsw%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt; 90%&lt;/td&gt;
&lt;td&gt;Healthy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90–75%&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt; 75%&lt;/td&gt;
&lt;td&gt;Red — likely disk-bound, planner risk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  📊 Metric 3 — Bytes Per Vector
&lt;/h3&gt;

&lt;p&gt;Heuristic for structural bloat.&lt;br&gt;&lt;br&gt;
If stored bytes per embedding drift too high → rebuild pays off.&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="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&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;relname&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pg_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&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;oid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;index_bytes&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;pg_index&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indexrelid&lt;/span&gt; &lt;span class="o"&gt;=&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;oid&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&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;relname&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%hnsw%'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;index_name&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;index_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;index_bytes&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;reltuples&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your_embedding_table_name'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;bytes_per_vector&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example: expected &lt;strong&gt;5.0 KB/vector&lt;/strong&gt;, observed &lt;strong&gt;9.2 KB/vector → rebuild.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not perfect, but highly correlated with real-world recall loss.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Health Score
&lt;/h2&gt;

&lt;p&gt;Instead of &lt;strong&gt;binary rebuild vs no rebuild&lt;/strong&gt;, we compute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dead_score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.4&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="n"&gt;cache_score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.4&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="n"&gt;bloat_score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Weighting rationale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dead tuples = recall degradation&lt;/strong&gt; → heaviest signal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache residency = latency + planner reliability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bloat matters, but less across large embeddings&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If score &amp;lt; threshold → rebuild &lt;em&gt;just that index&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;We stopped rebuilding everything.&lt;br&gt;&lt;br&gt;
We rebuild &lt;strong&gt;only when needed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Smarter. Faster. Cheaper.&lt;br&gt;&lt;br&gt;
And search quality no longer surprises us.&lt;/p&gt;

&lt;p&gt;Like the writing? Follow &lt;a href="https://www.uselayers.com" rel="noopener noreferrer"&gt;product development at Layers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vectordatabase</category>
      <category>postgres</category>
      <category>pgvector</category>
    </item>
    <item>
      <title>Laravel Nova: Adding custom buttons to resource toolbars</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Thu, 14 Mar 2019 00:48:15 +0000</pubDate>
      <link>https://dev.to/jake/laravel-nova-adding-custom-buttons-to-resource-toolbars-1ob5</link>
      <guid>https://dev.to/jake/laravel-nova-adding-custom-buttons-to-resource-toolbars-1ob5</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrwozsd3lx3ceqnr91ro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrwozsd3lx3ceqnr91ro.png" alt="header image"&gt;&lt;/a&gt;&lt;br&gt;
If you've seen &lt;a href="https://github.com/laravel/nova-issues/issues/786" rel="noopener noreferrer"&gt;issue 786 on laravel/nova-issues&lt;/a&gt; on Github or attempted to add custom buttons to Resources in Laravel Nova you are probably frustrated. I spent a &lt;em&gt;whole&lt;/em&gt; day trying to figure this out. But it's a lot simpler than you think!&lt;/p&gt;

&lt;p&gt;Laravel Nova is powered by Vue.JS a very powerful JS microframework. Each resource has a custom component and that component has its own scope. This allows us to override Nova's built-in components and add custom buttons.&lt;/p&gt;

&lt;p&gt;Let's start by creating a new Nova Resource Tool:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

php artisan nova:resource-tool 0x15f/custom-resource-toolbar


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

&lt;/div&gt;

&lt;p&gt;Say &lt;code&gt;yes&lt;/code&gt; to all of the prompts...&lt;/p&gt;

&lt;p&gt;Now that you've created your resource tool navigate to the &lt;code&gt;nova-components/custom-resource-toolbar/resources/js&lt;/code&gt; directory open &lt;code&gt;tool.js&lt;/code&gt; in your favorite JS editor and paste the following.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;Nova&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;booting&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-detail-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/CustomDetailToolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quotes-detail-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/QuotesDetailToolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Navigate into the &lt;code&gt;components&lt;/code&gt; directory and delete &lt;code&gt;Tool.vue&lt;/code&gt;. Now create two files, one named &lt;code&gt;CustomDetailToolbar.vue&lt;/code&gt; the other named &lt;code&gt;QuotesDetailToolbar.vue&lt;/code&gt;. Paste the following into &lt;code&gt;CustomDetailToolbar.vue&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full justify-end items-center mx-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"hasComponent"&lt;/span&gt; &lt;span class="na"&gt;:is=&lt;/span&gt;&lt;span class="s"&gt;"component"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resourceName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resourceId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourceName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-detail-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nf"&gt;hasComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;    
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And paste the following into &lt;code&gt;QuotesDetailToolbar.vue&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full justify-end items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
                &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-default btn-icon btn-white"&lt;/span&gt;
                &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"'/nova-vendor/custom-resource-toolbar/export-quote/' + this.$parent.resourceId"&lt;/span&gt;
                &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-color: var(--primary); color: white;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                Export as PDF
             &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resourceName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resourceId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nf"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nova&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom Detail Toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can now build your Resource Tool using &lt;code&gt;npm run watch&lt;/code&gt;, add it to your Resources, and open your &lt;code&gt;CustomResourceToolbar.php&lt;/code&gt; file and change the component name to &lt;code&gt;custom-detail-toolbar&lt;/code&gt;. I'll explain what we did below.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;tool.js&lt;/code&gt; we registered two new components &lt;code&gt;custom-detail-toolbar&lt;/code&gt; and &lt;code&gt;quotes-detail-toolbar&lt;/code&gt;, &lt;code&gt;custom-detail-toolbar&lt;/code&gt; is used by Nova to load any components that should be displayed on that toolbar. Nova then looks for the resource's toolbar. You can define your resources toolbar component by registering a component with your resources plural name followed by &lt;code&gt;-detail-toolbar&lt;/code&gt;. Within that component, you can add any CSS/JS that should be placed in the toolbar.&lt;/p&gt;

&lt;p&gt;If you notice I have a &lt;code&gt;mounted&lt;/code&gt; function in my component with the following code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nova&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom Detail Toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Typically a resource tool has a panel on your resource detail page. This function runs when our component has been mounted to remove the panel that nova adds giving your page a clean feel.&lt;/p&gt;

&lt;p&gt;You can find all the code used in this tutorial &lt;a href="https://github.com/0x15f/custom-resource-toolbars" rel="noopener noreferrer"&gt;on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I quickly wrote this article, I'll clean it up later.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>vue</category>
      <category>php</category>
    </item>
    <item>
      <title>Using libcurl3 and libcurl4 on Ubuntu 18.04 (Bionic)</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Tue, 02 Oct 2018 15:49:02 +0000</pubDate>
      <link>https://dev.to/jake/using-libcurl3-and-libcurl4-on-ubuntu-1804-bionic-184g</link>
      <guid>https://dev.to/jake/using-libcurl3-and-libcurl4-on-ubuntu-1804-bionic-184g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57bv9zbbs818d597yu7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57bv9zbbs818d597yu7l.png" alt="image" width="363" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm sure many of us have needed to run libcurl3 and libcurl4 on Ubuntu 18 for some reason at any point in time.&lt;/p&gt;

&lt;p&gt;On Ubuntu 18.04 (Bionic) libcurl3 and libcurl4 cannot coexist. Many third party packages, libraries, etc require libcurl3 while curl itself requires libcurl4.&lt;/p&gt;

&lt;p&gt;Please note, this solution is only for precompiled commands (and potentially binaries, not tested). This was tested on a &lt;a href="https://github.com/tihmstar/tsschecker" rel="noopener noreferrer"&gt;C# binary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First you'll need to get a copy of libcurl3 and save it to your &lt;code&gt;/usr/lib&lt;/code&gt; directory. You can do that by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt-get install libcurl3 -y
$ cp /usr/lib/x86_64-linux-gnu/libcurl.so.3 /usr/lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then remove libcurl3 and reinstall libcurl4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt-get install libcurl4 libcurl4-openssl-dev -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally prepend &lt;code&gt;env LD_PRELOAD=/usr/lib/libcurl.so.3&lt;/code&gt; to any commands that require libcurl3.&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>libcurl</category>
      <category>curl</category>
    </item>
    <item>
      <title>Using Headers &amp; Cookies with PHP's file_get_contents() function</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Thu, 13 Sep 2018 03:00:24 +0000</pubDate>
      <link>https://dev.to/jake/using-headers--cookies-with-phps-filegetcontents-function-2cn8</link>
      <guid>https://dev.to/jake/using-headers--cookies-with-phps-filegetcontents-function-2cn8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This solution was originally linked to me by a colleague, all credit goes to &lt;a href="https://twitter.com/LiamHammett/status/1033056901695762432" rel="noopener noreferrer"&gt;this tweet&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qh5s9h0d0pylmw96629.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qh5s9h0d0pylmw96629.png" alt="image" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did you know that using stream contexts, you can set headers when making HTTP requests with php's file_get_contents() function?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stream_create_context&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"method"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"header"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Accept-languange: en&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;
        &lt;span class="s2"&gt;"Cookie: foo=bar&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>php</category>
      <category>http</category>
      <category>headers</category>
      <category>cookies</category>
    </item>
    <item>
      <title>How do you find potential clients to cold-email?</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Sun, 15 Apr 2018 15:16:28 +0000</pubDate>
      <link>https://dev.to/jake/how-do-you-find-potential-clients-to-cold-email-1g6g</link>
      <guid>https://dev.to/jake/how-do-you-find-potential-clients-to-cold-email-1g6g</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.clipartxtras.com%2F5d848a908432460ef1c6b2b0366af9f4_ice-cliparts-transparent-free-download-clip-art-free-clip-art-ice-clipart-transparent_425-395.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%2Fimg.clipartxtras.com%2F5d848a908432460ef1c6b2b0366af9f4_ice-cliparts-transparent-free-download-clip-art-free-clip-art-ice-clipart-transparent_425-395.jpeg" alt="ice_cube_photo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I just launched a development studio &lt;a href="http://lynndigital.com" rel="noopener noreferrer"&gt;Lynn Digital&lt;/a&gt;. I'll give you a bit of background on my new company. Originally I needed more developers to assist me on larger projects when freelancing but eventually, clients started to dislike developers they didn't know working on their projects so I decided to start a company. Currently, I have four developers that I can subcontract work too, we each bring a different skillset to the table which allows us to work together on nearly any project. &lt;/p&gt;

&lt;p&gt;I've sent a handful of custom cold-emails to potential clients I found through another client but I was only able to get 5 - 10 references. I don't want to buy a database of emails and launch a marketing campaign, I want to be able to reach out to potential clients personally. &lt;/p&gt;

&lt;p&gt;How can I find potential clients to cold-email?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>marketing</category>
    </item>
    <item>
      <title>Configuring PHP-FPM For High Network Traffic</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Tue, 23 Jan 2018 19:50:14 +0000</pubDate>
      <link>https://dev.to/jake/configuring-php-fpm-for-high-network-traffic-47le</link>
      <guid>https://dev.to/jake/configuring-php-fpm-for-high-network-traffic-47le</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2yamnx2v4e6zkwn2223.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2yamnx2v4e6zkwn2223.png" alt="php-fpm logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintaining a constant response time on a server with high network traffic while using PHP is probably the hardest &amp;amp; most annoying thing I've done in my career. Switching from Apache to Nginx was a huge performance upgrade... that lasted for a solid half hour before the RPS (requests-per-second) was causing the stack to overflow once again.&lt;/p&gt;

&lt;p&gt;I started browsing Stack Overflow &amp;amp; Google (devs best &amp;amp; worst friend/enemy) for answers, I found a few posts that showed how to fix the issue (PHP-FPM conf) but didn't explain anything. As a hands-on developer, I wanted to know how things worked. &lt;/p&gt;

&lt;p&gt;Originally my PHP-FPM conf looked something like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[www]

user = apache
group = apache

listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1

pm = dynamic

pm.max_children = 200

pm.start_servers = 20

pm.min_spare_servers = 10
pm.max_spare_servers = 20

pm.max_requests = 1000


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

&lt;/div&gt;

&lt;p&gt;I started reading into what &lt;code&gt;pm = dynamic&lt;/code&gt; meant and I found this (in another conf file haha)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;dynamic - the number of child processes are set dynamically based on the following directives. With this process management, there will be always at least 1 children.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hold on... are all 200 children being spawned on startup? Are they always idle?&lt;/p&gt;

&lt;p&gt;Yes &amp;amp; Yes (i think). I started viewing and recording memory usage of the &lt;code&gt;apache&lt;/code&gt; pool using &lt;code&gt;ps aux |grep apache&lt;/code&gt;. No matter how many requests were being processed (0 requests - 1000 requests) there were always 200 children alive. Don't get me wrong, I love kids but 200 at once for no reason is a bit much.&lt;/p&gt;

&lt;p&gt;After spending a few hours screwing with my PHP-FPM conf and running stress tests I came up with this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[www]

user = apache
group = apache

listen = 127.0.0.1:9001

listen.allowed_clients = 127.0.0.1

pm = ondemand
pm.max_children = 200
pm.process_idle_timeout = 1s
pm.max_requests = 1000


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

&lt;/div&gt;

&lt;p&gt;I ran another stress test: 2000 RPS (requests-per-second) for one min and the average response time went from 1000MS to 120MS.&lt;/p&gt;

&lt;p&gt;What happened?&lt;/p&gt;

&lt;p&gt;PHP-FPM's PM (Pool Manager) was spawning 200 children on startup, even though those children were idle the PM was using unnecessary resources to manage the children. The switch from &lt;code&gt;dynamic&lt;/code&gt; to &lt;code&gt;ondemand&lt;/code&gt; allowed children to be spawned when needed and killed them after 1s of inactivity. &lt;/p&gt;

&lt;p&gt;Feel free to critique this post, I have little knowledge of how PHP-FPM's pool manager works. I felt that it might be helpful to someone in a bind with PHP-FPM.&lt;/p&gt;

&lt;p&gt;EDIT: Some info in this article is incorrect e.g I confused &lt;code&gt;dynamic&lt;/code&gt; with &lt;code&gt;static&lt;/code&gt; spawning 200 children on startup. However, I still use this same setup on new servers and it performs so much better than any other config I've used. &lt;/p&gt;

</description>
      <category>php</category>
      <category>nginx</category>
      <category>phpfpm</category>
      <category>network</category>
    </item>
    <item>
      <title>Hi, I'm Jake Casto</title>
      <dc:creator>Jake Casto</dc:creator>
      <pubDate>Sat, 27 May 2017 14:16:27 +0000</pubDate>
      <link>https://dev.to/jake/hi-im-jake-casto</link>
      <guid>https://dev.to/jake/hi-im-jake-casto</guid>
      <description>&lt;p&gt;I have been coding for 3 years.&lt;/p&gt;

&lt;p&gt;I live in Winston-Salem NC.&lt;/p&gt;

&lt;p&gt;I'm a freelance developer.&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: [PHP, Python, C#/++].&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
