<?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: Driptaroop Das</title>
    <description>The latest articles on DEV Community by Driptaroop Das (@dripto).</description>
    <link>https://dev.to/dripto</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%2F1018252%2F6dbf989c-86a1-41cc-a9a1-c1beb3958d9c.jpg</url>
      <title>DEV Community: Driptaroop Das</title>
      <link>https://dev.to/dripto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dripto"/>
    <language>en</language>
    <item>
      <title>Explaining PostgreSQL Explain</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Fri, 17 Jan 2025 16:33:37 +0000</pubDate>
      <link>https://dev.to/dripto/explain-to-me-220a</link>
      <guid>https://dev.to/dripto/explain-to-me-220a</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I publish my tech articles in my blog &lt;a href="https://blog.dripto.xyz" rel="noopener noreferrer"&gt;https://blog.dripto.xyz&lt;/a&gt; including multiple learnings on postgres. Check it out if you are interested. :) &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hello after a long hibernation and welcome to my &lt;code&gt;Postgres&lt;/code&gt; series where I will cover different aspects of Postgres I learned as an application developer. In this section, we will be covering the &lt;code&gt;Explain&lt;/code&gt; command in PostgreSQL.&lt;/p&gt;

&lt;p&gt;In short, The &lt;code&gt;EXPLAIN&lt;/code&gt; command is used to obtain information about how the query planner works. It shows the query plan that the planner generates for the query. The &lt;code&gt;EXPLAIN&lt;/code&gt; command is very useful for optimizing queries and understanding how the query planner works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Lets jump directly to a query.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Explain&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Start the database with &lt;code&gt;compose&lt;/code&gt; (a.k.a. &lt;code&gt;docker compose&lt;/code&gt;). In this post, we will use the docker compose from &lt;a href="https://github.com/driptaroop/postgres-engineering/blob/main/explain/compose.yaml" rel="noopener noreferrer"&gt;the repo&lt;/a&gt;. This will populate a &lt;code&gt;process&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;Please note that this post is also easy to follow even if you don't have a database in ready and don't want to start/follow my compose file.&lt;/p&gt;

&lt;p&gt;Let's take this query:&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'10cfd02f-21d3-4439-9983-5685ec9204c4'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and try to find the query plan for this:&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;Explain&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'10cfd02f-21d3-4439-9983-5685ec9204c4'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gather  (cost=1000.00..53116.77 rows=1 width=133)
  Workers Planned: 2
  -&amp;gt;  Parallel Seq Scan on process  (cost=0.00..52116.67 rows=1 width=133)
        Filter: (user_id = '10cfd02f-21d3-4439-9983-5685ec9204c4'::text)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explain Enhancement
&lt;/h3&gt;

&lt;p&gt;Additional information can be derived by setting &lt;code&gt;track_io_timing&lt;/code&gt; flag on. Enables timing of database I/O calls. This parameter is off by default, as it will repeatedly query the operating system for the current time, which may cause significant overhead on some platforms.&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;SET&lt;/span&gt; &lt;span class="n"&gt;track_io_timing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can get even more information by using the options. The most important options are,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ANALYZE&lt;/strong&gt;: Adding this option makes EXPLAIN not just display the query plan and PostgreSQL's estimates, but also actually run the query. It then shows the real execution time and the number of rows processed at each step. Be cautious with this for UPDATE and DELETE queries since they’ll actually modify data! This is a must-have for serious SQL performance debugging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BUFFERS&lt;/strong&gt;: This one works only with ANALYZE and provides details about how many pages are read, written, or dirtied at each step. If you’re analyzing performance, this is gold—you’ll definitely want this information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VERBOSE&lt;/strong&gt;: When enabled, it shows all the output expressions for every step in the execution plan. Most of the time, this just adds noise, but can be useful for hard to debug queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SETTINGS&lt;/strong&gt;: Available since PostgreSQL v12, this option includes any performance-related parameters that have been changed from their defaults in the output. It's handy for understanding how tweaks to the configuration affect your query.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WAL&lt;/strong&gt;: Introduced in v13, this option shows the Write-Ahead Logging (WAL) activity generated by queries that modify data. Like BUFFERS, it works only with ANALYZE and is super useful for understanding the impact of data changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FORMAT&lt;/strong&gt;: This lets you choose the format of the output. For humans, the default TEXT format is by far the easiest to read and work with for performance tuning. Other formats like XML, JSON, or YAML are better suited for automated tools or scripts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's try to enhance the &lt;code&gt;Explain&lt;/code&gt; command with the &lt;code&gt;ANALYZE&lt;/code&gt; and &lt;code&gt;BUFFERS&lt;/code&gt; options.&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;Explain&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'10cfd02f-21d3-4439-9983-5685ec9204c4'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of this will be,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gather  (cost=1000.00..53116.77 rows=1 width=133) (actual time=446.306..449.906 rows=1 loops=1)
  Workers Planned: 2
  Workers Launched: 2
  Buffers: shared hit=1179 read=40521
  I/O Timings: shared read=731.428
  -&amp;gt;  Parallel Seq Scan on process  (cost=0.00..52116.67 rows=1 width=133) (actual time=433.582..433.598 rows=0 loops=3)
        Filter: (user_id = '10cfd02f-21d3-4439-9983-5685ec9204c4'::text)
        Rows Removed by Filter: 666666
        Buffers: shared hit=1179 read=40521
        I/O Timings: shared read=731.428
Planning:
  Buffers: shared hit=8
Planning Time: 3.036 ms
Execution Time: 449.998 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;Let's go through the output of the enhanced &lt;code&gt;Explain&lt;/code&gt; command line by line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Gather (cost=1000.00..53116.77 rows=1 width=133) (actual time=446.306..449.906 rows=1 loops=1)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Gather&lt;/code&gt; -&amp;gt; This is the topmost node in the plan tree. It is used to collect the results from all the workers.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;(cost=1000.00..53116.77 rows=1 width=133)&lt;/code&gt; -&amp;gt; This is the estimated cost of the plan. The cost is calculated by the planner based on the number of rows and the width of the rows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cost=1000.00..53116.77&lt;/code&gt; -&amp;gt; The estimated cost of the plan. The cost is unitless and is used to compare the cost of different plans. The lower the cost, the better the plan.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rows=1&lt;/code&gt; -&amp;gt; The estimated number of rows that will be returned by the plan.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;width=133&lt;/code&gt; -&amp;gt; The estimated width of the rows that will be returned by the plan.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;(actual time=446.306..449.906 rows=1 loops=1)&lt;/code&gt; -&amp;gt; This is the actual time taken by the plan to execute.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Workers Planned: 2&lt;/code&gt; -&amp;gt; This is the number of workers that are planned to execute the plan.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Workers Launched: 2&lt;/code&gt; -&amp;gt; This is the number of workers that are actually launched to execute the plan. this can be different than the number of workers planned if the system is busy or have a restrictive configuration.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;Buffers: shared hit=1179 read=40521&lt;/code&gt; -&amp;gt; This is the number of buffers that are used by the plan. The number of buffers that are hit in the shared buffer cache and the number of buffers that are read from the disk.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shared hit=1179&lt;/code&gt; -&amp;gt; The number of buffers that are hit in the shared buffer cache. This means that the data is already in the shared buffer cache and does not need to be read from the disk.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read=40521&lt;/code&gt; -&amp;gt; The number of buffers that are read from the disk. This means that the data is not in the shared buffer cache and needs to be read from the disk. This is an expensive operation compared to reading from the shared buffer cache.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;I/O Timings: shared read=731.428&lt;/code&gt; -&amp;gt; This is the time taken to read the data from the disk. This is an important metric to consider when optimizing the query.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;-&amp;gt; Parallel Seq Scan on process (cost=0.00..52116.67 rows=1 width=133) (actual time=433.582..433.598 rows=0 loops=3)&lt;/code&gt; -&amp;gt; In postgres the query plans execute from bottom to top. This is the bottommost node in the plan tree. This is the actual operation that is performed by the plan.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Parallel Seq Scan&lt;/code&gt; -&amp;gt; This is the actual operation that is performed by the plan. In this case, it is a parallel sequential scan of the table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loops=3&lt;/code&gt; -&amp;gt; The number of times the operation is executed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Filter: (user_id = '10cfd02f-21d3-4439-9983-5685ec9204c4'::text)&lt;/code&gt; -&amp;gt; perform filter on the rows that are returned by the scan.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Rows Removed by Filter: 666666&lt;/code&gt; -&amp;gt; The number of rows that are removed by the filter. This is a per-loop average, rounded to the nearest integer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The total number of rows removed is this value multiplied by the number of loops. so in this case, it is 3*666666=1999998.&lt;/li&gt;
&lt;li&gt;So that means the total number of rows should be returned from the query is 2000000-1999998=2. But only 1 row is returned. where is the other row?&lt;/li&gt;
&lt;li&gt;This is because this value is a per-loop average, rounded to the nearest integer.&lt;/li&gt;
&lt;li&gt;What does this mean? When more than one loop is used for the plan node (e.g. loops=2 or greater), then “Rows Removed by Filter” is an average value of the “Rows Removed by Filter” per-loop.&lt;/li&gt;
&lt;li&gt;For example, if one loop removed 10 rows, and another removed 30 rows, we’d expect to see a value of 20 as the average of the two.&lt;/li&gt;
&lt;li&gt;When there’s one loop (loops=1), the figure is the actual number of rows processed and removed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Buffers: shared hit=8&lt;/code&gt; -&amp;gt; The number of buffers that are used by the plan. The number of buffers that are hit in the shared buffer cache.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;Planning Time: 3.036 ms&lt;/code&gt; -&amp;gt; The time taken by the planner to generate the plan. The planner generates the plan by considering different strategies from statistics.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For simple queries, the planning time is usually very low. But for complex queries involving multiple tables and joins, the planning time can be significant.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Execution Time: 449.998 ms&lt;/code&gt; -&amp;gt; The total time taken by the plan to execute. This is the sum of the actual time taken by the plan to execute. Generally this is what we try to optimize.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Whew, that was a lot of information specially for a simple select planning. But this information is very useful when optimizing queries. By looking at the output of the &lt;code&gt;Explain&lt;/code&gt; command, we can identify the bottlenecks in the query and optimize it accordingly.&lt;/p&gt;

&lt;p&gt;For more complex queries the output will be more complex and will have more information. But the basic structure of the output will be the same. Example for a more complex query with partitioned table, multiple join, subqueries and CTEs:&lt;/p&gt;

&lt;p&gt;Query:&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;Completed_orders&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="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o1&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;first_name&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;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&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;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;discount&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;final_price&lt;/span&gt;
&lt;span class="k"&gt;FROM&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;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
        &lt;span class="k"&gt;JOIN&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;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Completed_orders&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Completed'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&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;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
        &lt;span class="k"&gt;JOIN&lt;/span&gt;
    &lt;span class="n"&gt;order_details&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;
        &lt;span class="k"&gt;JOIN&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sort  (cost=211.31..211.77 rows=184 width=530) (actual time=0.124..0.125 rows=0 loops=1)
  Sort Key: o1.order_date DESC
  Sort Method: quicksort  Memory: 25kB
  Buffers: shared hit=3
  -&amp;gt;  Hash Join  (cost=82.80..204.39 rows=184 width=530) (actual time=0.038..0.039 rows=0 loops=1)
        Hash Cond: (od.product_id = p.product_id)
        -&amp;gt;  Hash Join  (cost=68.30..186.64 rows=184 width=284) (never executed)
              Hash Cond: (od.order_id = o1.order_id)
              -&amp;gt;  Append  (cost=0.00..101.20 rows=4080 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_1 od_1  (cost=0.00..20.20 rows=1020 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_2 od_2  (cost=0.00..20.20 rows=1020 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_3 od_3  (cost=0.00..20.20 rows=1020 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_4 od_4  (cost=0.00..20.20 rows=1020 width=40) (never executed)
              -&amp;gt;  Hash  (cost=68.19..68.19 rows=9 width=248) (never executed)
                    -&amp;gt;  Hash Join  (cost=56.03..68.19 rows=9 width=248) (never executed)
                          Hash Cond: (c1.customer_id = o1.customer_id)
                          -&amp;gt;  Seq Scan on customers c1  (cost=0.00..11.50 rows=150 width=240) (never executed)
                          -&amp;gt;  Hash  (cost=55.92..55.92 rows=9 width=16) (never executed)
                                -&amp;gt;  Append  (cost=0.00..55.92 rows=9 width=16) (never executed)
                                      -&amp;gt;  Seq Scan on orders_2023 o1_1  (cost=0.00..18.62 rows=3 width=16) (never executed)
                                            Filter: ((status)::text = 'Completed'::text)
                                      -&amp;gt;  Seq Scan on orders_2024 o1_2  (cost=0.00..18.62 rows=3 width=16) (never executed)
                                            Filter: ((status)::text = 'Completed'::text)
                                      -&amp;gt;  Seq Scan on orders_2025 o1_3  (cost=0.00..18.62 rows=3 width=16) (never executed)
                                            Filter: ((status)::text = 'Completed'::text)
        -&amp;gt;  Hash  (cost=12.00..12.00 rows=200 width=222) (actual time=0.005..0.005 rows=0 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 8kB
              -&amp;gt;  Seq Scan on products p  (cost=0.00..12.00 rows=200 width=222) (actual time=0.005..0.005 rows=0 loops=1)
Settings: search_path = 'public'
Planning:
  Buffers: shared hit=646
Planning Time: 3.759 ms
Execution Time: 0.362 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query:&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;Completed_orders&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="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o1&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;first_name&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;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&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;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;discount&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;final_price&lt;/span&gt;
&lt;span class="k"&gt;FROM&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;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
        &lt;span class="k"&gt;JOIN&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;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Completed_orders&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Completed'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&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;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
        &lt;span class="k"&gt;JOIN&lt;/span&gt;
    &lt;span class="n"&gt;order_details&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;
        &lt;span class="k"&gt;JOIN&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;od&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sort  (cost=211.31..211.77 rows=184 width=530) (actual time=0.124..0.125 rows=0 loops=1)
  Sort Key: o1.order_date DESC
  Sort Method: quicksort  Memory: 25kB
  Buffers: shared hit=3
  -&amp;gt;  Hash Join  (cost=82.80..204.39 rows=184 width=530) (actual time=0.038..0.039 rows=0 loops=1)
        Hash Cond: (od.product_id = p.product_id)
        -&amp;gt;  Hash Join  (cost=68.30..186.64 rows=184 width=284) (never executed)
              Hash Cond: (od.order_id = o1.order_id)
              -&amp;gt;  Append  (cost=0.00..101.20 rows=4080 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_1 od_1  (cost=0.00..20.20 rows=1020 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_2 od_2  (cost=0.00..20.20 rows=1020 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_3 od_3  (cost=0.00..20.20 rows=1020 width=40) (never executed)
                    -&amp;gt;  Seq Scan on order_details_4 od_4  (cost=0.00..20.20 rows=1020 width=40) (never executed)
              -&amp;gt;  Hash  (cost=68.19..68.19 rows=9 width=248) (never executed)
                    -&amp;gt;  Hash Join  (cost=56.03..68.19 rows=9 width=248) (never executed)
                          Hash Cond: (c1.customer_id = o1.customer_id)
                          -&amp;gt;  Seq Scan on customers c1  (cost=0.00..11.50 rows=150 width=240) (never executed)
                          -&amp;gt;  Hash  (cost=55.92..55.92 rows=9 width=16) (never executed)
                                -&amp;gt;  Append  (cost=0.00..55.92 rows=9 width=16) (never executed)
                                      -&amp;gt;  Seq Scan on orders_2023 o1_1  (cost=0.00..18.62 rows=3 width=16) (never executed)
                                            Filter: ((status)::text = 'Completed'::text)
                                      -&amp;gt;  Seq Scan on orders_2024 o1_2  (cost=0.00..18.62 rows=3 width=16) (never executed)
                                            Filter: ((status)::text = 'Completed'::text)
                                      -&amp;gt;  Seq Scan on orders_2025 o1_3  (cost=0.00..18.62 rows=3 width=16) (never executed)
                                            Filter: ((status)::text = 'Completed'::text)
        -&amp;gt;  Hash  (cost=12.00..12.00 rows=200 width=222) (actual time=0.005..0.005 rows=0 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 8kB
              -&amp;gt;  Seq Scan on products p  (cost=0.00..12.00 rows=200 width=222) (actual time=0.005..0.005 rows=0 loops=1)
Settings: search_path = 'public'
Planning:
  Buffers: shared hit=646
Planning Time: 3.759 ms
Execution Time: 0.362 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will explore these unexplained planning methods (such as &lt;code&gt;Hash Join&lt;/code&gt;, &lt;code&gt;Append&lt;/code&gt;, &lt;code&gt;Seq Scan&lt;/code&gt;, &lt;code&gt;Sort&lt;/code&gt;, etc.) in the upcoming sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualization Tools
&lt;/h2&gt;

&lt;p&gt;There are many tools available to visualize the query plan with explain analyze. Some of the popular ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PGAdmin&lt;/strong&gt;: PGAdmin has a pretty good graphical query plan visualization tool. You can see the query plan in a graphical format by clicking on the &lt;code&gt;Explain&lt;/code&gt; button in the query editor.&lt;/p&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%2Fraw.githubusercontent.com%2Fdriptaroop%2Fpostgres-engineering%2F656988edded5cb2bf4762a3262421512c664bf3a%2Fexplain%2Fassets%2Fpgadmin_explain_analyze_plan.svg%3Fraw%3Dtrue%2520align%3D" 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%2Fraw.githubusercontent.com%2Fdriptaroop%2Fpostgres-engineering%2F656988edded5cb2bf4762a3262421512c664bf3a%2Fexplain%2Fassets%2Fpgadmin_explain_analyze_plan.svg%3Fraw%3Dtrue%2520align%3D" alt="PGAdmin Explain" width="2250" height="1480"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://explain.depesz.com/" rel="noopener noreferrer"&gt;Depesz' EXPLAIN ANALYZE visualizer&lt;/a&gt;&lt;/p&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%2F2az05fj8cf104pl6c29l.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%2F2az05fj8cf104pl6c29l.png" alt="Depesz" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://explain.dalibo.com/" rel="noopener noreferrer"&gt;Dalibo's EXPLAIN ANALYZE visualizer&lt;/a&gt;. Offline version available at &lt;a href="https://github.com/dalibo/pev2" rel="noopener noreferrer"&gt;https://github.com/dalibo/pev2&lt;/a&gt;.&lt;/p&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%2Fttiir9c6erz2vst8mcof.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%2Fttiir9c6erz2vst8mcof.png" alt="PEV" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we explore the &lt;code&gt;PostgreSQL EXPLAIN&lt;/code&gt; command, a crucial tool for understanding and optimizing query performance. We delve into the basic usage of &lt;code&gt;EXPLAIN&lt;/code&gt; to reveal query plans, and discuss advanced options such as &lt;code&gt;ANALYZE&lt;/code&gt;, &lt;code&gt;BUFFERS&lt;/code&gt;, and more, which provide deeper insights into query execution and performance bottlenecks. Additionally, we analyze example output from &lt;code&gt;EXPLAIN&lt;/code&gt; and highlight visualization tools that can further aid in interpreting complex query plans. This piece offers essential knowledge for developers looking to enhance their SQL performance debugging skills using PostgreSQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Essential Reads
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;My own blog: &lt;a href="https://blog.dripto.xyz" rel="noopener noreferrer"&gt;https://blog.dripto.xyz&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-explain.html" rel="noopener noreferrer"&gt;PostgreSQL Documentation: EXPLAIN&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.depesz.com/2013/04/16/explaining-the-unexplainable/" rel="noopener noreferrer"&gt;Explaining the unexplainable&lt;/a&gt;: If you can read only one article about &lt;code&gt;Explain&lt;/code&gt;, this is the one to read.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.cybertec-postgresql.com/en/how-to-interpret-postgresql-explain-analyze-output/" rel="noopener noreferrer"&gt;How to interpret PostgreSQL EXPLAIN ANALYZE output&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/driptaroop/postgres-engineering/blob/main/explain/readme.md" rel="noopener noreferrer"&gt;This article also available in github.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
      <category>rdbms</category>
      <category>database</category>
    </item>
    <item>
      <title>Distributed job runner/task scheduler with postgres and spring</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Wed, 15 Mar 2023 07:12:31 +0000</pubDate>
      <link>https://dev.to/dripto/distributed-job-runnertask-scheduler-with-postgres-and-spring-3pbf</link>
      <guid>https://dev.to/dripto/distributed-job-runnertask-scheduler-with-postgres-and-spring-3pbf</guid>
      <description>&lt;p&gt;Imagine a scenario where there is a system that is inserting new rows occasionally in your database table. and your application is querying the database to see if there are any new rows inserted in the table, and if there are any new rows, it will take those rows up and process and update them.&lt;/p&gt;

&lt;p&gt;In a non-distributed system, the process of querying the database to see if there are any new rows, and processing and updating them, works fine. However, in a distributed system, there's always the possibility of multiple instances updating a single row, leading to data race and deadlocking.&lt;/p&gt;

&lt;p&gt;One of the solutions to this problem is using a distributed job runner/task scheduler. frameworks like shedlock(&lt;a href="https://github.com/lukas-krecan/ShedLock"&gt;https://github.com/lukas-krecan/ShedLock&lt;/a&gt;) solve this by employing a distributed lock. Shedlock gives you the guarantee that even in the case of multiple instances/nodes/threads, a task lock can be acquired by at most 1 node. But this is not the most efficient and scalable solution in a distributed environment. If there are multiple nodes, only one node will do all the processing, and other nodes will be idle, effectively emulating a non-distributed environment.&lt;/p&gt;

&lt;p&gt;A better solution is to leverage the &lt;code&gt;Postgres&lt;/code&gt; pessimistic locking in combination with &lt;code&gt;skip_locked&lt;/code&gt; to pick up equal amounts of rows to process. This way, we can scale up or down the number of nodes to handle the load. Let me show how in an example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example with Spring boot
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Let's assume there is a very simple table consisting of only 2 columns, the primary key &lt;code&gt;id&lt;/code&gt; and the &lt;code&gt;data&lt;/code&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The equivalent JPA entity and repository would be,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TestRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now imagine someplace where we need to check for new rows in the table and update them,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;testRepo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TestRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findFirstByProcessedIsFalse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// ----- (1)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                       
            &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                        
            &lt;span class="n"&gt;testRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"do processing"&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;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;(1): this method does not exist yet. We'll add them next
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TestRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findFirstByProcessedIsFalse&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Test&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;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;A single instance of this code will run just fine. But in a distributed system, there would be data race when multiple nodes will try to get a hold of the same row from the DB table. Since this operation is &lt;code&gt;transactional&lt;/code&gt;, only one node will be able to acquire the locks. This will result in blocked threads in other nodes, which will either wait till the lock is released or the DB lock timeout is up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;Let's solve this using &lt;code&gt;pessimistic locking&lt;/code&gt; in combination with &lt;code&gt;skip_locked&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;SKIP_LOCK&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-2"&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TestRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LockModeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ------ (1)&lt;/span&gt;
    &lt;span class="nd"&gt;@QueryHints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;QueryHint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AvailableSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JPA_LOCK_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SKIP_LOCK&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// ------ (2)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findFirstByProcessedIsFalse&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Test&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;blockquote&gt;
&lt;p&gt;The method employs two tricks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@Lock(LockModeType.PESSIMISTIC_WRITE)&lt;/code&gt; creates and holds a pessimistic lock on a row. As long as the lock is held, the rows cannot be edited by other threads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@QueryHints(QueryHint(name = AvailableSettings.JPA_LOCK_TIMEOUT, value = SKIP_LOCK))&lt;/code&gt; instructs other threads not to wait for acquiring the pessimistic locks and instead skip them and move on to the next free row. This is achieved by changing the &lt;code&gt;JPA_LOCK_TIMEOUT&lt;/code&gt; mode. The &lt;code&gt;SKIP_LOCK&lt;/code&gt; timeout mode value in JPA is &lt;code&gt;-2&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;And thats it. No need to change the rest of the code. This will ensure that the nodes in a distributed system will be able to get a hold of unique rows and do processing on them, effectively enabling a more scalable system.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>kotlin</category>
      <category>springboot</category>
      <category>java</category>
    </item>
    <item>
      <title>Using layered docker images over fat-jar docker images in spring boot application</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Wed, 01 Feb 2023 17:24:22 +0000</pubDate>
      <link>https://dev.to/dripto/using-layered-docker-images-over-fat-jar-docker-images-in-spring-boot-application-d7</link>
      <guid>https://dev.to/dripto/using-layered-docker-images-over-fat-jar-docker-images-in-spring-boot-application-d7</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;To achieve more efficient docker image building and faster startup times, instead of doing this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM eclipse-temurin:17-jdk
ARG ARG_VERSION
ARG APP_NAME=test-app

EXPOSE 8080

WORKDIR app
ARG JAR_FILE=build/libs/${APP_NAME}-${ARG_VERSION}.jar

ADD ${JAR_FILE} app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

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

&lt;/div&gt;



&lt;p&gt;Do this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM eclipse-temurin:17-jdk as builder
ARG ARG_VERSION
ARG APP_NAME=test-app

ARG JAR_FILE=build/libs/${APP_NAME}-${ARG_VERSION}.jar
WORKDIR app

COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM eclipse-temurin:17-jdk
WORKDIR /app

COPY --from=builder app/dependencies/ ./
COPY --from=builder app/spring-boot-loader/ ./
COPY --from=builder app/snapshot-dependencies/ ./
COPY --from=builder app/application/ ./

EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Even though considered archaic by some, using &lt;code&gt;Dockerfile&lt;/code&gt; is still one of the more flexible ways to create a docker image (and contrary to popular belief, it doesn't always require a docker daemon to be present, i.e. check out kaniko). In our organization, it is very common to build docker images for &lt;code&gt;Spring-boot&lt;/code&gt; applications via a &lt;code&gt;Dockerfile&lt;/code&gt; for deployment purposes.&lt;/p&gt;

&lt;p&gt;One of the main issues is that over time, the use of a traditional &lt;code&gt;Dockerfile&lt;/code&gt;s can consume disk space exponentially.&lt;/p&gt;

&lt;p&gt;To illustrate this, let's take an example of a typical build of a Spring Boot repository. This build creates what is known as a &lt;code&gt;fat-jar&lt;/code&gt;, which is &lt;em&gt;a JAR file that contains not only the Java program but also embeds its dependencies&lt;/em&gt;. Since it contains a lot of dependencies, it is not uncommon for a &lt;code&gt;fat-jar&lt;/code&gt; of a fairly complex project to be around 100MB in size.&lt;/p&gt;

&lt;p&gt;Now, consider an organization that has around 50 microservices. With each build of these microservices, the organization will be generating around 5GB of data (100MB x 50). If these services are built around 60 times a month (twice a day on average, though it should be much more if done in a CI pipeline) and each build creates and deploys its own docker image, this will take up around 300GB of storage at least per month. As you can see, this can quickly become an issue as the number of services and builds increases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can be done?
&lt;/h2&gt;

&lt;p&gt;As we've seen, the use of fat jar images can lead to a significant increase in disk space usage over time. However, it's worth noting that even though the images are built multiple times, only a small fraction of it actually changes per build. The dependencies largely stay the same, and only the application code changes. In fact, the application code that changes is usually quite small in comparison to the entire image, often being less than 10MB. By keeping this information in mind, we can leverage Docker image layers to our advantage. By using layers, we can create more efficient and secure images that are faster to build and deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding Docker Images and Layers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A Docker image is a collection of layers. Each layer is an immutable TAR archive with a hash code generated from the file. When building a Docker image, each command that adds files will result in a layer being created. These layers can be cached and reused in future builds, making the process more efficient.&lt;/p&gt;

&lt;p&gt;When using a &lt;code&gt;fat-jar&lt;/code&gt; image, the &lt;code&gt;fat-jar&lt;/code&gt; is added directly to the &lt;code&gt;Dockerfile&lt;/code&gt;. This creates a Docker image with a single layer of application and dependencies. As a result, every single change to the fat jar (even for a single file change) will create a new layer and thus adding to the issue of disk space consumption as described previously. By leveraging layers, we can optimize the space usage as well as making the process of building and deploying the images faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Spring Boot and Layered Jars&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Spring Boot version 2.3.0 and above offers two new features to improve the generation of Docker images:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Buildpack support&lt;/strong&gt; : This feature provides the Java runtime for the application, allowing for the automatic building of the Docker image without the need for a &lt;code&gt;Dockerfile&lt;/code&gt;. However, this feature is out of scope for this blog post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layered jars&lt;/strong&gt; : This feature helps to optimize the Docker layer generation process. If the Spring Boot jar is created using the &lt;code&gt;spring-boot-maven-plugin&lt;/code&gt; or &lt;code&gt;spring-boot-gradle-plugin&lt;/code&gt;, the jar file comes pre-created with 4 layers. The &lt;code&gt;BOOT-INF/layers.idx&lt;/code&gt; file records the different layers and can be checked by extracting the jar. A sample &lt;code&gt;BOOT-INF/layers.idx&lt;/code&gt; file might look like this:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; - &lt;span class="s2"&gt;"dependencies"&lt;/span&gt;:
   - &lt;span class="s2"&gt;"BOOT-INF/lib/"&lt;/span&gt;
 - &lt;span class="s2"&gt;"spring-boot-loader"&lt;/span&gt;:
   - &lt;span class="s2"&gt;"org/"&lt;/span&gt;
 - &lt;span class="s2"&gt;"snapshot-dependencies"&lt;/span&gt;:
 - &lt;span class="s2"&gt;"application"&lt;/span&gt;:
   - &lt;span class="s2"&gt;"BOOT-INF/classes/"&lt;/span&gt;
   - &lt;span class="s2"&gt;"BOOT-INF/classpath.idx"&lt;/span&gt;
   - &lt;span class="s2"&gt;"BOOT-INF/layers.idx"&lt;/span&gt;
   - &lt;span class="s2"&gt;"META-INF/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The layers can also be inspected by using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;layertools &lt;span class="nt"&gt;-jar&lt;/span&gt; target/&amp;lt;jar_name&amp;gt;.jar list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will provide a simplistic view of the content of the &lt;code&gt;layers.idx&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dependencies
 spring-boot-loader
 snapshot-dependencies
 application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also extract the layers into directories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;layertools &lt;span class="nt"&gt;-jar&lt;/span&gt; target/&amp;lt;jar_name&amp;gt;.jar extract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;application&lt;/code&gt; changes the most often. It hosts the actual code for the application. By storing each layer as an individual Docker image layer, we can optimize the space usage and speed up the build and deployment process. Only the application layer will be changed with each build, while the other layers can be cached and reused from previous builds.&lt;/p&gt;

&lt;p&gt;So, how do we do this?&lt;/p&gt;

&lt;p&gt;We use multistage Docker builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multistage Dockerfiles
&lt;/h2&gt;

&lt;p&gt;We use multistage dockerfiles to create layered docker images for our Spring Boot applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, let's add the fat jar file to the base image:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; FROM eclipse-temurin:17-jdk as builder
 ARG ARG_VERSION
 ARG &lt;span class="nv"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-app

 ARG &lt;span class="nv"&gt;JAR_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build/libs/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.jar
 WORKDIR app

 COPY &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JAR_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; app.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, we extract the layers of the artifact using the following command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; RUN java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;layertools &lt;span class="nt"&gt;-jar&lt;/span&gt; app.jar extract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then, we copy the layers from the builder image to the actual image:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; FROM eclipse-temurin:17-jdk
 WORKDIR /app

 COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder app/dependencies/ ./
 COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder app/spring-boot-loader/ ./
 COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder app/snapshot-dependencies/ ./
 COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder app/application/ ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, we start the image using &lt;code&gt;org.springframework.boot.loader.JarLauncher&lt;/code&gt; and expose the necessary ports:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; EXPOSE 8080
 ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"java"&lt;/span&gt;, &lt;span class="s2"&gt;"org.springframework.boot.loader.JarLauncher"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM eclipse-temurin:17-jdk as builder
ARG ARG_VERSION
ARG APP_NAME=test-app

ARG JAR_FILE=build/libs/${APP_NAME}-${ARG_VERSION}.jar
WORKDIR app

COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM eclipse-temurin:17-jdk
WORKDIR /app

COPY --from=builder app/dependencies/ ./
COPY --from=builder app/spring-boot-loader/ ./
COPY --from=builder app/snapshot-dependencies/ ./
COPY --from=builder app/application/ ./

EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Additional Advantage:
&lt;/h2&gt;

&lt;p&gt;Since the jar is already extracted in the docker image, the startup time is improved. On average, I can see a 1 - 1.5 sec improvement in the startup time across all my spring boot application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;In conclusion, using layered docker images can greatly reduce the storage space consumed by your Spring Boot applications and improve the startup time of your services. By using multistage Dockerfiles, we can extract the layers of our fat jar and store them in individual image layers. This way, only the application layer will be changed per build and the other layers can be cached and reused from the previous builds. This not only saves storage space, but also improves the startup time of your services, providing an additional advantage to your organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Read:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.baeldung.com/docker-layers-spring-boot" rel="noopener noreferrer"&gt;https://www.baeldung.com/docker-layers-spring-boot&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://springframework.guru/why-you-should-be-using-spring-boot-docker-layers/" rel="noopener noreferrer"&gt;https://springframework.guru/why-you-should-be-using-spring-boot-docker-layers/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=hAHXp_jQWVo" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=hAHXp_jQWVo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>docker</category>
      <category>springboot</category>
      <category>java</category>
      <category>programming</category>
    </item>
    <item>
      <title>Declarative HTTP Clients in Spring Boot 3 with Kotlin</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Mon, 30 Jan 2023 03:27:39 +0000</pubDate>
      <link>https://dev.to/dripto/declarative-http-clients-in-spring-boot-3-with-kotlin-4c16</link>
      <guid>https://dev.to/dripto/declarative-http-clients-in-spring-boot-3-with-kotlin-4c16</guid>
      <description>&lt;p&gt;Kotlin and Spring Boot 3 make a powerful combination for building modern, scalable applications. With the introduction of declarative HTTP clients in Spring Boot 3, developers can define their HTTP client configurations in a concise, readable manner.&lt;/p&gt;

&lt;p&gt;In this blog post, we'll explore how to use declarative HTTP clients in Spring Boot 3 with Kotlin to streamline your development process and increase your productivity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Setting up Spring Boot 3&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use declarative HTTP clients in Spring Boot 3 with Kotlin, you'll need to have a Spring Boot 3 project set up using Kotlin as the programming language. You can create a new Spring Boot 3 project using the Spring Initializer and selecting Kotlin as the programming language.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Enabling declarative HTTP clients&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to use the webflux dependency to enable declarative clients. If you already have your project set up, you'll need to enable the HTTP clients by adding the following dependency to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.springframework.boot:spring-boot-starter-webflux"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Defining your client&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, you'll need to define your HTTP client by creating a new interface. This interface will define all of the endpoints that you need to make HTTP requests to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.PathVariable&lt;/span&gt;
 &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.service.annotation.GetExchange&lt;/span&gt;
 &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.service.annotation.HttpExchange&lt;/span&gt;

 &lt;span class="nd"&gt;@HttpExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/shows"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ImdbClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nd"&gt;@GetExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{showId}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getShow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"showId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;showId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@HttpExchange&lt;/code&gt;: Used here in the class level to declare the base path for all the http calls. Also can be used to declare other parameters of the http call.&lt;br&gt;
&lt;code&gt;@GetExchange&lt;/code&gt;: a specialized version of @HttpExchange with GET method to use with individual methods.&lt;br&gt;
&lt;code&gt;@PathVariable&lt;/code&gt;: Annotation which indicates that a method parameter should be bound to a URI template variable. Note the templated path variable showId in the @GetExchange("/{showId}") annotation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Autowiring your client&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;you can autowire your client into your application and use it to make HTTP requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt; &lt;span class="nd"&gt;@Service&lt;/span&gt;
 &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ImdbClient&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getShows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;show&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getShow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1"&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;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Configuration&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, You can customize and bootstrap your HTTP client by supplying a configuration to bind it with a webclient instance. This will allow you to configure the underlying WebClient.Builder used to create your client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt; &lt;span class="nd"&gt;@Configuration&lt;/span&gt;
 &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nd"&gt;@Bean&lt;/span&gt;
     &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;imdbClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ImdbClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;webClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.imdb.com"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;HttpServiceProxyFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebClientAdapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ImdbClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&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;In conclusion, declarative HTTP clients in Spring Boot 3 and Kotlin provide a simple and efficient way to handle HTTP requests in your applications. With its concise syntax and powerful features, you can streamline your development process and improve the overall quality of your code.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>springboot</category>
      <category>rest</category>
      <category>java</category>
    </item>
    <item>
      <title>Lazily Initializing Implicit Logger in Every Class in Kotlin</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Mon, 30 Jan 2023 00:12:00 +0000</pubDate>
      <link>https://dev.to/dripto/lazily-initializing-implicit-logger-in-every-class-in-kotlin-3kf3</link>
      <guid>https://dev.to/dripto/lazily-initializing-implicit-logger-in-every-class-in-kotlin-3kf3</guid>
      <description>&lt;h2&gt;
  
  
  How Kotlin(JVM) logger declaration works
&lt;/h2&gt;

&lt;p&gt;If you're familiar with the Kotlin programming language on the JVM platform, you may have encountered the need for logging in your projects. Typically, this requires declaring a logger for each class in your code, using a library such as slf4j. While this approach is functional, it can become tedious and repetitive, especially as the number of classes in your project increases. In this blog, we'll explore an alternative method for class-level logging in Kotlin JVM that leverages implicit loggers, streamlining the process and reducing the boilerplate code you need to write.&lt;/p&gt;

&lt;p&gt;A typical logger declaration using slf4j in Kotlin JVM may look like the following code sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.LoggerFactory&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;log&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doing things"&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;It's worth noting that if you choose to use a different logging framework, such as log4j2 or kotlin-logging, the code for declaring a logger may look different. For instance, here's a code sample for declaring a logger using kotlin-logging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KotlinLogging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The syntax may vary based on the specific logging framework you choose, but the overall process of declaring a logger for each class remains the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Lombok does this with less boilerplate in Java
&lt;/h2&gt;

&lt;p&gt;If you're familiar with Java, you may have encountered Lombok, a library that reduces boilerplate code in your Java projects. One aspect of Lombok that can be particularly useful for logging is its ability to simplify the declaration of class-level loggers. With Lombok, a logger declaration can be reduced to just a single annotation, as demonstrated in this code sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.extern.slf4j.Slf4j&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleClass&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something is wrong here"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;@Slf4j&lt;/code&gt; annotation generates a logger for the class using the slf4j framework. This greatly reduces the amount of code you need to write for logging, making it a convenient and efficient solution for logging in Java projects. While Kotlin does not have a direct equivalent to Lombok, there is another approache to streamlining logging that we'll explore in this blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we do something better in Kotlin?
&lt;/h2&gt;

&lt;p&gt;As it turned out, we can.&lt;/p&gt;

&lt;p&gt;Fortunately, Kotlin provides features that allow us to improve upon the standard method of declaring loggers for each class. One such feature is reified type parameters, which enables the creation of inline extension variables with preserved type information. With this in mind, we can create an inline extension variable that acts as an implicit logger for a class. The reified generics allow us to fetch class details to pass to the logger creation, and by storing the created loggers in a hashmap, we can avoid redundant recreations and ensure constant lookup times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.Logger&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.LoggerFactory&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.reflect.KClass&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;loggerMap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashMapOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;KClass&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;reified&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="py"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loggerMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrPut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&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="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isCompanion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enclosingClass&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&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;Once this code is in classpath, declaring a logger for a class becomes incredibly simple. That is, you don't have to declare anything at all, just go ahead and use the &lt;code&gt;log&lt;/code&gt; variable and Kotlin compiler will create one for you the first time it is used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doing things"&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;This reduces the amount of code required and makes the logging process more efficient and less prone to error. You can even put it in a library and use it for multiple applications.&lt;/p&gt;

&lt;p&gt;Whether you're a seasoned developer or just starting out with Kotlin, implementing an implicit logger is a great way to streamline your logging process and save time in your development workflow. So, give it a try and see how it can simplify your projects.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>jvm</category>
      <category>logging</category>
      <category>java</category>
    </item>
    <item>
      <title>Using user-defined types (UDT) with Apache Cassandra in Spring Boot 3.x</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Fri, 27 Jan 2023 11:57:22 +0000</pubDate>
      <link>https://dev.to/dripto/using-user-defined-types-udt-with-apache-cassandra-in-spring-boot-3x-345j</link>
      <guid>https://dev.to/dripto/using-user-defined-types-udt-with-apache-cassandra-in-spring-boot-3x-345j</guid>
      <description>&lt;p&gt;Apache Cassandra is a highly scalable and distributed NoSQL database that is well-suited for handling large amounts of data. One of the powerful features of Cassandra is the ability to use user-defined types (UDT) to model complex data structures. In this blog post, we will learn how to use UDTs with Spring Boot 3.x to interact with a Cassandra database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the data model in cassandra
&lt;/h2&gt;

&lt;p&gt;First, let's create a simple UDT in Cassandra. You can do this by running the following command in the Cassandra shell (imagining &lt;code&gt;test&lt;/code&gt; being the cassandra keyspace):&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;street&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;zip&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This creates a UDT called "address" with three fields: street, city, and zip.&lt;/p&gt;

&lt;p&gt;Next, we will create a table that uses this UDT. For example, we can create a table called "users" with the following command:&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This table has three columns: id, name, and address. The "address" column is the UDT type here. When defining a user-defined type column, it is best practice to utilize the &lt;code&gt;frozen&lt;/code&gt; keyword. This ensures that the user-defined type value cannot be partially updated, but must be completely overwritten. By using the 'frozen' keyword, Cassandra treats the value of the user-defined type as a single, immutable block of data, similar to a binary large object (BLOB).&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup spring boot 3.x application
&lt;/h2&gt;

&lt;p&gt;Now that we have our UDT and table set up, we can start using them with Spring Boot. Head to &lt;code&gt;start.spring.io&lt;/code&gt; to create a spring boot 3.x project with cassandra or add the following dependency to the existing spring boot projects.&lt;/p&gt;

&lt;p&gt;Gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.springframework.boot:spring-boot-starter-data-cassandra"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Maven:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-data-cassandra&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We also need to configure the Cassandra connection in our application.yml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cassandra&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contact-points&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9042&lt;/span&gt;
      &lt;span class="na"&gt;keyspace-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup data model in Spring boot application
&lt;/h2&gt;

&lt;p&gt;Once we have the dependency and configuration in place, We also need to create a POJO class that represents our "users" table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.cassandra.core.mapping.PrimaryKey&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.cassandra.core.mapping.Table&lt;/span&gt;

&lt;span class="nd"&gt;@Table&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@PrimaryKey&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @CassandraType annotation is not required by default and is redundant. but can be added if requried.
     * @CassandraType(type = CassandraType.Name.UDT, userTypeName = "address")
     */&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And a POJO class that represents our &lt;code&gt;address&lt;/code&gt; UDT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.cassandra.core.mapping.UserDefinedType&lt;/span&gt;

&lt;span class="nd"&gt;@UserDefinedType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;we also need to create a Cassandra repository to interact with our &lt;code&gt;users&lt;/code&gt; table. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.cassandra.repository.CassandraRepository&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CassandraRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use the repository to call Cassandra
&lt;/h2&gt;

&lt;p&gt;With these classes in place, we can now use the UserRepository to perform CRUD operations on the &lt;code&gt;users&lt;/code&gt; table. For example, we can save a new user with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRunner&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&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="s"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Street name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Berlin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We hope this tutorial gave you a good idea on how to use user-defined types (UDT) with Apache Cassandra in Spring Boot 3.x. We've shown you how to create a UDT and table in Cassandra, and how to use them with Spring Boot to perform CRUD operations on the table.&lt;/p&gt;

&lt;p&gt;Using UDTs can be a powerful way to model complex data structures in Cassandra, and Spring Boot makes it easy to work with these structures in a Java application. With the right configuration and setup, you can easily start using UDTs in your projects to improve the scalability and performance of your data storage.&lt;/p&gt;

&lt;p&gt;Be sure to follow best practices when working with UDTs, such as using frozen types and avoiding unnecessary nesting to minimize the complexity. Remember also to keep in mind that UDTs are not supported in some version of Cassandra and not all the drivers support it.&lt;/p&gt;

&lt;p&gt;As always, feel free to leave a comment or reach out if you have any questions or need further help. Happy coding!&lt;/p&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>cassandra</category>
      <category>spring</category>
    </item>
    <item>
      <title>Execute startup scripts in cassandra docker</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Tue, 24 Jan 2023 23:57:34 +0000</pubDate>
      <link>https://dev.to/dripto/execute-startup-scripts-in-cassandra-docker-cp2</link>
      <guid>https://dev.to/dripto/execute-startup-scripts-in-cassandra-docker-cp2</guid>
      <description>&lt;p&gt;Recently I started learning about Cassandra. As with any of my learning journeys, it started with me creating a small-scale application in my local machine to use as my inner feedback loop to tinker with. I chose to have a docker-compose file to start the Cassandra instance for me whenever I wanted.&lt;/p&gt;

&lt;p&gt;One of the things I wanted to do was to auto-create and provision the &lt;code&gt;keyspace&lt;/code&gt; in the Cassandra container as soon as it started. There were 2 ways I found to do that,&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;docker-entrypoint-initdb.d&lt;/code&gt; :
&lt;/h2&gt;

&lt;p&gt;if using the &lt;code&gt;bitnami/cassandra&lt;/code&gt; images, the script (&lt;code&gt;sh&lt;/code&gt;, &lt;code&gt;cql&lt;/code&gt; or &lt;code&gt;cql.gz&lt;/code&gt;) files at the &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt; directory are executed at the startup. Its fairly easy to accomplish that. Assuming the startup scripts are at &lt;code&gt;./init-scripts/cassandra&lt;/code&gt; location, the docker-compose file would look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  cassandra:
    image: bitnami/cassandra:4.0.7
    ports:
      - "7000:7000"
      - "9042:9042"
    environment:
      - CASSANDRA_CLUSTER_NAME=test
    volumes:
      - "./init-scripts/cassandra:/docker-entrypoint-initdb.d"

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

&lt;/div&gt;



&lt;p&gt;But somehow I couldn't get the bitnami docker image to work for me without issues. The other option was to use the official cassandra image. But that one didn't have the &lt;code&gt;docker-entrypoint-initdb.d&lt;/code&gt; startup script like the bitnami image. The other solution was to use an init-container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using init-containers:
&lt;/h2&gt;

&lt;p&gt;The idea of an init-container is fairly simple. In addition to the actual container, one additional container is needed to startup, execute startup scripts on actual container and then stop silently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  cassandra:
    image: cassandra:4.1.0
    ports:
      - "7000:7000"
      - "9042:9042"
    environment:
      - CASSANDRA_CLUSTER_NAME=test
  init-cassandra:
    image: cassandra:4.1.0
    depends_on:
      - cassandra # IMPORTANT: this init container can only start up after the original container is started
    restart: "no" # IMPORTANT: the restart should be set to "no" so that the init container is used only once after the original container is started
    entrypoint: ["/init.sh"] # executing the init script
    volumes:
      - ./cassandra-init-data.sh:/init.sh # the init script is added via volumes

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

&lt;/div&gt;



&lt;p&gt;The init script (&lt;code&gt;./cassandra-init-data.sh&lt;/code&gt; in this case) looked like this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env bash

until printf "" 2&amp;gt;&amp;gt;/dev/null &amp;gt;&amp;gt;/dev/tcp/cassandra/9042; do
    sleep 5;
    echo "Waiting for cassandra...";
done

echo "Creating keyspace"
cqlsh cassandra -u cassandra -p cassandra -e "CREATE KEYSPACE IF NOT EXISTS spring_cassandra WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};"

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

&lt;/div&gt;



&lt;p&gt;This script waits for cassandra 9042 port to be available in the original image and then creates a keyspace.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;printf "" 2&amp;gt;&amp;gt;/dev/null &amp;gt;&amp;gt;/dev/tcp/cassandra/9042&lt;/code&gt; checks whether any message can be sent to &lt;code&gt;cassandra:9042&lt;/code&gt; port or else it is failing silently. Check &lt;a href="https://stackoverflow.com/questions/27599839/how-to-wait-for-an-open-port-with-netcat/50055449#50055449" rel="noopener noreferrer"&gt;this link&lt;/a&gt; for more details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this is it. It is something new that I learned today and thought it is interesting enough to be shared in here.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>cassandra</category>
      <category>nosql</category>
      <category>programming</category>
    </item>
    <item>
      <title>Accessing the POST request body in Spring WebClient filters</title>
      <dc:creator>Driptaroop Das</dc:creator>
      <pubDate>Mon, 16 Jan 2023 00:34:13 +0000</pubDate>
      <link>https://dev.to/dripto/accessing-the-post-request-body-in-spring-webclient-filters-38kk</link>
      <guid>https://dev.to/dripto/accessing-the-post-request-body-in-spring-webclient-filters-38kk</guid>
      <description>&lt;p&gt;Accessing the POST request body in Spring WebClient filters can be a bit tricky, as the request body is typically passed as a stream. In this blog post, we'll explore two solutions to this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;In some cases, you may need to access the POST request body in a Spring WebClient filter, in order to perform an operation such as signing the request with JWS header. However, because the request body is passed as a stream, it can be difficult to access and manipulate all at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Extending the &lt;code&gt;ClientHttpRequestDecorator&lt;/code&gt; class
&lt;/h3&gt;

&lt;p&gt;One approach to solving this problem is to extend the &lt;code&gt;ClientHttpRequestDecorator&lt;/code&gt; class and override the &lt;code&gt;writeWith&lt;/code&gt; method. This allows you to gain access to the &lt;code&gt;DataBuffer&lt;/code&gt; publisher, which represents the stream of the request body. You can then concatenate and retrieve the original request by joining the published data buffers using &lt;code&gt;DataBufferUtils&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BufferingRequestDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClientHttpRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClientHttpRequestDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;writeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;DataBuffer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;DataBufferUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setJwsHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extractBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;just&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setJwsHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"JWS-header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;createJws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// or do whatever you want with the data&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;extractBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DataBuffer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readableByteCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readPosition&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bytes&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;Once you have this class implemented, you can call it in the WebClient filter and deconstruct the old request and create a new one out of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebClientBufferingFilter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ExchangeFilterFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClientRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ExchangeFunction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;ClientRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;outputMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nc"&gt;BufferingRequestDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputMessage&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;context&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;build&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;Create a webclient with this filter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebClientBufferingFilter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution 2
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using a custom JSON Encoder
&lt;/h3&gt;

&lt;p&gt;By default, WebClient uses Jackson to encode the data into JSON. When setting up the request, we use the utility BodyInserters.fromValue to create a body inserter for our data. The DefaultRequestBuilder for WebClient keeps this BodyInserter object at the time when the request will be sent, passing it all known message-writers and other relevant contextual information. However, we may need to access the request body before it is sent in order to perform additional actions such as signing or adding an authorization header.&lt;/p&gt;

&lt;p&gt;Create a wrapper class around Jackson2JsonEncoder that allows us to intercept the encoded body. Specifically, we will be wrapping the encode method implementation from AbstractJackson2Encoder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;REQUEST_CONTEXT_KEY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"test"&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BodyProvidingJsonEncoder&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Jackson2JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bufferFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DataBufferFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;elementType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ResolvableType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MimeType&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="n"&gt;hints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataBuffer&lt;/span&gt;&lt;span class="p"&gt;&amp;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;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufferFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elementType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DataBuffer&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deferContextual&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;clientHttpRequest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientHttpRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;REQUEST_CONTEXT_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;db&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;extractBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DataBuffer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readableByteCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readPosition&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bytes&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;Build a custom &lt;code&gt;ReactorClientHttpConnector&lt;/code&gt; to put the request in the context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MessageSigningHttpConnector&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ReactorClientHttpConnector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;requestCallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nc"&gt;ClientHttpRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientHttpResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// execute the super-class method as usual, but insert an interception into the requestCallback that can&lt;/span&gt;
        &lt;span class="c1"&gt;// capture the request to be saved for this thread.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;incomingRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClientHttpRequest&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;requestCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incomingRequest&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contextWrite&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nc"&gt;REQUEST_CONTEXT_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;incomingRequest&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define a custom &lt;code&gt;ExchangeFunction&lt;/code&gt; using the utility &lt;code&gt;ExchangeFunctions.create()&lt;/code&gt; which accepts a custom &lt;code&gt;HttpConnector&lt;/code&gt;. This connector has access to the function that makes the request. It is at this point that we can get a handle on the &lt;code&gt;ClientHttpRequest&lt;/code&gt; and wait for the body to be serialized so that the header can be added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;httpConnector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessageSigningHttpConnector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bodyProvidingJsonEncoder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BodyProvidingJsonEncoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exchangeFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExchangeFunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;httpConnector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;ExchangeStrategies&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;codecs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;clientDefaultCodecsConfigurer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClientCodecConfigurer&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;clientDefaultCodecsConfigurer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defaultCodecs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;jackson2JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bodyProvidingJsonEncoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;clientDefaultCodecsConfigurer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defaultCodecs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jackson2JsonDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Jackson2JsonDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;APPLICATION_JSON&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="nf"&gt;build&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="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;As always, be sure to test your codes thoroughly.&lt;/p&gt;

&lt;p&gt;Solution 2 is originally found in &lt;a href="https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient" rel="noopener noreferrer"&gt;https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient&lt;/a&gt;. I tinkered with it a bit before finding solution 1 which is somehow inspired by &lt;a href="https://github.com/spring-projects/spring-framework/issues/26489" rel="noopener noreferrer"&gt;https://github.com/spring-projects/spring-framework/issues/26489&lt;/a&gt;. Finally, I decided to go with solution 1 as I found it to be a bit more simple and elegant.&lt;/p&gt;

&lt;p&gt;originally published at: &lt;a href="https://blog.dripto.xyz/accessing-the-post-request-body-in-spring-webclient-filters" rel="noopener noreferrer"&gt;https://blog.dripto.xyz/accessing-the-post-request-body-in-spring-webclient-filters&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>programming</category>
      <category>java</category>
      <category>kotlin</category>
    </item>
  </channel>
</rss>
