<?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: Bolaji Wahab</title>
    <description>The latest articles on DEV Community by Bolaji Wahab (@bolajiwahab).</description>
    <link>https://dev.to/bolajiwahab</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%2F789892%2Fbab32861-9369-432a-b556-45a98f26b94f.png</url>
      <title>DEV Community: Bolaji Wahab</title>
      <link>https://dev.to/bolajiwahab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bolajiwahab"/>
    <language>en</language>
    <item>
      <title>Data type mismatch in referential integrity and its implications</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Mon, 10 Apr 2023 15:43:19 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/data-type-mismatch-in-referential-integrity-and-its-implications-3hf8</link>
      <guid>https://dev.to/bolajiwahab/data-type-mismatch-in-referential-integrity-and-its-implications-3hf8</guid>
      <description>&lt;p&gt;I have seen situations where different compatible types were used in referential integrity AKA foreign key constraints. When we say data types are compatible, it means there is a conversion path between the types.&lt;/p&gt;

&lt;p&gt;The catalog &lt;code&gt;pg_cast&lt;/code&gt; stores data type conversion paths, both built-in and user-defined but it does not represent every type conversion that the system knows how to perform. Yet, we can infer some compatible types from &lt;code&gt;pg_cast&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let us have a look at compatible types for &lt;code&gt;integer&lt;/code&gt; and &lt;code&gt;varchar&lt;/code&gt;&lt;/p&gt;

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

postgres=# SELECT castsource::regtype, casttarget::regtype FROM pg_cast WHERE castsource IN ('integer'::regtype, 'varchar'::regtype) ORDER BY castsource;
    castsource     |    casttarget
-------------------+-------------------
 integer           | bigint
 integer           | smallint
 integer           | real
 integer           | double precision
 integer           | numeric
 integer           | money
 integer           | boolean
 integer           | oid
 integer           | regproc
 integer           | regprocedure
 integer           | regoper
 integer           | regoperator
 integer           | regclass
 integer           | regcollation
 integer           | regtype
 integer           | regconfig
 integer           | regdictionary
 integer           | regrole
 integer           | regnamespace
 integer           | "char"
 integer           | bit
 character varying | character
 character varying | "char"
 character varying | name
 character varying | regclass
 character varying | xml
 character varying | character varying
 character varying | text
(28 rows)


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

&lt;/div&gt;

&lt;p&gt;Let us assume we have a &lt;code&gt;products&lt;/code&gt; table and another table storing orders of those products. We want to ensure that the &lt;code&gt;orders&lt;/code&gt; table only contains orders of products that actually exist. So, we define a foreign key constraint in the orders table that references the products table:&lt;/p&gt;

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

postgres=# CREATE TABLE products (
    product_number bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    product_name text,
    price numeric
);
CREATE TABLE


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

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

postgres=# CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_number integer REFERENCES products (product_number), 
    quantity integer 
);
CREATE TABLE


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

&lt;/div&gt;

&lt;p&gt;Everything seems to work. Let us populate the tables&lt;/p&gt;

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

postgres=# INSERT INTO products (product_name, price) VALUES ('book', 10), ('pen', 5), ('bag', 15);
INSERT 0 3


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

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

postgres=# INSERT INTO orders (order_id, product_number, quantity) VALUES (1, 1, 1), (2, 2, 1), (3, 3, 1);
INSERT 0 3


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

&lt;/div&gt;

&lt;p&gt;Also, everything works. But what happens when &lt;code&gt;product_number&lt;/code&gt; is greater than &lt;code&gt;2147483648&lt;/code&gt; (max value of integer).&lt;/p&gt;

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

postgres=# SELECT setval('products_product_number_seq', 2147483648);
   setval
------------
 2147483648
(1 row)

postgres=# INSERT INTO products (product_name, price) VALUES ('shoe', 20) RETURNING product_number;
 product_number
----------------
     2147483649
(1 row)

postgres=# INSERT into orders (order_id, product_number, quantity) VALUES (4, 2147483649, 1);
ERROR:  integer out of range


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

&lt;/div&gt;

&lt;p&gt;We got &lt;code&gt;integer out of range&lt;/code&gt; error. One way to fix this is to change &lt;code&gt;orders.product_number&lt;/code&gt; type to &lt;code&gt;bigint&lt;/code&gt; but we risk full table rewrite and application downtime for the duration of the change. Let us find out:&lt;/p&gt;

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

postgres=# SELECT relname, relfilenode FROM pg_class WHERE relname = 'orders';
 relname | relfilenode
---------+-------------
 orders  |       42404
(1 row)


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

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

postgres=# ALTER TABLE orders ALTER COLUMN product_number TYPE bigint ;
ALTER TABLE
postgres=# SELECT relname, relfilenode FROM pg_class WHERE relname = 'orders';
 relname | relfilenode
---------+-------------
 orders  |       42419


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

&lt;/div&gt;

&lt;p&gt;We can see &lt;code&gt;relfilenode&lt;/code&gt; has changed, this tells us the table was rewritten. Imagine having to do this in a heavily-loaded large database.&lt;br&gt;
There are other ways of adjusting the type such as using triggers and mirrored columns but that can be tasking.&lt;/p&gt;

&lt;p&gt;Most common source of type mismatch in referential integrity happens when using types that have size/length constraint that  such as &lt;code&gt;smallint&lt;/code&gt;, &lt;code&gt;integer&lt;/code&gt; &lt;code&gt;serial&lt;/code&gt;, &lt;code&gt;character varying(N)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, is there a way we can catch this kind of mismatch earlier, maybe during development/review?&lt;br&gt;
We can use the query below which produces nice info/message about type mismatch in referential integrity&lt;/p&gt;

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

WITH pk AS (
    SELECT CASE nc.nspname 
               WHEN 'public' THEN nc.nspname || '.' || c.conrelid::regclass::text
               ELSE c.conrelid::regclass::text
           END AS conrelid,
           c.conindid::regclass,
           string_agg(a.attname, ', ' order by a.attnum) as attnames,
           string_agg(format_type(a.atttypid, a.atttypmod), ', ' order by a.attnum) AS atttypid
      FROM pg_namespace AS nc
      JOIN pg_class AS r ON nc.oid = r.relnamespace
      JOIN pg_attribute AS a ON r.oid = a.attrelid
      JOIN pg_constraint AS c ON r.oid = c.conrelid
     WHERE a.attnum = ANY (c.conkey)
       AND NOT a.attisdropped 
       AND c.contype = 'p' 
       AND r.relkind IN ('r', 'p')
       AND nc.nspname NOT IN ('information_schema', 'pg_catalog')
  GROUP BY nc.nspname, c.conrelid::regclass::text, c.conindid::regclass
), fk AS (
    SELECT CASE nc.nspname 
               WHEN 'public' THEN nc.nspname || '.' || c.conrelid::regclass::text
               ELSE c.conrelid::regclass::text
           END AS conrelid,
           c.conindid::regclass,
           string_agg(a.attname, ', ' order by a.attnum) as attnames,
           string_agg(format_type(a.atttypid, a.atttypmod), ', ' order by a.attnum) AS atttypid
      FROM pg_namespace AS nc
      JOIN pg_class AS r ON nc.oid = r.relnamespace
      JOIN pg_attribute AS a ON r.oid = a.attrelid
      JOIN pg_constraint AS c ON r.oid = c.conrelid
      JOIN pk ON pk.conindid = c.conindid
     WHERE a.attnum = ANY (c.conkey)
       AND NOT a.attisdropped 
       AND c.contype = 'f' 
       AND r.relkind IN ('r', 'p') 
       AND nc.nspname NOT IN ('information_schema', 'pg_catalog')
  GROUP BY nc.nspname, c.conrelid::regclass::text, c.conindid::regclass
)
SELECT 'Parent ' || pk.conrelid || '.{' || pk.attnames || '} datatype {' || pk.atttypid || '} is different from child ' || fk.conrelid || '.{' || fk.attnames || '} datatype {' || fk.atttypid  || '}' AS info
  FROM pk 
  JOIN fk ON pk.conindid = fk.conindid
 WHERE pk.atttypid != fk.atttypid;
                                                                                       info
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Parent public.products.{product_number} datatype {bigint} is different from child public.orders.{product_number} datatype {integer}


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

&lt;/div&gt;

&lt;p&gt;The query above can also be incorporated into some continuous integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;It is easy to miss out the right data types when creating referential integrity and this can have impactful implications during the lifecycle of an application. Few of the implications are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application errors due to size/length constraint such as in &lt;code&gt;integer&lt;/code&gt; and &lt;code&gt;character varying(N)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Application downtime when setting the right data type.&lt;/li&gt;
&lt;li&gt;If there is a rewrite, the rewrite will temporarily require as much as double the disk space of the table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key takeaways
&lt;/h3&gt;

&lt;p&gt;Review the data types in referential integrity as part of your database design review.&lt;/p&gt;

</description>
      <category>database</category>
      <category>design</category>
      <category>postgres</category>
      <category>datatypes</category>
    </item>
    <item>
      <title>Progress Reporting In PostgreSQL</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Mon, 10 Apr 2023 08:57:18 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/progress-reporting-in-postgresql-1i0d</link>
      <guid>https://dev.to/bolajiwahab/progress-reporting-in-postgresql-1i0d</guid>
      <description>&lt;p&gt;Can we monitor the progress of certain operations in PostgreSQL? Yes, we can monitor the progress of these operations in PostgreSQL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create index&lt;/li&gt;
&lt;li&gt;create index concurrently&lt;/li&gt;
&lt;li&gt;reindex&lt;/li&gt;
&lt;li&gt;reindex concurrently&lt;/li&gt;
&lt;li&gt;vacuum &lt;/li&gt;
&lt;li&gt;vacuum analyze &lt;/li&gt;
&lt;li&gt;vacuum full&lt;/li&gt;
&lt;li&gt;analyze&lt;/li&gt;
&lt;li&gt;copy &lt;/li&gt;
&lt;li&gt;cluster&lt;/li&gt;
&lt;li&gt;basebackup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let us jump right into it. The query snippets below can be used to monitor the progress of the listed-above operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring the progress of &lt;em&gt;&lt;code&gt;create index&lt;/code&gt;&lt;/em&gt;, &lt;em&gt;&lt;code&gt;create index concurrently&lt;/code&gt;&lt;/em&gt;, &lt;em&gt;&lt;code&gt;reindex&lt;/code&gt;&lt;/em&gt;, &lt;em&gt;&lt;code&gt;reindex concurrently&lt;/code&gt;&lt;/em&gt;
&lt;/h2&gt;


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

&lt;p&gt;SELECT p.datname                                                 AS database_name,&lt;br&gt;
       p.pid,&lt;br&gt;
       clock_timestamp() - a.xact_start                          AS duration_so_far,&lt;br&gt;
       a.application_name,&lt;br&gt;
       a.client_addr,&lt;br&gt;
       a.usename,&lt;br&gt;
       coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false') AS waiting,&lt;br&gt;
       p.command,&lt;br&gt;
       trim(trailing ';' from a.query)                           AS query,&lt;br&gt;
       a.state,&lt;br&gt;
       p.index_relid::regclass                                   AS index_name,&lt;br&gt;
       p.relid::regclass                                         AS table_name,&lt;br&gt;
       pg_size_pretty(pg_relation_size(p.relid))                 AS table_size,&lt;br&gt;
       p.phase,&lt;br&gt;
       CASE p.phase&lt;br&gt;
           WHEN 'initializing' THEN '1 of 12'&lt;br&gt;
           WHEN 'waiting for writers before build' THEN '2 of 12'&lt;br&gt;
           WHEN 'building index: scanning table' THEN '3 of 12'&lt;br&gt;
           WHEN 'building index: sorting live tuples' THEN '4 of 12'&lt;br&gt;
           WHEN 'building index: loading tuples in tree' THEN '5 of 12'&lt;br&gt;
           WHEN 'waiting for writers before validation' THEN '6 of 12'&lt;br&gt;
           WHEN 'index validation: scanning index' THEN '7 of 12'&lt;br&gt;
           WHEN 'index validation: sorting tuples' THEN '8 of 12'&lt;br&gt;
           WHEN 'index validation: scanning table' THEN '9 of 12'&lt;br&gt;
           WHEN 'waiting for old snapshots' THEN '10 of 12'&lt;br&gt;
           WHEN 'waiting for readers before marking dead' THEN '11 of 12'&lt;br&gt;
           WHEN 'waiting for readers before dropping' THEN '12 of 12'&lt;br&gt;
       END AS phase_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.blocks_done / nullif(p.blocks_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.blocks_done::text,&lt;br&gt;
           p.blocks_total::text&lt;br&gt;
       ) AS scan_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.tuples_done / nullif(p.tuples_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.tuples_done::text,&lt;br&gt;
           p.tuples_total::text&lt;br&gt;
       ) AS tuples_loading_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce((100 * p.lockers_done / nullif(p.lockers_total, 0))::text || '%', 'not applicable'),&lt;br&gt;
           p.lockers_done::text,&lt;br&gt;
           p.lockers_total::text&lt;br&gt;
       ) AS lockers_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce((100 * p.partitions_done / nullif(p.partitions_total, 0))::text || '%', 'not applicable'),&lt;br&gt;
           p.partitions_done::text,&lt;br&gt;
           p.partitions_total::text&lt;br&gt;
       ) AS partitions_progress,&lt;br&gt;
       p.current_locker_pid,&lt;br&gt;
       trim(trailing ';' from l.query) AS current_locker_query&lt;br&gt;
  FROM pg_stat_progress_create_index   AS p&lt;br&gt;
  JOIN pg_stat_activity                AS a ON a.pid = p.pid&lt;br&gt;
  LEFT JOIN pg_stat_activity           AS l ON l.pid = p.current_locker_pid&lt;br&gt;
 ORDER BY clock_timestamp() - a.xact_start DESC;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Monitoring the progress of &lt;em&gt;&lt;code&gt;vacuum&lt;/code&gt;&lt;/em&gt;, &lt;em&gt;&lt;code&gt;vacuum analyze&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/h2&gt;
&lt;br&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;SELECT p.datname                                                 AS database_name,&lt;br&gt;
       p.pid,&lt;br&gt;
       clock_timestamp() - a.xact_start                          AS duration_so_far,&lt;br&gt;
       a.application_name,&lt;br&gt;
       a.client_addr,&lt;br&gt;
       a.usename,&lt;br&gt;
       coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false') AS waiting,&lt;br&gt;
       trim(trailing ';' from a.query)                           AS query,&lt;br&gt;
       a.state,&lt;br&gt;
       p.relid::regclass                                         AS table_name,&lt;br&gt;
       pg_size_pretty(pg_relation_size(p.relid))                 AS table_size,&lt;br&gt;
       pg_size_pretty(pg_total_relation_size(p.relid))           AS total_table_size,&lt;br&gt;
       CASE&lt;br&gt;
           WHEN ltrim(a.query) ~* '^autovacuum.&lt;em&gt;to prevent wraparound' THEN 'wraparound'&lt;br&gt;
           WHEN ltrim(a.query) ~&lt;/em&gt; '^vacuum' THEN 'user'&lt;br&gt;
           ELSE 'regular'&lt;br&gt;
       END AS mode,&lt;br&gt;
       p.phase,&lt;br&gt;
       CASE p.phase&lt;br&gt;&lt;br&gt;
           WHEN 'initializing' THEN '1 of 7'&lt;br&gt;
           WHEN 'scanning heap' THEN '2 of 7'&lt;br&gt;
           WHEN 'vacuuming indexes' THEN '3 of 7'&lt;br&gt;
           WHEN 'vacuuming heap' THEN '4 of 7'&lt;br&gt;
           WHEN 'cleaning up indexes' THEN '5 of 7'&lt;br&gt;
           WHEN 'truncating heap' THEN '6 of 7'&lt;br&gt;
           WHEN 'performing final cleanup' THEN '7 of 7'&lt;br&gt;
       END AS vacuum_phase_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.heap_blks_scanned / nullif(p.heap_blks_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.heap_blks_scanned::text,&lt;br&gt;
           p.heap_blks_total::text&lt;br&gt;
       ) AS vacuum_scan_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.heap_blks_vacuumed / nullif(p.heap_blks_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.heap_blks_vacuumed::text,&lt;br&gt;
           p.heap_blks_total::text&lt;br&gt;
       ) AS vacuum_progress,&lt;br&gt;
       p.index_vacuum_count,&lt;br&gt;
       p.max_dead_tuples,&lt;br&gt;
       p.num_dead_tuples&lt;br&gt;
  FROM pg_stat_progress_vacuum  AS p&lt;br&gt;
  JOIN pg_stat_activity         AS a ON a.pid = p.pid&lt;br&gt;
 ORDER BY clock_timestamp() - a.xact_start DESC;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Monitoring the progress of &lt;em&gt;&lt;code&gt;vacuum full&lt;/code&gt;&lt;/em&gt;, &lt;em&gt;&lt;code&gt;cluster&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/h2&gt;
&lt;br&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;SELECT p.datname                                                 AS database_name,&lt;br&gt;
       p.pid,&lt;br&gt;
       clock_timestamp() - a.xact_start                          AS duration_so_far,&lt;br&gt;
       a.application_name,&lt;br&gt;
       a.client_addr,&lt;br&gt;
       a.usename,&lt;br&gt;
       coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false') AS waiting,&lt;br&gt;
       p.command,&lt;br&gt;
       trim(trailing ';' from a.query)                           AS query,&lt;br&gt;
       a.state,&lt;br&gt;
       p.relid::regclass                                         AS table_name,&lt;br&gt;
       p.phase,&lt;br&gt;
       CASE p.phase&lt;br&gt;&lt;br&gt;
           WHEN 'initializing' THEN '1 of 8'&lt;br&gt;
           WHEN 'seq scanning heap' THEN '2 of 8'&lt;br&gt;
           WHEN 'index scanning heap' THEN '3 of 8'&lt;br&gt;
           WHEN 'sorting tuples' THEN '4 of 8'&lt;br&gt;
           WHEN 'writing new heap' THEN '5 of 8'&lt;br&gt;
           WHEN 'swapping relation files' THEN '6 of 8'&lt;br&gt;
           WHEN 'rebuilding index' THEN '7 of 8'&lt;br&gt;
           WHEN 'performing final cleanup' THEN '7 of 8'&lt;br&gt;
       END AS vacuum_phase_progress,&lt;br&gt;
       cluster_index_relid::regclass AS cluster_index,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.heap_blks_scanned / nullif(p.heap_blks_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.heap_blks_scanned::text,&lt;br&gt;
           p.heap_blks_total::text&lt;br&gt;
       ) AS heap_scan_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.heap_tuples_written / nullif(p.heap_tuples_scanned, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.heap_tuples_written::text,&lt;br&gt;
           p.heap_tuples_scanned::text&lt;br&gt;
       ) AS heap_tuples_written_progress,&lt;br&gt;
       p.index_rebuild_count&lt;br&gt;
  FROM pg_stat_progress_cluster AS p&lt;br&gt;
  JOIN pg_stat_activity         AS a ON a.pid = p.pid&lt;br&gt;
 ORDER BY clock_timestamp() - a.xact_start DESC;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Monitoring the progress of &lt;em&gt;&lt;code&gt;analyze&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/h2&gt;
&lt;br&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;SELECT p.datname                                                 AS database_name,&lt;br&gt;
       p.pid,&lt;br&gt;
       clock_timestamp() - a.xact_start                          AS duration_so_far,&lt;br&gt;
       a.application_name,&lt;br&gt;
       a.client_addr,&lt;br&gt;
       a.usename,&lt;br&gt;
       coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false') AS waiting,&lt;br&gt;
       trim(trailing ';' from a.query)                           AS query,&lt;br&gt;
       a.state,&lt;br&gt;
       p.relid::regclass                                         AS table_name,&lt;br&gt;
       pg_size_pretty(pg_relation_size(p.relid))                 AS table_size,&lt;br&gt;
       pg_size_pretty(pg_total_relation_size(p.relid))             AS total_table_size,&lt;br&gt;
       CASE&lt;br&gt;
           WHEN ltrim(a.query) ~* '^analyze' THEN 'user'&lt;br&gt;
           ELSE 'regular'&lt;br&gt;
       END AS mode,&lt;br&gt;
       p.phase,&lt;br&gt;
       CASE p.phase&lt;br&gt;&lt;br&gt;
           WHEN 'initializing' THEN '1 of 6'&lt;br&gt;
           WHEN 'acquiring sample rows' THEN '2 of 6'&lt;br&gt;
           WHEN 'acquiring inherited sample rows' THEN '3 of 6'&lt;br&gt;
           WHEN 'computing statistics' THEN '4 of 6'&lt;br&gt;
           WHEN 'computing extended statistics' THEN '5 of 6'&lt;br&gt;
           WHEN 'finalizing analyze' THEN '6 of 6'&lt;br&gt;
       END AS phase_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.sample_blks_scanned / nullif(p.sample_blks_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.sample_blks_scanned::text,&lt;br&gt;
           p.sample_blks_total::text&lt;br&gt;
       ) AS scan_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce((100 * p.ext_stats_computed / nullif(p.ext_stats_total, 0))::text || '%', 'not applicable'),&lt;br&gt;
           p.ext_stats_computed::text,&lt;br&gt;
           p.ext_stats_total::text&lt;br&gt;
       ) AS extended_statistics_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce((100 * p.ext_stats_computed / nullif(p.child_tables_total, 0))::text || '%', 'not applicable'),&lt;br&gt;
           p.child_tables_done::text,&lt;br&gt;
           p.child_tables_total::text&lt;br&gt;
       ) AS child_tables_progress,&lt;br&gt;
       current_child_table_relid::regclass AS current_child_table&lt;br&gt;
  FROM pg_stat_progress_analyze AS p&lt;br&gt;
  JOIN pg_stat_activity         AS a ON a.pid = p.pid&lt;br&gt;
 ORDER BY clock_timestamp() - a.xact_start DESC;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Monitoring the progress of &lt;em&gt;&lt;code&gt;copy&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/h2&gt;
&lt;br&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;SELECT p.datname                                                      AS database_name,&lt;br&gt;
       p.pid,&lt;br&gt;
       clock_timestamp() - a.xact_start                               AS duration_so_far,&lt;br&gt;
       a.application_name,&lt;br&gt;
       a.client_addr,&lt;br&gt;
       a.usename,&lt;br&gt;
       coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false')      AS waiting,&lt;br&gt;
       p.command,&lt;br&gt;
       p.type,&lt;br&gt;
       trim(trailing ';' from a.query)                                AS query,&lt;br&gt;
       a.state,&lt;br&gt;
       p.relid::regclass                                              AS table_name,&lt;br&gt;
       coalesce(pg_size_pretty(pg_relation_size(p.relid)), '-')       AS table_size,&lt;br&gt;
       coalesce(pg_size_pretty(pg_total_relation_size(p.relid)), '-') AS total_table_size,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.bytes_processed / nullif(p.bytes_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.bytes_processed::text,&lt;br&gt;
           p.bytes_total::text&lt;br&gt;
       ) AS bytes_progress,&lt;br&gt;
       p.tuples_processed,&lt;br&gt;
       p.tuples_excluded&lt;br&gt;
  FROM pg_stat_progress_copy  AS p&lt;br&gt;
  JOIN pg_stat_activity       AS a ON a.pid = p.pid&lt;br&gt;
 ORDER BY clock_timestamp() - a.xact_start DESC;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Monitoring the progress of &lt;em&gt;&lt;code&gt;basebackup&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/h2&gt;
&lt;br&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;SELECT a.datname                                                      AS database_name,&lt;br&gt;
       p.pid,&lt;br&gt;
       clock_timestamp() - a.query_start                              AS duration_so_far,&lt;br&gt;
       a.application_name,&lt;br&gt;
       a.client_addr,&lt;br&gt;
       a.usename,&lt;br&gt;
       coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false')      AS waiting,&lt;br&gt;
       trim(trailing ';' from a.query)                                AS query,&lt;br&gt;
       a.state,&lt;br&gt;
       p.phase,&lt;br&gt;
       CASE p.phase&lt;br&gt;&lt;br&gt;
           WHEN 'initializing' THEN '1 of 6'&lt;br&gt;
           WHEN 'waiting for checkpoint to finish' THEN '2 of 6'&lt;br&gt;
           WHEN 'estimating backup size' THEN '3 of 6'&lt;br&gt;
           WHEN 'streaming database files' THEN '4 of 6'&lt;br&gt;
           WHEN 'waiting for wal archiving to finish' THEN '5 of 6'&lt;br&gt;
           WHEN 'transferring wal files' THEN '6 of 6'&lt;br&gt;
       END AS phase_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce(round(100.0 * p.backup_streamed / nullif(p.backup_total, 0), 2)::text || '%', 'not applicable'),&lt;br&gt;
           p.backup_streamed::text,&lt;br&gt;
           coalesce(p.backup_total::text, '0')&lt;br&gt;
       ) AS backup_progress,&lt;br&gt;
       format(&lt;br&gt;
           '%s (%s of %s)',&lt;br&gt;
           coalesce((100 * p.tablespaces_streamed / nullif(p.tablespaces_total, 0))::text || '%', 'not applicable'),&lt;br&gt;
           p.tablespaces_streamed::text,&lt;br&gt;
           p.tablespaces_total::text&lt;br&gt;
       ) AS tablespace_progress&lt;br&gt;
  FROM pg_stat_progress_basebackup  AS p&lt;br&gt;
  JOIN pg_stat_activity             AS a ON a.pid = p.pid&lt;br&gt;
 ORDER BY clock_timestamp() - a.query_start DESC;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Little explanation:&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;The above queries have lot of things in common.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is a phase column which tells what phase the process is currently on.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;phase_progress&lt;/code&gt; which gives the ratio of the current phase number and the total expected number of phases, which is gotten from the description of the respective views.&lt;/li&gt;
&lt;li&gt;Then we have progress for the phases provided they are available otherwise &lt;code&gt;not applicable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For system/user commands such as analyze and vacuum, we have this expression &lt;code&gt;trim(leading from a.query)&lt;/code&gt; which removes leading spaces before we compare to deduce whether it is &lt;code&gt;autovacuum/autoanalyze&lt;/code&gt; or &lt;code&gt;manual vacuum/manual analyze&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The combination of &lt;code&gt;coalesce&lt;/code&gt; and &lt;code&gt;nullif&lt;/code&gt; produces &lt;code&gt;not applicable&lt;/code&gt; when one of the variables is &lt;code&gt;NULL&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Expression &lt;code&gt;coalesce(a.wait_event_type ||'.'|| a.wait_event, 'false')&lt;/code&gt; gives details whether the process is waiting or not.&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;trim(trailing ';' from a.query)&lt;/code&gt; removes &lt;code&gt;;&lt;/code&gt; from the query text to give a nice format. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you find these queries useful for your progress reporting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S:&lt;/strong&gt; All queries were tested on PostgreSQL 14.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>monitoring</category>
      <category>reporting</category>
      <category>dba</category>
    </item>
    <item>
      <title>Cleanly modifying PostgreSQL configurations</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Sat, 23 Jul 2022 08:43:00 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/cleanly-modifying-postgresql-configurations-42d3</link>
      <guid>https://dev.to/bolajiwahab/cleanly-modifying-postgresql-configurations-42d3</guid>
      <description>&lt;p&gt;PostgreSQL has numerous configurations. Making changes to these configurations could occasionally result in issues or even stop the server from starting.&lt;br&gt;
Additionally, a config might have been modified but yet to be applied. This may easily result in a scenario in which we modify a config, reload or restart the server, and then encounter a problem unrelated to our modification.&lt;/p&gt;

&lt;p&gt;But the good news is that PostgreSQL provided an infrastructure to check if a config has an error so that it may be fixed before we reload or restart the server.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ways of adjusting PostgreSQL configs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Editing the config files&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;ALTER SYSTEM&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Checking for errors in the config
&lt;/h4&gt;

&lt;p&gt;The SQL query below can be used to check for configuration errors&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; SELECT name, sourcefile, sourceline, setting, error FROM pg_catalog.pg_file_settings WHERE error IS NOT NULL;
 name |                    sourcefile                     | sourceline | setting |                error
------+---------------------------------------------------+------------+---------+--------------------------------------
 test | /usr/local/etc/postgresql/14/main/postgresql.conf |          2 | test    | unrecognized configuration parameter
(1 row)

Time: 2.354 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us try reloading the server. We get this info in the LOG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2022-07-22 16:00:53.190 CEST [4317] LOG:  received SIGHUP, reloading configuration files
2022-07-22 16:00:53.190 CEST [4317] LOG:  unrecognized configuration parameter "test" in file "/usr/local/etc/postgresql/14/main/postgresql.conf" line 2
2022-07-22 16:00:53.191 CEST [4317] LOG:  configuration file "/usr/local/etc/postgresql/14/main/postgresql.conf" contains errors; no changes were applied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us try restarting the server. We got the below error and the server could not be started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2022-07-22 14:03:45.281 GMT [4940] LOG:  unrecognized configuration parameter "test" in file "/usr/local/etc/postgresql/14/main/postgresql.conf" line 2
2022-07-22 14:03:45.282 GMT [4940] FATAL:  configuration file "/usr/local/etc/postgresql/14/main/postgresql.conf" contains errors
pg_ctl: could not start server
Examine the log output.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issues can be quickly fixed because we can check the state of the configuration before reloading or restarting.&lt;/p&gt;

&lt;p&gt;To avoid surprises, this can also be implemented into a configuration management system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking for pending reload and pending restart
&lt;/h3&gt;

&lt;p&gt;It is wise to confirm the current state of the server before making any configuration changes. This guarantees that we begin in a clean condition and helps prevent cascade problems.&lt;/p&gt;

&lt;p&gt;Consider a scenario in which one of the DBAs on your team changed &lt;code&gt;random page cost&lt;/code&gt; but the change is not yet effective. You make your modification and reload or restart the server. After a few days, your queries began to perform poorly. You were questioned about the adjustment you made and even requested to reverse it, but there was still no relief.&lt;br&gt;
Investigating such issues can be tiresome and time-consuming; it is better to be safe than sorry.&lt;/p&gt;

&lt;p&gt;We can easily check for pending reload and restart with this SQL 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;current_settings&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;name&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kB'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MB'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1%1$s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'8kB'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'B'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ms'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'min'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%1$s %2$s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt;
             &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt;
           &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_settings&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_settings&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;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seqno&lt;/span&gt; &lt;span class="k"&gt;DESC&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;rn&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="c1"&gt;-- When autovacuum_work_mem is greater than -1 but less than the min_val of maintenance_work_mem which is 1024 kB,&lt;/span&gt;
        &lt;span class="c1"&gt;-- PostgreSQL sets autovacuum_work_mem to 1024 kB&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'autovacuum_work_mem'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;*&lt;/span&gt;
                    &lt;span class="k"&gt;CASE&lt;/span&gt;
                      &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*[a-zA-Z]?(B)'&lt;/span&gt;
                        &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                      &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1%1$s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="k"&gt;END&lt;/span&gt;
                  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1024 kB'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'1024 kB'&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kB'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MB'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;*&lt;/span&gt;
                        &lt;span class="k"&gt;CASE&lt;/span&gt;
                          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*[a-zA-Z]?(B)'&lt;/span&gt;
                            &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                          &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1%1$s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="k"&gt;END&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'8kB'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;*&lt;/span&gt;
                        &lt;span class="k"&gt;CASE&lt;/span&gt;
                          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*[a-zA-Z]?(B)'&lt;/span&gt;
                            &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                          &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1%1$s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="k"&gt;END&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'B'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;*&lt;/span&gt;
                        &lt;span class="k"&gt;CASE&lt;/span&gt;
                          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*[a-zA-Z]?(B)'&lt;/span&gt;
                            &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                          &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1%1$s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="k"&gt;END&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ms'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s1"&gt;'%1$s %2$s'&lt;/span&gt;
                      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                    &lt;span class="n"&gt;EPOCH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                        &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                            &lt;span class="s1"&gt;'%1$s%2$s'&lt;/span&gt;
                                          &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;
                                          &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
                                              &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*([a-zA-Z]+)'&lt;/span&gt;
                                                &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
                                              &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;
                                            &lt;span class="k"&gt;END&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;interval&lt;/span&gt;
                                    &lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s1"&gt;'%1$s %2$s'&lt;/span&gt;
                      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                    &lt;span class="n"&gt;EPOCH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                        &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                            &lt;span class="s1"&gt;'%1$s%2$s'&lt;/span&gt;
                                          &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;
                                          &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
                                              &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*([a-zA-Z]+)'&lt;/span&gt;
                                                &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
                                              &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;
                                            &lt;span class="k"&gt;END&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;interval&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;AS&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'min'&lt;/span&gt;
              &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s1"&gt;'%1$s %2$s'&lt;/span&gt;
                      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                    &lt;span class="n"&gt;EPOCH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                        &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                            &lt;span class="s1"&gt;'%1$s%2$s'&lt;/span&gt;
                                          &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;
                                          &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
                                              &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s1"&gt;*([a-zA-Z]+)'&lt;/span&gt;
                                                &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
                                              &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;
                                            &lt;span class="k"&gt;END&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;interval&lt;/span&gt;
                                    &lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'-1'&lt;/span&gt;
               &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;
             &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;
           &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;file_setting&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourcefile&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourceline&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;current_settings&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;
     &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_file_settings&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;
        &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cs&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;cs&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="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_setting&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_setting&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pending_setting&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&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="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourcefile&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourceline&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
         &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_setting&lt;/span&gt;
          &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sighup'&lt;/span&gt;
           &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
         &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;
       &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pending_reload&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt;
         &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_setting&lt;/span&gt;
          &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'postmaster'&lt;/span&gt;
           &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
         &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;
       &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pending_restart&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;current_settings&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;
 &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;file_settings&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
 &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
   &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_setting&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_setting&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;pending_reload&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending_restart&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;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        name         | current_setting | pending_setting |  context   |                         sourcefile                         | sourceline | error  | pending_reload | pending_restart
---------------------+-----------------+-----------------+------------+------------------------------------------------------------+------------+--------+----------------+-----------------
 archive_command     | (disabled)      | /bin/true       | sighup     | /usr/local/var/lib/postgresql/14/main/postgresql.auto.conf |         28 | &amp;lt;null&amp;gt; | true           | false
 autovacuum_work_mem | 10 MB           | 1024 kB         | sighup     | /usr/local/var/lib/postgresql/14/main/postgresql.auto.conf |         20 | &amp;lt;null&amp;gt; | true           | false
 max_wal_size        | 1024 MB         | 10 GB           | sighup     | /usr/local/var/lib/postgresql/14/main/postgresql.auto.conf |         26 | &amp;lt;null&amp;gt; | true           | false
 archive_mode        | off             | on              | postmaster | /usr/local/var/lib/postgresql/14/main/postgresql.auto.conf |         27 | &amp;lt;null&amp;gt; | false            | true
 shared_buffers      | 160 MB          | 1024 MB         | postmaster | /usr/local/var/lib/postgresql/14/main/postgresql.auto.conf |         25 | &amp;lt;null&amp;gt; | false            | true
(5 rows)

Time: 8.198 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid surprises, this can also be implemented into a configuration management system.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Generating nicely parsed schema files with pg_dump_schema_parser</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Tue, 07 Jun 2022 23:35:20 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/generating-nicely-parsed-schema-files-with-pgdumpschemaparser-4mhl</link>
      <guid>https://dev.to/bolajiwahab/generating-nicely-parsed-schema-files-with-pgdumpschemaparser-4mhl</guid>
      <description>&lt;p&gt;Ever had to go through thousands of lines of a schema file generated by &lt;code&gt;pg_dump&lt;/code&gt;, it can be tedious and tasking. A little background &lt;a href="https://www.quora.com/Is-it-a-good-idea-to-write-4-thousand-lines-of-code-in-a-single-controller-file" rel="noopener noreferrer"&gt;4-thousand-lines-of-code-in-a-single-file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A proper database design/development should be version controlled with a proper schema migration in place but some times we may want a schema dump for several reasons.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pg_dump&lt;/code&gt; is a tool supplied with PostgreSQL, used in taking bakckups and schema dumps of a single database.&lt;/p&gt;

&lt;p&gt;I have had to look through 7000 lines of a schema file and it wasn't funny at all, I lost interest along the way. This prompted me to do some search for a tool I could use to split the schema dump into smaller pieces. I stumbled upon few stackoverflow questions around it, notably the following &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/18330358/postgresql-dump-each-table-into-a-different-file" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/18330358/postgresql-dump-each-table-into-a-different-file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/67890251/how-can-i-pg-dump-each-table-in-its-own-file-and-stay-consistent" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/67890251/how-can-i-pg-dump-each-table-in-its-own-file-and-stay-consistent&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/7210320/pg-dump-individual-tables-possible-to-do-in-a-loop" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/7210320/pg-dump-individual-tables-possible-to-do-in-a-loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the above questions were limited to tables, I needed a tool that targets all object types.&lt;/p&gt;

&lt;p&gt;After an exhaustive search and I couldn't find a tool to do the job properly, I decided to write one to do the job. This gave birth to &lt;a href="https://github.com/bolajiwahab/pg_schema_dump_parser" rel="noopener noreferrer"&gt;pg_schema_dump_parser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pg_schema_dump_parser&lt;/code&gt; is a tool written in python which runs &lt;code&gt;pg_dump&lt;/code&gt; in the background and parses the generated schema into the respective object name and according to the type of the object. Have a look at &lt;a href="https://github.com/bolajiwahab/pg_schema_dump_parser#:~:text=Sample%20parsed%20schema-,Running%20the%20program,-Create%20pg_schema_dump_parser.config" rel="noopener noreferrer"&gt;how to use pg_schema_dump_parser&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a schema dump which is nicely parsed into smaller pieces can greatly improve developer experience, imagine a developer having to go through a schema dump of 7000 thousands of lines just to find the definition for a particular table and another developer locating the specific file with the definition of a table of interest. The second developer would have a nice experience and find the table's definition in lesser time.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>sql</category>
      <category>schemadump</category>
      <category>schemafiles</category>
    </item>
    <item>
      <title>Tracking dropped database objects in PostgreSQL</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Wed, 04 May 2022 18:06:00 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/tracking-dropped-database-objects-in-postgresql-4mf9</link>
      <guid>https://dev.to/bolajiwahab/tracking-dropped-database-objects-in-postgresql-4mf9</guid>
      <description>&lt;p&gt;Ever been in the situation where you need to perform a point-in-time recovery to recover a dropped database object and you need to find the exact time the object was dropped?. &lt;br&gt;
Finding the exact time is not always easy, most times it is all about guesses, we have all been there.&lt;br&gt;
This is why I wrote a simple extension &lt;a href="https://github.com/bolajiwahab/pg_drop_events" rel="noopener noreferrer"&gt;pg_drop_events&lt;/a&gt; some times back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pg_drop_events&lt;/strong&gt; is a wrapper which utilises PostgreSQL's event triggers, wraps around the dropping statement and logs the info into &lt;code&gt;pg_drop_events&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;A sample data looks like below:&lt;/p&gt;

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

postgres=# SELECT pid, usename, query, xact_id, wal_position, objid, object_name, object_type, xact_time FROM pg_drop_events;
  pid  | usename   |             query              | xact_id | wal_position | objid | object_name | object_type  |             xact_time
-------+-----------+--------------------------------+---------+--------------+-------+-------------+--------------+-------------------------------
 54630 | bolaji    | DROP TABLE t.t3                |   25184 | 1/A266B090   | 51293 | t.t3        | table        | 2022-05-04 17:16:32.913969+00
 54633 | bolaji    | ALTER TABLE t.t1 DROP COLUMN a |   25185 | 1/A266BBF8   | 51287 | t.t1.a      | table column | 2022-05-04 17:16:39.033796+00
 54638 | postgres  | DROP SCHEMA t CASCADE          |   25186 | 1/A266BEC0   | 51287 | t.t1        | table        | 2022-05-04 17:16:56.094366+00
 54639 | postgres  | DROP SCHEMA t CASCADE          |   25186 | 1/A266BEC0   | 51290 | t.t2        | table        | 2022-05-04 17:16:56.094366+00



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

&lt;/div&gt;

&lt;p&gt;The info from &lt;code&gt;pg_drop_events&lt;/code&gt; can be used to perform point-in-time recovery, well that is the aim of the extension, to aid point-in-time recovery. &lt;/p&gt;

&lt;p&gt;PostgreSQL provides various runtime config to perform point-in-time recovery. The full list can be found here &lt;a href="https://www.postgresql.org/docs/14/runtime-config-wal.html#:~:text=server%20command%20line.-,20.5.5.%C2%A0Recovery%20Target,-By%20default%2C%20recovery" rel="noopener noreferrer"&gt;recovery target&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personally, I prefer using &lt;code&gt;xact_id&lt;/code&gt; to perform point-in-time recovery of a dropped database object. I will talk about the reasons in the next chapter.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>disaster</category>
      <category>recovery</category>
    </item>
    <item>
      <title>PostgreSQL WAL activities</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Mon, 31 Jan 2022 22:31:34 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/postgresql-wal-activities-bce</link>
      <guid>https://dev.to/bolajiwahab/postgresql-wal-activities-bce</guid>
      <description>&lt;p&gt;PostgreSQL is ACID-compliant, ACID meaning&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;  - Atomicity&lt;br&gt;
&lt;strong&gt;C&lt;/strong&gt;  - Consistency&lt;br&gt;
&lt;strong&gt;I&lt;/strong&gt;  - Isolation&lt;br&gt;
&lt;strong&gt;D&lt;/strong&gt;  - Durability&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;PostgreSQL has various internal implementations enforcing &lt;strong&gt;ACID&lt;/strong&gt; but our focus here is on its &lt;strong&gt;Durability&lt;/strong&gt; implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Durability
&lt;/h4&gt;

&lt;p&gt;Durability guarantees that transactions that have committed are stored permanently. Such transactions will survive any fault to any component of the database system or failure to the whole system.&lt;/p&gt;

&lt;p&gt;Every relational database management system has different ways of enforcing durability but the concept is generally the same.&lt;/p&gt;

&lt;p&gt;PostgreSQL uses WAL(Write-Ahead Log) to implement &lt;strong&gt;Durability&lt;/strong&gt;. &lt;br&gt;
Modifications are written to the WAL files before they are written to the data files. This way if the database system should crash for any reason, we are sure not to loose any committed transactions, we simply perform what we call crash recovery.&lt;/p&gt;

&lt;p&gt;Both streaming replication and archive recovery rely on WAL files.&lt;/p&gt;

&lt;p&gt;WAL files are written to &lt;code&gt;pg_xlog&lt;/code&gt; (&lt;strong&gt;&amp;lt; PG10&lt;/strong&gt;) and &lt;code&gt;pg_wal&lt;/code&gt;(&lt;strong&gt;&amp;gt;= PG10&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;There are various configuration as regards to WAL. You can checkout the documentation at &lt;a href="https://www.postgresql.org/docs/14/wal-configuration.html" rel="noopener noreferrer"&gt;WAL configation&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  WAL archival
&lt;/h4&gt;

&lt;p&gt;WAL files can be archived. This simply means copying the WAL files somewhere usually outside of the database server. This serves two main purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recovery with prior restoration of a basebackup.&lt;/li&gt;
&lt;li&gt;Replication through archive recovery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have a look at &lt;a href="https://www.postgresql.org/docs/14/runtime-config-wal.html#:~:text=20.5.3.%C2%A0-,Archiving,-archive_mode%20(enum)" rel="noopener noreferrer"&gt;Archiving&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WAL files can also be read through &lt;code&gt;pg_xlogdump&lt;/code&gt;(&lt;strong&gt;&amp;lt; PG10&lt;/strong&gt;) and &lt;code&gt;pg_waldump&lt;/code&gt;(&lt;strong&gt;&amp;gt;= PG10&lt;/strong&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Monitoring WAL activities
&lt;/h4&gt;

&lt;p&gt;PostgreSQL does not provide any catalog out of the box for monitoring WAL activities but we can monitor through snapshotting and comparison. &lt;br&gt;
Few activities we can monitor are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WAL generation rate&lt;/li&gt;
&lt;li&gt;WAL archival rate&lt;/li&gt;
&lt;li&gt;WAL archival lag&lt;/li&gt;
&lt;li&gt;WAL recovery rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can achieve the above with psql &lt;strong&gt;watch&lt;/strong&gt; meta command. We get the actual metrics after a complete cycle(here I used 60s).&lt;/p&gt;

&lt;h5&gt;
  
  
  WAL generation rate
&lt;/h5&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PG &amp;gt;= 10&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;WITH wal_data AS (&lt;br&gt;
    SELECT&lt;br&gt;
        now()::timestamp(0),&lt;br&gt;
        pg_wal_lsn_diff(&lt;br&gt;
            x,&lt;br&gt;
            current_setting($$my.wal_loc$$, true)::pg_lsn&lt;br&gt;
        ) AS wal_data_generated,&lt;br&gt;
        set_config($$my.wal_loc$$::text, x::text, false) AS wal_pos&lt;br&gt;
    FROM pg_current_wal_lsn() AS l(x)&lt;br&gt;
)&lt;br&gt;
SELECT&lt;br&gt;
    now,&lt;br&gt;
    round(wal_data_generated / y.bytes_per_wal_segment, 2) AS wal_files_generated_per_minute,&lt;br&gt;
    pg_size_pretty(wal_data_generated) AS wal_data_generated_per_minute&lt;br&gt;
FROM wal_data, pg_control_init() AS y&lt;br&gt;
WHERE wal_data_generated IS NOT NULL&lt;br&gt;
\watch 60&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; **PG &amp;lt; 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WITH wal_data AS (&lt;br&gt;
    SELECT&lt;br&gt;
        now()::timestamp(0),&lt;br&gt;
        pg_xlog_location_diff(&lt;br&gt;
            x,&lt;br&gt;
            current_setting($$my.wal_loc$$, true)::pg_lsn&lt;br&gt;
        ) AS wal_data_generated,&lt;br&gt;
        set_config($$my.wal_loc$$::text, x::text, false) AS wal_pos&lt;br&gt;
    FROM pg_current_xlog_location() AS l(x)&lt;br&gt;
)&lt;br&gt;
SELECT&lt;br&gt;
    now,&lt;br&gt;
    round(wal_data_generated / y.bytes_per_wal_segment, 2) AS wal_files_generated_per_minute,&lt;br&gt;
    pg_size_pretty(wal_data_generated) AS wal_data_generated_per_minute&lt;br&gt;
FROM wal_data, pg_control_init() AS y&lt;br&gt;
WHERE wal_data_generated IS NOT NULL&lt;br&gt;
\watch 60&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;##### WAL archival rate
&amp;gt; **PG &amp;gt;= 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WITH archival_data AS (&lt;br&gt;
    SELECT&lt;br&gt;
        now()::timestamp(0),&lt;br&gt;
        CASE&lt;br&gt;
            WHEN current_setting('archive_mode') IN ('on', 'always') THEN&lt;br&gt;
            pg_wal_lsn_diff(&lt;br&gt;
                regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn,&lt;br&gt;
                current_setting($$my.wal_loc$$, true)::pg_lsn&lt;br&gt;
            )&lt;br&gt;
            ELSE NULL&lt;br&gt;
        END AS wal_data_archived,&lt;br&gt;
        CASE&lt;br&gt;
            WHEN current_setting('archive_mode') IN ('on', 'always') THEN&lt;br&gt;
            set_config($$my.wal_loc$$::text, regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn::text, false)&lt;br&gt;
            ELSE NULL&lt;br&gt;
        END AS wal_pos&lt;br&gt;
    FROM pg_stat_archiver&lt;br&gt;
)&lt;br&gt;
SELECT&lt;br&gt;
    now,&lt;br&gt;
    round(wal_data_archived / y.bytes_per_wal_segment, 2)AS wal_files_archived_per_minute,&lt;br&gt;
    pg_size_pretty(wal_data_archived) AS wal_data_archived_per_minute&lt;br&gt;
FROM archival_data, pg_control_init() AS y&lt;br&gt;
WHERE wal_data_archived IS NOT NULL&lt;br&gt;
\watch 60&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; **PG &amp;lt; 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WITH archival_data AS (&lt;br&gt;
    SELECT&lt;br&gt;
        now()::timestamp(0),&lt;br&gt;
        CASE&lt;br&gt;
            WHEN current_setting('archive_mode') IN ('on', 'always') THEN&lt;br&gt;
            pg_xlog_location_diff(&lt;br&gt;
                regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn,&lt;br&gt;
                current_setting($$my.wal_loc$$, true)::pg_lsn&lt;br&gt;
            )&lt;br&gt;
            ELSE NULL&lt;br&gt;
        END AS wal_data_archived,&lt;br&gt;
        CASE&lt;br&gt;
            WHEN current_setting('archive_mode') IN ('on', 'always') THEN&lt;br&gt;
            set_config($$my.wal_loc$$::text, regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn::text, false)&lt;br&gt;
            ELSE NULL&lt;br&gt;
        END AS wal_pos&lt;br&gt;
    FROM pg_stat_archiver&lt;br&gt;
)&lt;br&gt;
SELECT&lt;br&gt;
    now,&lt;br&gt;
    round(wal_data_archived / y.bytes_per_wal_segment, 2)AS wal_files_archived_per_minute,&lt;br&gt;
    pg_size_pretty(wal_data_archived) AS wal_data_archived_per_minute&lt;br&gt;
FROM archival_data, pg_control_init() AS y&lt;br&gt;
WHERE wal_data_archived IS NOT NULL&lt;br&gt;
\watch 60&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;##### WAL archival lag
&amp;gt; **PG &amp;gt;= 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SELECT&lt;br&gt;
    now()::timestamp(0),&lt;br&gt;
    CASE&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND NOT pg_is_in_recovery() THEN&lt;br&gt;
        (round(pg_wal_lsn_diff(pg_current_wal_lsn(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) / y.bytes_per_wal_segment::numeric,2) - 1&lt;br&gt;
        )&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND pg_is_in_recovery() THEN&lt;br&gt;
        (round(pg_wal_lsn_diff(pg_last_wal_replay_lsn(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) / y.bytes_per_wal_segment::numeric,2) - 1&lt;br&gt;
        )&lt;br&gt;
        ELSE 0&lt;br&gt;
    END AS wal_files_ready_to_be_archived,&lt;br&gt;
    CASE&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND NOT pg_is_in_recovery() THEN&lt;br&gt;
        pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) - y.bytes_per_wal_segment&lt;br&gt;
        )&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND pg_is_in_recovery() THEN&lt;br&gt;
        pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_replay_lsn(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) - y.bytes_per_wal_segment&lt;br&gt;
        )&lt;br&gt;
        ELSE '0 MB'&lt;br&gt;
    END AS archival_lag&lt;br&gt;
FROM pg_stat_archiver, pg_control_init() AS y&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; **PG &amp;lt; 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SELECT&lt;br&gt;
    now()::timestamp(0),&lt;br&gt;
    CASE&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND NOT pg_is_in_recovery() THEN&lt;br&gt;
        (round(pg_xlog_location_diff(pg_current_xlog_location(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) / y.bytes_per_wal_segment::numeric, 2) - 1&lt;br&gt;
        )&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND pg_is_in_recovery() THEN&lt;br&gt;
        (round(pg_xlog_location_diff(pg_last_xlog_replay_location(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) / y.bytes_per_wal_segment::numeric, 2) - 1&lt;br&gt;
        )&lt;br&gt;
        ELSE 0&lt;br&gt;
    END AS wal_files_ready_to_be_archived,&lt;br&gt;
    CASE&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND NOT pg_is_in_recovery() THEN&lt;br&gt;
        pg_size_pretty(pg_xlog_location_diff(pg_current_xlog_location(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) - y.bytes_per_wal_segment&lt;br&gt;
        )&lt;br&gt;
        WHEN current_setting('archive_mode') IN ('on', 'always') AND pg_is_in_recovery() THEN&lt;br&gt;
        pg_size_pretty(pg_xlog_location_diff(pg_last_xlog_replay_location(),&lt;br&gt;
            regexp_replace(last_archived_wal, $$^\w{8}(\w{8})(0{6})(\w{2})$$, $$\1/\3\2$$)::pg_lsn) - y.bytes_per_wal_segment&lt;br&gt;
        )&lt;br&gt;
        ELSE '0 MB'&lt;br&gt;
    END AS archival_lag&lt;br&gt;
FROM pg_stat_archiver, pg_control_init() AS y&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;##### WAL recovery rate
&amp;gt; **PG &amp;gt;= 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WITH wal_data AS (&lt;br&gt;
    SELECT&lt;br&gt;
        now()::timestamp(0),&lt;br&gt;
        CASE&lt;br&gt;
            WHEN pg_is_in_recovery() THEN&lt;br&gt;
            pg_wal_lsn_diff(&lt;br&gt;
                x,&lt;br&gt;
                current_setting($$my.wal_loc$$, true)::pg_lsn&lt;br&gt;
            )&lt;br&gt;
            ELSE NULL&lt;br&gt;
        END AS wal_data_replayed,&lt;br&gt;
        set_config($$my.wal_loc$$::text, x::text, false) AS wal_pos&lt;br&gt;
    FROM pg_last_wal_replay_lsn() AS l(x)&lt;br&gt;
)&lt;br&gt;
SELECT&lt;br&gt;
    now,&lt;br&gt;
    round(wal_data_replayed / y.bytes_per_wal_segment, 2) AS wal_files_replayed_per_minute,&lt;br&gt;
    pg_size_pretty(wal_data_replayed) AS wal_data_replayed_per_minute&lt;br&gt;
FROM wal_data, pg_control_init() AS y&lt;br&gt;
WHERE wal_data_replayed IS NOT NULL&lt;br&gt;
\watch 60&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; **PG &amp;lt; 10**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WITH wal_data AS (&lt;br&gt;
    SELECT&lt;br&gt;
        now()::timestamp(0),&lt;br&gt;
        CASE&lt;br&gt;
            WHEN pg_is_in_recovery() THEN&lt;br&gt;
            pg_xlog_location_diff(&lt;br&gt;
                x,&lt;br&gt;
                current_setting($$my.wal_loc$$, true)::pg_lsn&lt;br&gt;
            )&lt;br&gt;
            ELSE NULL&lt;br&gt;
        END AS wal_data_replayed,&lt;br&gt;
        set_config($$my.wal_loc$$::text, x::text, false) AS wal_pos&lt;br&gt;
    FROM pg_last_xlog_replay_location() AS l(x)&lt;br&gt;
)&lt;br&gt;
SELECT&lt;br&gt;
    now,&lt;br&gt;
    round(wal_data_replayed / y.bytes_per_wal_segment, 2) AS wal_files_replayed_per_minute,&lt;br&gt;
    pg_size_pretty(wal_data_replayed) AS wal_data_replayed_per_minute&lt;br&gt;
FROM wal_data, pg_control_init() AS y&lt;br&gt;
WHERE wal_data_replayed IS NOT NULL&lt;br&gt;
\watch 60&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Example output:
- WAL generation rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Mon Jan 31 22:16:15 2022 (every 60s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_generated_per_minute&lt;/th&gt;
&lt;th&gt;wal_data_generated_per_minute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(0 rows)&lt;/p&gt;

&lt;p&gt;Time: 40.007 ms&lt;br&gt;
                         Mon Jan 31 22:17:15 2022 (every 60s)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_generated_per_minute&lt;/th&gt;
&lt;th&gt;wal_data_generated_per_minute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2022-01-31 21:17:16&lt;/td&gt;
&lt;td&gt;266.23&lt;/td&gt;
&lt;td&gt;4260 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(1 row)&lt;/p&gt;

&lt;p&gt;Time: 27.568 ms&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- WAL archival rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_archived_per_minute&lt;/th&gt;
&lt;th&gt;wal_data_archived_per_minute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(0 rows)&lt;/p&gt;

&lt;p&gt;Time: 62.165 ms&lt;br&gt;
                        Mon Jan 31 23:22:13 2022 (every 60s)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_archived_per_minute&lt;/th&gt;
&lt;th&gt;wal_data_archived_per_minute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2022-01-31 22:22:13&lt;/td&gt;
&lt;td&gt;213.00&lt;/td&gt;
&lt;td&gt;3408 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(1 row)&lt;/p&gt;

&lt;p&gt;Time: 44.091 ms&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- WAL archival lag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_ready_to_be_archived&lt;/th&gt;
&lt;th&gt;archival_lag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2022-01-31 21:41:19&lt;/td&gt;
&lt;td&gt;1.02&lt;/td&gt;
&lt;td&gt;16 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(1 row)&lt;/p&gt;

&lt;p&gt;Time: 35.998 ms&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- WAL recovery rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            Mon Jan 31 22:42:12 2022 (every 60s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_replayed_per_minute&lt;/th&gt;
&lt;th&gt;wal_data_replayed_per_minute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(0 rows)&lt;/p&gt;

&lt;p&gt;Time: 31.892 ms&lt;br&gt;
                        Mon Jan 31 22:43:12 2022 (every 60s)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;now&lt;/th&gt;
&lt;th&gt;wal_files_replayed_per_minute&lt;/th&gt;
&lt;th&gt;wal_data_replayed_per_minute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2022-01-31 21:43:13&lt;/td&gt;
&lt;td&gt;251.53&lt;/td&gt;
&lt;td&gt;4024 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(1 row)&lt;/p&gt;

&lt;p&gt;Time: 25.209 ms&lt;/p&gt;



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

&lt;/div&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>PostgreSQL temp files usage</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Sat, 22 Jan 2022 02:45:12 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/postgresql-temp-files-usage-1gb9</link>
      <guid>https://dev.to/bolajiwahab/postgresql-temp-files-usage-1gb9</guid>
      <description>&lt;h1&gt;
  
  
  Temporary files
&lt;/h1&gt;

&lt;p&gt;Certain query operations such as &lt;code&gt;sort&lt;/code&gt; or &lt;code&gt;hash&lt;/code&gt; table require some memory facility. This memory is provided by a runtime config &lt;code&gt;work_mem&lt;/code&gt;.&lt;br&gt;
From the official documentation &lt;a href="https://www.postgresql.org/docs/14/runtime-config-resource.html#:~:text=the%20standby%20server.-,work_mem,-(integer)" rel="noopener noreferrer"&gt;work_mem&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;work_mem (integer)&lt;/strong&gt;&lt;br&gt;
Sets the base maximum amount of memory to be used by a query operation (such as a sort or hash table) before writing to temporary disk files.&lt;br&gt;
Note that for a complex query, several sort or hash operations might be running in parallel; each operation will generally be allowed to use as much memory as this value specifies before it starts to write data into temporary files.&lt;br&gt;
&lt;strong&gt;Sort operations are used for ORDER BY, DISTINCT, and merge joins. Hash tables are used in hash joins, hash-based aggregation, result cache nodes, and hash-based processing of IN subqueries&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the set &lt;code&gt;work_mem&lt;/code&gt; is exceeded, the operation starts writing to the temporary disk files.&lt;/p&gt;

&lt;p&gt;Another operation that can write temp files is &lt;strong&gt;CREATE INDEX&lt;/strong&gt;. This is controlled by a different runtime config &lt;code&gt;maintenance_work_mem&lt;/code&gt;.&lt;br&gt;
From the official documentation &lt;a href="https://www.postgresql.org/docs/14/runtime-config-resource.html#:~:text=40MB%20or%20more.-,maintenance_work_mem,-(integer)" rel="noopener noreferrer"&gt;maintenance_work_mem&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;maintenance_work_mem (integer)&lt;/strong&gt;&lt;br&gt;
Specifies the maximum amount of memory to be used by maintenance operations, such as VACUUM, CREATE INDEX, and ALTER TABLE ADD FOREIGN KEY.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The particular location where these temporary disk files are written to is controlled by runtime config &lt;code&gt;temp_tablespaces&lt;/code&gt;&lt;br&gt;
Again from the official documentation &lt;a href="https://www.postgresql.org/docs/9.4/runtime-config-client.html#:~:text=see%20Section%2021.6.-,temp_tablespaces,-(string)" rel="noopener noreferrer"&gt;temp_tablespaces&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;temp_tablespaces (string)&lt;/strong&gt;&lt;br&gt;
This variable specifies tablespaces in which to create temporary objects (temp tables and indexes on temp tables) when a CREATE command does not explicitly specify a tablespace. &lt;strong&gt;Temporary files for purposes such as sorting large data sets are also created in these tablespaces&lt;/strong&gt;.&lt;br&gt;
The value is a list of names of tablespaces. &lt;strong&gt;When there is more than one name in the list, PostgreSQL chooses a random member of the list&lt;/strong&gt; each time a temporary object is to be created; except that within a transaction, successively created temporary objects are placed in successive tablespaces from the list. &lt;strong&gt;If the selected element of the list is an empty string, PostgreSQL will automatically use the default tablespace of the current database instead&lt;/strong&gt;.&lt;br&gt;
When &lt;code&gt;temp_tablespaces&lt;/code&gt; is set interactively, specifying a nonexistent tablespace is an error, as is specifying a tablespace for which the user does not have CREATE privilege. However, when using a previously set value, &lt;strong&gt;nonexistent tablespaces are ignored, as are tablespaces for which the user lacks CREATE privilege&lt;/strong&gt;. In particular, this rule applies when using a value set in &lt;code&gt;postgresql.conf&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;The default value is an empty string, which results in all temporary objects being created in the default tablespace of the current database&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Default tablespace vs Specific temp tablespaces
&lt;/h2&gt;

&lt;p&gt;Default installation of PostgreSQL has default tablespace(&lt;strong&gt;pg_default&lt;/strong&gt;) set as the &lt;code&gt;temp_tablespaces&lt;/code&gt;.&lt;br&gt;
You might want to separate writes of actual data from temp files, improving disk IO. That is where &lt;code&gt;temp_tablespaces&lt;/code&gt; is most useful.&lt;br&gt;
Also, you can point your &lt;code&gt;temp_tablespaces&lt;/code&gt; to a faster disk such as nvme which is faster.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up a tablespace for temporary files
&lt;/h2&gt;

&lt;p&gt;You can use the following sample code to set up temp_tablespaces&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new tablespace:
&lt;/li&gt;
&lt;/ul&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="n"&gt;TABLESPACE&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="k"&gt;LOCATION&lt;/span&gt; &lt;span class="s1"&gt;'actual_location'&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;Set cluster-wide &lt;code&gt;temp_tablespaces&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;SYSTEM&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;temp_tablespaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'temp'&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;pg_reload_conf&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Caveats of temp files
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Temp files can use up the available disk space when a query needs to create a lot of temp files, with PostgreSQL reporting an error such as
&lt;code&gt;could not write block .... of temporary file no space left on device ...&lt;/code&gt; and the query being canceled.&lt;/li&gt;
&lt;li&gt;Temp files are only kept around for the duration of a query. Once the query finishes or cancels, the temp files are cleaned up.
Once in a while DBAs experience (1) and when they check the disk usage, they find out there is still space and start troubleshooting for where the issue might be. The issue is described in (2). When this happens, it might be high time you optimized your queries. The other workaround would be, using a larger free disk as your &lt;code&gt;temp_tablespaces&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Monitoring temp files usage
&lt;/h2&gt;

&lt;p&gt;We can log temp files with the help of a runtime config &lt;a href="https://www.postgresql.org/docs/14/runtime-config-logging.html#:~:text=change%20this%20setting.-,log_temp_files,-(integer)" rel="noopener noreferrer"&gt;log_temp_files&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;log_temp_files (integer)&lt;/code&gt;&lt;br&gt;
Controls logging of temporary file names and sizes. Temporary files can be created for sorts, hashes, and temporary query results. &lt;strong&gt;If enabled by this setting, a log entry is emitted for each temporary file when it is deleted&lt;/strong&gt;. A value of zero logs all temporary file information, while positive values log only files whose size is greater than or equal to the specified amount of data. If this value is specified without units, it is taken as kilobytes. The default setting is -1, which disables such logging. Only superusers can change this setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While &lt;code&gt;log_temp_files&lt;/code&gt; logs information about a temporary file when it is deleted, you might want to monitor the temporary file while it's being created as well, keeping an eye on things.&lt;br&gt;
Also, you might want to know which queries are making use of temporary files and maybe optimize them for the faster &lt;code&gt;work_mem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We would be doing this from SQL.&lt;br&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;tablespaces&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;spcname&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_tablespace_location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data_directory'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'/base'&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;tbl_location&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_tablespace&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tablespace_suffix&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;tbl_name&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tbl_location&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'/pgsql_tmp'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tablespaces&lt;/span&gt;
     &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pg_default'&lt;/span&gt;
     &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;tbl_location&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'/pgsql_tmp'&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tablespaces&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;LATERAL&lt;/span&gt; &lt;span class="n"&gt;pg_ls_dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbl_location&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;path&lt;/span&gt;
     &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PG_'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'server_version'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'^(?:&lt;/span&gt;&lt;span class="se"&gt;\d\.\d\d&lt;/span&gt;&lt;span class="s1"&gt;?|&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&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="n"&gt;stat&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;temp_tablespace&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_size_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tablespace_suffix&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;LATERAL&lt;/span&gt; &lt;span class="n"&gt;pg_ls_dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&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;file&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;LATERAL&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;pg_stat_file&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;
     &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
            &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_tablespace&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datname&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0 MB'&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;temp_size_written&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_tablespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'not using temp files'&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;temp_tablespace&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;application_name&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_addr&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usename&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_start&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;interval&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;AS&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state_change&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;interval&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;AS&lt;/span&gt; &lt;span class="n"&gt;duration_since_state_change&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TRAILING&lt;/span&gt; &lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&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;query&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_event_type&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_event&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_activity&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
  &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pg_backend_pid&lt;/span&gt;&lt;span class="p"&gt;()&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;temp_size_written&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;Example output:&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="n"&gt;datname&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;temp_size_written&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;temp_tablespace&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;application_name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;client_addr&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;usename&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;duration_since_state_change&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;                        &lt;span class="n"&gt;query&lt;/span&gt;                        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;state&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;             &lt;span class="n"&gt;wait&lt;/span&gt;
&lt;span class="c1"&gt;---------+------+-------------------+----------------------+------------------+-------------+---------+----------+-----------------------------+-----------------------------------------------------+--------+------------------------------&lt;/span&gt;
 &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;4943&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1911&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt;                 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;psql&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;                    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7518&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1338&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pg_default&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;psql&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;                    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;100000000&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;a&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;a&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3819&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;                      &lt;span class="o"&gt;|&lt;/span&gt;                                                     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;AutoVacuumMain&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3818&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;                      &lt;span class="o"&gt;|&lt;/span&gt;                                                     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;WalWriterMain&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3816&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;                      &lt;span class="o"&gt;|&lt;/span&gt;                                                     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;CheckpointerMain&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3821&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;                      &lt;span class="o"&gt;|&lt;/span&gt;                                                     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;LogicalLauncherMain&lt;/span&gt;
 &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5935&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;psql&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bolaji&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;                    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;show&lt;/span&gt; &lt;span class="n"&gt;work_mem&lt;/span&gt;                                       &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;idle&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ClientRead&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3817&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt;              &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;temp&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;                      &lt;span class="o"&gt;|&lt;/span&gt;                                                     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;BgWriterHibernate&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;960&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, there is a new function &lt;a href="https://www.postgresql.org/docs/12/functions-admin.html#:~:text=pg_ls_tmpdir(%5Btablespace%20oid%5D)" rel="noopener noreferrer"&gt;pg_ls_tmpdir&lt;/a&gt; available from PG12. I decided not to use this function in the above query because it is not available in lower versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATES&lt;/strong&gt;: 27-05-2025&lt;br&gt;
The above query does not work in a restricted environment (an example is &lt;strong&gt;RDS&lt;/strong&gt;) due to access to the OS files. We can use this modified query to achieve the same in such environments but this requires that you are running on at least PostgreSQL 12.&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;tablespaces&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;oid&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tbl_oid&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spcname&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_tablespace_location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data_directory'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'/base'&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;tbl_location&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_tablespace&lt;/span&gt;
      &lt;span class="c1"&gt;-- Exclude pg_global as it resides in the same directory as pg_default&lt;/span&gt;
     &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;spcname&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'pg_global'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;
         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tbl_name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;temp_tablespace&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tablespaces&lt;/span&gt;
     &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;LATERAL&lt;/span&gt; &lt;span class="n"&gt;pg_ls_tmpdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tbl_oid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
     &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
            &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tbl_name&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datname&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0 MB'&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;temp_size_written&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_tablespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'not using temp files'&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;temp_tablespace&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;application_name&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_addr&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usename&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_start&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;interval&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;AS&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock_timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state_change&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;interval&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;AS&lt;/span&gt; &lt;span class="n"&gt;duration_since_state_change&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TRAILING&lt;/span&gt; &lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&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;query&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt;
     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_event_type&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_event&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_activity&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
  &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pg_backend_pid&lt;/span&gt;&lt;span class="p"&gt;()&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;temp_size_written&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;



</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>PostgreSQL lock trees</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Thu, 13 Jan 2022 22:12:24 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/2022-01-13-postgresql-lock-trees-56e0</link>
      <guid>https://dev.to/bolajiwahab/2022-01-13-postgresql-lock-trees-56e0</guid>
      <description>&lt;p&gt;There are two forms of locks in PostgreSQL, row level and object level. While most operations would simply take row level locks, migrations usually take the more aggressive ones (object level locks).&lt;/p&gt;

&lt;p&gt;PostgreSQL keeps a queue of locks, with &lt;em&gt;first in first out&lt;/em&gt; (FIFO). While readers do not block writers, often times migration ends up blocking several other operations on the database leading to lock contention. This is due to the FIFO nature of the lock queue, there is no jumping the queue.&lt;br&gt;
Imagine a migration which needs to alter a table (requiring access exclusive lock on the table), it blocks other operations even those doing ordinary &lt;code&gt;SELECT&lt;/code&gt;. Also, the migration might have to wait to take the necessary lock if there are conflicting locks on the table leading to &lt;em&gt;lock contention&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Often times when there is a lock contention, we end up with questions such as what (human or application) is the main root of the lock contention.&lt;/p&gt;

&lt;p&gt;Here is an example of other people's work on the subject which you might find helpful:&lt;br&gt;
&lt;a href="https://postgres.ai/blog/20211018-postgresql-lock-trees" rel="noopener noreferrer"&gt;postgresql-lock-trees&lt;/a&gt; by Nikolay Samokhvalov.&lt;/p&gt;

&lt;p&gt;After experimenting with the above example and the drawback being the call on the function &lt;code&gt;pg_blocking_pids(int)&lt;/code&gt;, I started looking at how we can simply do away with calling the function.&lt;br&gt;
Then I rewrote the query, relying on lock conflicts as listed on the documentation &lt;a href="https://www.postgresql.org/docs/14/explicit-locking.html" rel="noopener noreferrer"&gt;Conflicting Lock Modes&lt;/a&gt;, &lt;code&gt;pg_locks&lt;/code&gt; and &lt;code&gt;pg_stat_activity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The query:&lt;/p&gt;

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

WITH conflicts(lock, conflict) AS (VALUES
    ('AccessShare', 'AccessExclusive'), ('RowShare', 'Exclusive'),
    ('RowShare', 'AccessExclusive'), ('RowExclusive', 'Share'),
    ('RowExclusive', 'ShareRowExclusive'), ('RowExclusive', 'Exclusive'),
    ('RowExclusive', 'AccessExclusive'), ('ShareUpdateExclusive', 'ShareUpdateExclusive'),
    ('ShareUpdateExclusive', 'Share'), ('ShareUpdateExclusive', 'ShareRowExclusive'),
    ('ShareUpdateExclusive', 'Exclusive'), ('ShareUpdateExclusive', 'AccessExclusive'),
    ('Share', 'RowExclusive'), ('Share', 'ShareUpdateExclusive'),
    ('Share', 'ShareRowExclusive'), ('Share', 'Exclusive'),
    ('Share', 'AccessExclusive'), ('ShareRowExclusive','RowExclusive'),
    ('ShareRowExclusive', 'ShareUpdateExclusive'), ('ShareRowExclusive', 'Share'),
    ('ShareRowExclusive', 'ShareRowExclusive'), ('ShareRowExclusive', 'Exclusive'),
    ('ShareRowExclusive', 'AccessExclusive'), ('Exclusive', 'RowShare'),
    ('Exclusive', 'RowExclusive'), ('Exclusive', 'ShareUpdateExclusive'),
    ('Exclusive', 'Share'), ('Exclusive', 'ShareRowExclusive'),
    ('Exclusive', 'Exclusive'), ('Exclusive', 'AccessExclusive'),
    ('AccessExclusive', 'AccessShare'), ('AccessExclusive', 'RowShare'),
    ('AccessExclusive', 'RowExclusive'), ('AccessExclusive', 'ShareUpdateExclusive'),
    ('AccessExclusive', 'Share'), ('AccessExclusive', 'ShareRowExclusive'),
    ('AccessExclusive','Exclusive'), ('AccessExclusive','AccessExclusive')
),
activity AS (
    SELECT
        datname,
        pid,
        usename,
        application_name,
        client_addr,
        xact_start,
        query_start,
        state_change,
        backend_xid,
        backend_xmin,
        state,
        wait_event_type,
        wait_event,
        query
    FROM pg_stat_activity
    WHERE pid != pg_backend_pid()
),
locks AS (
   SELECT
        pid,
        locktype,
        granted,
        relation::regclass AS relation,
        left(mode,-4) AS mode,
        ROW(locktype,database,relation::regclass,page,tuple,virtualxid,transactionid,classid,objid,objsubid) AS obj
    FROM pg_locks
),
pairs AS (
    SELECT
        w.pid AS waiter,
        l.pid AS locker,
        l.obj,
        l.mode,
        l.relation
    FROM locks AS w
    JOIN locks AS l ON l.obj IS NOT DISTINCT FROM w.obj AND l.locktype = w.locktype AND NOT l.pid = w.pid AND l.granted
    WHERE NOT w.granted
),
leaders AS (
    SELECT
        DISTINCT(locker),
        coalesce(relation, (select max(relation) from locks where pid = locker)) AS relation,
        mode
    FROM (SELECT *, rank() OVER (PARTITION BY locker ORDER BY relation NULLS FIRST) AS pos FROM pairs) AS ss
    WHERE pos = 1
),
lock_queue AS (
    SELECT
        array_agg(l.pid::text || '.' || l.mode order by p.query_start) AS queue,
        rel.relation
    FROM locks AS l
    JOIN activity p ON p.pid = l.pid
    JOIN activity a ON a.pid = l.pid,
    LATERAL (SELECT coalesce(relation, max(relation)) AS relation FROM locks WHERE pid = l.pid AND relation IS NOT NULL GROUP BY relation) AS rel
    WHERE NOT l.granted
    GROUP BY rel.relation
),
push_root_on_lock_queue AS (
    SELECT
        l.relation,
        array_prepend(p.locker::text || '.' || p.mode, queue) AS queue
    FROM lock_queue AS l
    JOIN leaders AS p ON l.relation = p.relation
),
lock_queue_element_num AS (
    SELECT
        relation,
        a.element,
        a.num
    FROM push_root_on_lock_queue, unnest(queue) WITH ORDINALITY a(element, num)
),
main AS (
    SELECT
        a.relation,
        split_part(a.element, '.', 1) AS prev_pid,
        split_part(a.element, '.', 2) AS prev_mode,
        split_part(b.element, '.', 1) AS pid,
        split_part(b.element, '.', 2) AS mode,
        a.num AS start,
        b.num AS next,
        CASE
            WHEN EXISTS (SELECT * FROM conflicts AS c WHERE c.lock = split_part(a.element, '.', 2) AND c.conflict = split_part(b.element, '.', 2)) THEN true
            ELSE false
        END AS conflict
    FROM lock_queue_element_num AS a
    JOIN lock_queue_element_num b ON a.relation = b.relation
    WHERE a.num &amp;lt; b.num
    ORDER BY b.num DESC
),
tree AS (
    SELECT
        DISTINCT ON (pid, prev_pid)
        *,
        dense_rank() OVER (PARTITION BY relation ORDER BY start) - 1 AS level
    FROM main
),
stat AS (
    SELECT
        m.pid,
        p.locker,
        array_agg(DISTINCT m.prev_pid || ':' || m.level) AS blocked_by
    FROM pairs AS p
    JOIN tree AS m ON p.waiter = m.pid::int
    WHERE conflict
    AND m.pid != m.prev_pid
    GROUP BY m.pid, p.locker
),
/* sometimes we can miss some blockers in pairs due to the lock path, the query should be fix normally but for now this is workaround
   TODO: fix pairs query to include all path
*/
sub AS (
    SELECT
        pidd,
        count(pidd) AS blocked_cnt
    FROM (
        SELECT DISTINCT ON (pid) split_part(unnest(blocked_by), ':', 1) AS pidd
        FROM stat AS s
    ) AS blocked_cnt
    WHERE NOT EXISTS (SELECT FROM pairs AS p WHERE p.locker = blocked_cnt.pidd::int)
    GROUP BY pidd
),
result AS (
    /* add details of waiters */
    SELECT
        DISTINCT(s.pid::int),
        s.blocked_by,
        (clock_timestamp() - a.xact_start)::interval(0) AS ts_age,
        (clock_timestamp() - a.state_change)::interval(0) AS change_age,
        CASE
            WHEN s.blocked_by = '{}' THEN NULL::interval(0)
            ELSE (clock_timestamp() - a.query_start)::interval(0)
        END AS blocking_age,
        a.backend_xid AS xid,
        a.backend_xmin AS xmin,
        replace(a.state, 'idle in transaction', 'idletx') AS state,
        a.datname,
        a.usename,
        a.client_addr,
        a.application_name,
        a.wait_event_type || ':' || a.wait_event AS wait,
        blocked.cnt AS blocked_cnt,
        CASE
            WHEN s.blocked_by = '{}' THEN true
            ELSE false
        END AS root,
        trim(trailing ';' from format(
            '%s %s%s',
            '[' || s.pid::text || ']',
            coalesce(repeat('.', array_length(s.blocked_by, 1)+1), '') || ' ',
            left(query, 1000)
        )) AS query
    FROM stat AS s
    JOIN activity AS a ON s.pid::int = a.pid,
    LATERAL (
        SELECT
            coalesce((CASE
                WHEN count(p.waiter) = 0 THEN (SELECT blocked_cnt FROM sub WHERE sub.pidd = s.pid UNION ALL SELECT 0 FROM sub WHERE NOT EXISTS (SELECT FROM sub WHERE sub.pidd = s.pid))
                ELSE count(p.waiter)
            END), 0) AS cnt
    FROM pairs AS p WHERE p.locker = s.pid::int)AS blocked
    UNION ALL
    /* add details of lockers */
    SELECT
        DISTINCT(p.locker::int),
        '{}'::text[] AS blocked_by,
        (clock_timestamp() - a.xact_start)::interval(0) AS ts_age,
        (clock_timestamp() - a.state_change)::interval(0) AS change_age,
        NULL::interval AS blocking_age,
        a.backend_xid AS xid,
        a.backend_xmin AS xmin,
        replace(state, 'idle in transaction', 'idletx') AS state,
        a.datname,
        a.usename,
        a.client_addr,
        a.application_name,
        a.wait_event_type || ':' || a.wait_event AS wait,
        blocked.cnt AS blocked_cnt,
        true AS root,
        trim(trailing ';' from format(
            '%s %s%s',
            '[' || p.locker::text || ']',
            coalesce(repeat('.', 0), '') || ' ',
            left(query, 1000)
        )) AS query
    FROM pairs AS p
    JOIN activity AS a ON p.locker::int = a.pid,
    LATERAL (SELECT count(t.prev_pid) AS cnt FROM tree t WHERE t.prev_pid = p.locker::text) blocked
    WHERE NOT EXISTS (SELECT FROM stat AS s WHERE s.pid::int = p.locker)
    /* every other pids not in locker and waiter pairs */
    UNION ALL
    SELECT
        DISTINCT(a.pid),
        '{}'::text[] AS blocked_by,
        (clock_timestamp() - a.xact_start)::interval(0) AS ts_age,
        (clock_timestamp() - a.state_change)::interval(0) AS change_age,
        NULL::interval AS blocking_age,
        a.backend_xid AS xid,
        a.backend_xmin AS xmin,
        replace(state, 'idle in transaction', 'idletx') AS state,
        a.datname,
        a.usename,
        a.client_addr,
        a.application_name,
        a.wait_event_type || ':' || a.wait_event AS wait,
        0::int AS blocked_cnt,
        true AS root,
        trim(trailing ';' from format(
            '%s %s%s',
            '[' || a.pid::text || ']',
            coalesce(repeat('.', 0), '') || ' ',
            left(query, 1000)
        )) AS query
    FROM activity AS a WHERE NOT EXISTS (SELECT FROM pairs AS p WHERE p.locker = a.pid OR p.waiter = a.pid)
    ORDER by blocked_cnt DESC, root, pid
)
SELECT
    pid,
    blocked_by,
    ts_age,
    change_age,
    blocking_age,
    xid,
    xmin,
    state,
    datname,
    usename,
    client_addr,
    application_name,
    wait,
    blocked_cnt,
    root,
    query
FROM result;


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

&lt;/div&gt;

&lt;p&gt;Example output:&lt;/p&gt;

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

  pid  |    blocked_by     |  ts_age  | change_age | blocking_age |  xid   |  xmin  | state  | datname | usename | client_addr | application_name |             wait             | blocked_cnt | root |                         query
-------+-------------------+----------+------------+--------------+--------+--------+--------+---------+---------+-------------+------------------+------------------------------+-------------+------+-------------------------------------------------------
 40188 | {}                | 00:10:03 | 00:10:00   | &amp;lt;null&amp;gt;       |  87324 | &amp;lt;null&amp;gt; | idletx | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Client:ClientRead            |           4 | t    | [40188]  update folder set id = id
 40224 | {40188:0}         | 00:09:57 | 00:09:56   | 00:09:56     |  87325 |  87324 | active | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Lock:transactionid           |           3 | f    | [40224] .. delete from folder
 40245 | {40188:0,40224:0} | 00:09:51 | 00:09:50   | 00:09:50     |  87326 |  87324 | active | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Lock:relation                |           2 | f    | [40245] ... alter table folder add column data1 jsonb
 40258 | {}                | 00:09:41 | 00:09:40   | &amp;lt;null&amp;gt;       |  87327 | &amp;lt;null&amp;gt; | idletx | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Client:ClientRead            |           1 | t    | [40258]  drop table sample
 40222 | {40245:2}         | 00:09:45 | 00:09:45   | 00:09:45     | &amp;lt;null&amp;gt; |  87324 | active | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Lock:relation                |           0 | f    | [40222] .. select * from folder where id = 1
 40253 | {40245:2}         | 00:09:47 | 00:09:47   | 00:09:47     | &amp;lt;null&amp;gt; |  87324 | active | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Lock:relation                |           0 | f    | [40253] .. select * from folder
 40260 | {40258:0}         | 00:09:38 | 00:09:38   | 00:09:38     | &amp;lt;null&amp;gt; |  87324 | active | bolaji  | bolaji  | &amp;lt;null&amp;gt;      | bolaji-psql      | Lock:relation                |           0 | f    | [40260] .. select * from sample
  3640 | {}                | &amp;lt;null&amp;gt;   | &amp;lt;null&amp;gt;     | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;      |                  | Activity:CheckpointerMain    |           0 | t    | [3640]
  3641 | {}                | &amp;lt;null&amp;gt;   | &amp;lt;null&amp;gt;     | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;      |                  | Activity:BgWriterHibernate   |           0 | t    | [3641]
  3642 | {}                | &amp;lt;null&amp;gt;   | &amp;lt;null&amp;gt;     | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;      |                  | Activity:WalWriterMain       |           0 | t    | [3642]
  3643 | {}                | &amp;lt;null&amp;gt;   | &amp;lt;null&amp;gt;     | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;  | &amp;lt;null&amp;gt;      |                  | Activity:AutoVacuumMain      |           0 | t    | [3643]
  3645 | {}                | &amp;lt;null&amp;gt;   | &amp;lt;null&amp;gt;     | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt; | &amp;lt;null&amp;gt;  | bolaji  | &amp;lt;null&amp;gt;      |                  | Activity:LogicalLauncherMain |           0 | t    | [3645]
(12 rows)

Time: 4.372 ms


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

&lt;/div&gt;

&lt;p&gt;A little explanation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{40188:0}&lt;/code&gt; - translates to &lt;code&gt;pid:lock_level&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;pid &lt;code&gt;40245&lt;/code&gt; blocked by &lt;code&gt;{40188:0,40224:0}&lt;/code&gt; - translates to pids &lt;code&gt;40188&lt;/code&gt; on level 0 (the root), &lt;code&gt;40224&lt;/code&gt; also level 0(the root) and &lt;code&gt;40245&lt;/code&gt; on level 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[40224] .. delete from folder&lt;/code&gt; - translates to pid &lt;code&gt;40224&lt;/code&gt; being on level 1 on the lock queue/tree behind the root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you find the above query useful for lock issue troubleshooting.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Intro</title>
      <dc:creator>Bolaji Wahab</dc:creator>
      <pubDate>Wed, 12 Jan 2022 22:59:42 +0000</pubDate>
      <link>https://dev.to/bolajiwahab/postgresql-weekly-nuggets-j3m</link>
      <guid>https://dev.to/bolajiwahab/postgresql-weekly-nuggets-j3m</guid>
      <description>&lt;p&gt;Hi there,&lt;/p&gt;

&lt;p&gt;My name is Bolaji Wahab, I have been using PostgreSQL for 4 years now and I'm loving it. I currently work as Database Engineer with focus on PostgreSQL.&lt;br&gt;
I am starting a weekly blog series on &lt;a href="https://dev.to/bolajiwahab/series/16321"&gt;dev.to&lt;/a&gt; on PostgreSQL with focus on &lt;em&gt;installation, configuration,  monitoring, performance tips, troubleshooting, debugging and various other PostgreSQL tips&lt;/em&gt;.&lt;br&gt;
I hope someone finds the blog series helpful.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
