<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[Tiger Data Blog]]></title>
        <description><![CDATA[Insights, product updates, and tips from TigerData (Creators of TimescaleDB) engineers on Postgres, time series & AI. IoT, crypto, and analytics tutorials & use cases.]]></description>
        <link>https://www.tigerdata.com/blog</link>
        <image>
            <url>https://www.tigerdata.com/icon.ico</url>
            <title>Tiger Data Blog</title>
            <link>https://www.tigerdata.com/blog</link>
        </image>
        <generator>RSS for Node</generator>
        <lastBuildDate>Tue, 07 Apr 2026 10:14:26 GMT</lastBuildDate>
        <atom:link href="https://www.tigerdata.com/blog" rel="self" type="application/rss+xml"/>
        <ttl>60</ttl>
        <item>
            <title><![CDATA[Implementing ASOF Joins in PostgreSQL and Timescale]]></title>
            <description><![CDATA[Read our step-by-step guide to implement ASOF joins in PostgreSQL and Timescale, and learn how to supercharge your queries with some Timescale magic.]]></description>
            <link>https://www.tigerdata.com/blog/implementing-asof-joins-in-timescale</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/implementing-asof-joins-in-timescale</guid>
            <category><![CDATA[PostgreSQL]]></category>
            <dc:creator><![CDATA[James Blackwood-Sewell]]></dc:creator>
            <pubDate>Thu, 15 Jun 2023 14:10:00 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2023/06/caspar-camille-rubin-fPkvU7RDmCo-unsplash--1-.jpg">
            </media:content>
            <content:encoded><![CDATA[<h2 id="what-is-an-asof-join">What Is an <code>ASOF</code> Join?</h2><p>An <code>ASOF</code> (or "as of") join is a type of join operation used when analyzing two sets of time-series data. It essentially matches each record from one table with the nearest—but not necessarily equal—value from another table based on a chosen column. Oracle supports this out of the box using a non-standard SQL syntax, but unfortunately, PostgreSQL does not provide a built-in <code>ASOF</code> keyword.</p><p>The chosen column needs to have some concept of range for the <code>ASOF</code> operation to work. You may think of it as being the "closest value," but not exceeding the comparison. It works for string (alphabetical), integer (ordinal), float (decimal), and any other data type that has an idea of ORDER. Because timestamps are near and dear to our hearts at Timescale, we will demonstrate with time and date columns.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">✨</div><div class="kg-callout-text">Want to understand <a href="https://www.timescale.com/learn/postgresql-join-type-theory">how the PostgreSQL parser picks a join method or join types</a>? Check out this article!</div></div><p><br></p><p>Performing this operation in PostgreSQL takes a bit of effort. This article aims to delve deeper into <code>ASOF</code>-style joins and how to implement similar functionality in PostgreSQL by subselecting data or other join types.</p><h2 id="understanding-asof-joins">Understanding <code>ASOF</code> Joins</h2><p><code>ASOF</code> joins are a powerful tool when dealing with time-series data. In simple terms, an ASOF join will, for each row in the left table, find a corresponding single row in the right table where the key value is less than or equal to the key in the left table.</p><p>This is a common operation when dealing with financial data, sensor readings, or <a href="https://timescale.ghost.io/blog/time-series-data/" rel="nofollow noopener noreferrer ugc">other types of time-series data where readings might not align perfectly by timestamp</a>.</p><p>For a simple example, consider the real-world question, "What was the temperature yesterday at this time?" It is very unlikely that a temperature reading was taken yesterday at exactly the millisecond that the question is asked today. What we really want is "What was the temperature taken yesterday up to today's time stamp?"</p><p>This simple example becomes a lot more complex when we start comparing temperatures day over day, week over week, etc.</p><h2 id="implementing-asof-joins-in-timescale">Implementing <code>ASOF</code> Joins in Timescale</h2><p>Even though PostgreSQL does not directly support <code>ASOF</code> joins, you can achieve similar functionality using a combination of SQL operations. Here's a simplified step-by-step guide:</p><h3 id="step-1-prepare-your-data">Step 1: Prepare your data</h3><p>Ensure your data is in the correct format for the <code>ASOF</code> join. You'll need a timestamp or other monotonically increasing column to use as a key for the join.</p><p>Suppose you have two tables, <code>bids</code> and <code>asks</code>, each containing a timestamp column, and you want to join them by instrument and the nearest timestamp.</p><pre><code class="language-sql">CREATE TABLE bids (
    instrument text,
    ts TIMESTAMPTZ,
    value NUMERIC
);
--
CREATE INDEX bids_instrument_ts_idx ON bids (instrument, ts DESC);
CREATE INDEX bids_ts_idx ON bids (ts);
--
CREATE TABLE asks (
    instrument text,
    ts TIMESTAMPTZ,
    value NUMERIC
);
CREATE INDEX asks_instrument_ts_idx ON asks (instrument, ts DESC);
CREATE INDEX asks_ts_idx ON asks (ts);
--
</code></pre><p>Normally you'd make both these tables into hypertables with the <code>create_hypertable</code> function (because you're a super educated Timescale user), but in this case, we aren't going to, as we won't be inserting much data (and we also have some Timescale magic to show off 🪄).</p><h3 id="step-2-insert-some-test-data">Step 2: Insert some test data</h3><p>Next, we'll create data for four instruments, <code>AAA, BBB, NCD,</code> and <code>USD</code>.</p><pre><code class="language-sql">INSERT INTO bids (instrument, ts, value)
SELECT 
   -- random 1 of 4 instruments
  (array['AAA', 'BBB', 'NZD', 'USD'])[floor(random() * 4 + 1)], 
   -- timestamp of last month plus some seconds
  now() - interval '1 month' + g.s, 
   -- random value
  random()* 100 +1
FROM (
  -- 2.5M seconds in a month
  SELECT ((random() * 2592000 + 1)::text || ' s')::interval s 
  FROM generate_series(1,3000000)) g;
INSERT INTO asks (instrument, ts, value)
SELECT 
   -- random 1 of 4 instruments
  (array['AAA', 'BBB', 'NZD', 'USD'])[floor(random() * 4 + 1)], 
   -- timestamp of last month plus some seconds
  now() - interval '1 month' + g.s, 
   -- random value
  random()* 100 +1
FROM (
  -- 2.5M seconds in a month
  SELECT ((random() * 2592000 + 1)::text || ' s')::interval s 
  FROM generate_series(1,2000000)) g;
</code></pre><h3 id="step-3-query-the-data-using-a-sub-select">Step 3: Query the data using a sub-select</h3><p>To mimic the behavior of an <code>ASOF</code> join, use a <code>SUBSELECT</code> join operation along with conditions to match rows based on your criteria. This will run the sub-query once per row returned from the target table. We need to use the <code>DISTINCT</code> clause to limit the number of rows returned to one.</p><p>This will work in vanilla Postgres, but when we are using Timescale (even though we aren't using hypertables yet), we get the benefits of a Skip Scan, which will supercharge the query (for more information on this check our <a href="https://docs.timescale.com/use-timescale/latest/query-data/skipscan/" rel="nofollow noopener noreferrer ugc">docs</a> or <a href="https://timescale.ghost.io/blog/how-we-made-distinct-queries-up-to-8000x-faster-on-postgresql/">blog post about how Skip Scan can give you an 8,000x speed-up</a>).</p><pre><code class="language-sql">SELECT bids.ts timebid, bids.value bid,
    (SELECT DISTINCT ON (asks.instrument) value ask
    FROM asks
    WHERE asks.instrument = bids.instrument
    AND asks.ts &lt;= bids.ts
    ORDER BY instrument, ts DESC) ask
FROM bids
WHERE bids.ts &gt; now() - interval '1 week'
</code></pre><pre><code class="language-sql">                              QUERY PLAN                                                                               
-------------------------------------------------------------------------
 Index Scan using bids_ts_idx on public.bids  
    (cost=0.43..188132.58 rows=62180 width=56) 
    (actual time=0.067..1700.957 rows=57303 loops=1)
   Output: bids.instrument, bids.ts, bids.value, (SubPlan 1)
   Index Cond: (bids.ts &gt; (now() - '7 days'::interval))
   SubPlan 1
     -&gt;  Unique  (cost=0.43..2.71 rows=5 width=24) 
                (actual time=0.027..0.029 rows=1 loops=57303)
           Output: asks.value, asks.instrument, asks.ts
           -&gt;  Custom Scan (SkipScan) on public.asks  
                  (cost=0.43..2.71 rows=5 width=24) 
                  (actual time=0.027..0.027 rows=1 loops=57303)
                 Output: asks.value, asks.instrument, asks.ts
                 -&gt;  Index Scan using asks_instrument_ts_idx on public.asks  
                        (cost=0.43..15996.56 rows=143152 width=24) 
                        (actual time=0.027..0.027 rows=1 loops=57303)
                       Output: asks.value, asks.instrument, asks.ts
                       Index Cond: ((asks.instrument = bids.instrument) 
                          AND (asks.ts &lt;= bids.ts))
 Planning Time: 1.231 ms
 Execution Time: 1703.821 ms

</code></pre><h3 id="conclusion">Conclusion</h3><p>While PostgreSQL does not have an <code>ASOF</code> keyword, it does offer the flexibility and functionality to perform similar operations. When you're using Timescale, things only get better with the enhancements like Skip Scan.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Fix Transaction ID Wraparound Exhaustion]]></title>
            <description><![CDATA[Learn more about transaction ID wraparound failure and how to avoid it in PostgreSQL databases. It involves treating your database as a house: turn on your Roomba, a.k.a. autovaccum.]]></description>
            <link>https://www.tigerdata.com/blog/how-to-fix-transaction-id-wraparound</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/how-to-fix-transaction-id-wraparound</guid>
            <category><![CDATA[PostgreSQL]]></category>
            <category><![CDATA[PostgreSQL Tips]]></category>
            <dc:creator><![CDATA[Kirk Laurence Roybal]]></dc:creator>
            <pubDate>Wed, 10 May 2023 16:45:49 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2023/05/How-to-fix-transaction-ID-wraparound.jpg">
            </media:content>
            <content:encoded><![CDATA[<p>Timescale employs <a href="https://timescale.ghost.io/blog/how-to-manage-a-commitfest/">PostgreSQL contributors</a> who are working feverishly to mitigate a problem with PostgreSQL. That problem is commonly referred to as “transaction ID wraparound” and stems from design decisions in the PostgreSQL project that have been around for decades.<br></p><p>Because this design decision was made so early in the project history, it affects all branches and forks of PostgreSQL, with Amazon RDS PostgreSQL, Greenplum, Netezza, Amazon Aurora, and many others suffering from transaction ID wraparound failures.<br></p><p>In this article, the second in a <a href="https://timescale.ghost.io/blog/how-to-fix-no-partition-of-relation-found-for-row/">series of posts tackling PostgreSQL errors or issues</a>, we’ll explain what transaction ID wraparound is, why it fails, and how you can mitigate or resolve it. But let’s start with a bit of PostgreSQL history.</p><h2 id="transaction-id-wraparound-xid-wraparound">Transaction ID Wraparound (XID Wraparound)</h2><p>To fully understand the problem of transaction ID wraparound (or XID wraparound), a bit of history is in order. The idea of a transaction counter in PostgreSQL originated as a very simple answer to transaction tracking. We need to know the order in which transactions are committed to a PostgreSQL database, so let's enumerate them. What is the simplest way to give transactions a concept of order? That would be a counter. What is the simplest counter? An integer. Tada!   <br></p><p>So, form follows function, and we have an integer counter. Seems like an obvious, elegant, and simple solution to the problem, doesn't it?<br></p><p>At first glance (and second and third, honestly), this rather simple solution stood up very well. Who would ever need more than 2<sup>31</sup> (just over 2 billion) transactions in flight? That was an astronomical number for 1985.</p><p></p><p>Since this is such a huge number, we should only need a single counter for the entire database cluster. That will keep the design simple, prevent the need to coordinate multiple transaction counters and allow for efficient (just four bytes!) storage. We simply add this small counter to each row, and we know exactly what the high watermark is for every row version in the entire cluster.</p><p>This simple method is row-centric and yet cluster-wide. So, our backups are easy (we know exactly where the pointer is for the entire cluster), and the data snapshot at the beginning and end of our transaction is stable. We can easily tell within the transaction if the data has changed underneath us from another transaction.   <br></p><p>We can even play peek-a-boo with other transaction data in flight. That lets us ensure that transactions settle more reasonably, even if we are wiggling the loose electrical connectors of our transaction context a bit.<br></p><p>We can stretch that counter quite a bit by making it a ring buffer. That is, we'll <code>OR</code> the next value to the end rather than add it there. That way, 2<sup>31</sup> or 1 = 1. So, our counter can wrap around the top (2<sup>31</sup>) and only becomes problematic when it reaches the oldest open transaction at the bottom.   </p><p>This "oldest" transaction is an upwardly moving number also, which then wraps around the top. So, we have the head (current transaction) chasing the tail (oldest transaction) around the integer, with 2,147,483,648 spaces from the bottom to the top. This makes our solution even look like a puppy dog, so now it's cute as well as elegant.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2023/05/dachshund-cricle.gif" class="kg-image" alt="A dog chasing his tail on top of a record player—the perfect representation of transaction ID wraparound" loading="lazy" width="480" height="360"></figure><p>The idea is that this would make the counter <strong>almost</strong> infinite, as the head should never catch the tail. At that point, who could possibly need more transactions than that? Brilliant!</p><p>Transaction counters are obviously the way to go here. They just make everything work so elegantly.</p><h2 id="explanation-the-plan-in-action">Explanation: The Plan in Action</h2><p>For many years, PostgreSQL raged forward with the XID wraparound transaction design. Quite a few features were added along the way that were based on this simple counter. Backups (<code>pg_basebackup</code> and its cousins), replication (both physical and logical), indexes, visibility maps, autovacuum, and defragmentation utilities all sprouted up to enhance and support this central concept.<br></p><p>All of these things worked together brilliantly for quite some time. We didn't start seeing the stress marks in the fuselage until the hardware caught up with us. As much as PostgreSQL wants to turn a blind eye to the reality of the hardware universe, the time came upon us when systems had the capacity to create more than 2<sup>31</sup> transactions at a time.   <br></p><p>High-speed ETL, queuing systems, IoT, and other machine-generated data could actually keep the system busy long enough that the counter could be pushed to its inherent limit.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-text"><i><em class="italic" style="white-space: pre-wrap;">"Everybody has a plan until I punch them in the face."</em></i> —Mike Tyson</div></div><p></p><p></p><p>These processes weren't exactly showstoppers, though. We came up with band-aids for much of it.   <br></p><p><code>COPY</code> got its own transaction context, reducing the load significantly. So did <code>VACUUM</code>.  <code>VACUUM</code> sprouted the ability to just freeze transactions without having to do a full row cleanup. That made the tail move forward a bit more quickly. External utilities gained features, individual tables gained <code>VACUUM</code> settings so they could be targeted separately.<br></p><p>Okay, that helped. But did it help enough? These features were never designed to fundamentally fix the issue. The issue is that size matters. But to be a bit more descriptive...</p><h2 id="possible-causes">Possible Causes</h2><h3 id="how-big-is-big">How big is big?</h3><p>In the early aughts, I was involved in building a data center for a private company. We spent some $5M creating a full-featured data center, complete with Halon, a 4K generator, Unisys ES7000, and a Clarion array. For the sake of our XID wraparound article, I'll focus on the Clarion array. It cost just a bit over $2M and could hold 96 drives for a whopping total of 1.6 TB! In 2002, that was incredible.<br></p><p>It doesn't seem so incredible now, does it? Kinda disappointing even. A few weeks ago, I needed an additional drive for my home backup unit. As I was walking through a Costco, I absent-mindedly threw a 2 TB drive into my cart that retailed for $69. It wasn't until I got home and was in the middle of installing it that it dawned on me how far we've come in the storage industry.<br></p><p>Some of the young whippersnappers don't even care about storage anymore. They think the "cloud" storage is effectively infinite. <a href="https://timescale.ghost.io/blog/scaling-postgresql-with-amazon-s3-an-object-storage-for-low-cost-infinite-database-scalability/">They're not wrong</a>.<br></p><p>To bring this around to PostgreSQL, tables with 2M rows were a big deal in 2002. Now that's not even on the radar of "big data." A VLDB (very large database) at the time was 2 TB. Now it's approaching 1 PB.<br></p><p>"A lot" of transactions in 2002 was 2M. Now, I would place that number at somewhere around 2B. Oops. Did I just say 2B? Isn't that close to the same number I said a few paragraphs ago was the limit of our transaction space? Let me see, that was 2<sup>31</sup>, which is 2,147,483,648. </p><p>Ouch.</p><h2 id="how-to-resolve-transaction-id-wraparound-failure">How to Resolve Transaction ID Wraparound Failure</h2><p>To be fair, not everybody has this problem. 2,147,483,648 is still a really big number, so a fairly small number of systems will ever reach this limit, even in the transaction environment of 2023.  </p><p>It also represents the number of transactions that are currently in flight, as the autovacuum process will latently brush away transaction counters that are no longer visible to the postmaster (<code>pg_stat_activity</code>).  But if the number of phone calls to consultants is any indication, this limitation is nonetheless becoming quite an issue. It certainly isn't going away any time soon.<br></p><p>Everybody in the PostgreSQL ecosystem is painfully aware of the limitation. This problem affects more than just the core of PostgreSQL, it affects all of the systems that have grown around it also. Do you know what it also affects? All the PostgreSQL-based databases, such as Amazon RDS and Aurora.<br></p><p>To make any changes to the core of PostgreSQL, all of the ramifications of those changes have to be thought out in advance. Fortunately, we have a whole community of people (some of them proudly part of our own organization) that are really, really good at thinking things out in advance.</p><p></p><p><strong>Query to show your current transaction ages:</strong></p><pre><code class="language-SQL">with overridden_tables as (
  select
    pc.oid as table_id,
    pn.nspname as scheme_name,
    pc.relname as table_name,
    pc.reloptions as options
  from pg_class pc
  join pg_namespace pn on pn.oid = pc.relnamespace
  where reloptions::text ~ 'autovacuum'
), per_database as (
  select
    coalesce(nullif(n.nspname || '.', 'public.'), '') || c.relname as relation,
    greatest(age(c.relfrozenxid), age(t.relfrozenxid)) as age,
    round(
      (greatest(age(c.relfrozenxid), age(t.relfrozenxid))::numeric *
      100 / (2 * 10^9 - current_setting('vacuum_freeze_min_age')::numeric)::numeric),
      2
    ) as capacity_used,
    c.relfrozenxid as rel_relfrozenxid,
    t.relfrozenxid as toast_relfrozenxid,
    (greatest(age(c.relfrozenxid), age(t.relfrozenxid)) &gt; 1200000000)::int as warning,
    case when ot.table_id is not null then true else false end as overridden_settings
  from pg_class c
  join pg_namespace n on c.relnamespace = n.oid
  left join pg_class t ON c.reltoastrelid = t.oid
  left join overridden_tables ot on ot.table_id = c.oid
  where c.relkind IN ('r', 'm') and not (n.nspname = 'pg_catalog' and c.relname &lt;&gt; 'pg_class')
    and n.nspname &lt;&gt; 'information_schema'
  order by 3 desc)
SELECT *
FROM per_database;
</code></pre>
<p><a href="https://gitlab.com/postgres-ai/postgres-checkup/-/blob/master/resources/checks/F002_autovacuum_wraparound.sh"><em>Adapted from Postgres-Checkup</em> </a></p><p></p><p>Many enhancements have already been made to PostgreSQL to mitigate the transaction ID wraparound problem and solve it permanently. Here are the steps on the way to the solution.</p><ul><li>The PostgreSQL system catalogs have already been enhanced to a 64-bit (eight-byte) transaction ID.</li><li>The functions and procedures of PostgreSQL have been expanded to 64-bit transaction ID parameters and outputs.</li><li>The backends (query worker processes) can deal with 64-bit transaction IDs.</li><li>Work has been done on the utilities of PostgreSQL (such as <code>pg_basebackup</code>) that previously assumed 32-bit integer transactions.</li><li>Replication, <code>VACUUM</code>, and other processes have been enhanced for 64-bit transactions.</li><li>A lot of other "stuff." Many smaller incidental fixes that were based on 32-bit assumptions needed modification.</li></ul><p>The goal of all of these changes is to eventually move to a 64-bit transaction counter for the entire system.</p><h3 id="where-do-we-go-from-here">Where do we go from here?</h3><p>There's a bit of bad news. I'm going to close my eyes while I write this, so I won't have to look at your face while you read it.<br></p><p>Updating the user tables in your database to use 64-bit transaction counters will require rewriting all of your data. Remember at the beginning, where I said the transaction counter was a per-row solution? Oh, yeah.   <br></p><p>That means that its limitations are also per row. There are only eight bytes reserved for <code>xmin</code>and eight bytes for <code>xmax</code> in the row header. So, every single row of data in the database is affected.<br></p><p>At some point, there will be a major version of PostgreSQL that requires a data dump, replication, <code>pg_upgrade</code> or another such process to re-create every row in the database in the new format. It is true that every major version of PostgreSQL <em>could</em> change the format of data on disk. <br></p><p>The <code>pg_upgrade</code> utility will not be able to use symlinks or hardlinks for the upgrade. These links usually allow for some efficiency while upgrading. There will be no such shortcuts when the "fix" for transaction ID wraparound is put into place.<br></p><p>Okay, now for the good news. We will all be in retirement (if not taking a dirt nap) when the next bunch of <s>suckers</s> engineers has to deal with this issue again. 2<sup>63</sup> is not double the number of transactions. It is 9,223,372,034,707,292,160 (nine quintillion) more.</p><h3 id="what-to-do-while-youre-waiting-for-infinity">What to do while you're waiting for infinity</h3><p>You can still make use of some basic mitigation strategies for transaction ID wraparound failures:</p><ul><li>Make the autovacuum process more aggressive to keep up with maintaining the database.</li><li>Use custom settings to make the autovacuum process more aggressive for the most active tables.</li><li><a href="https://www.postgresql.org/docs/current/app-vacuumdb.html">Schedule vacuumdb</a> to do additional vacuuming tasks for PostgreSQL to catch up faster.</li><li>Vacuum the <code>TOAST</code> tables separately so the autovacuum has a better chance of catching up.</li><li><code>REINDEX CONCURRENTLY</code> more frequently so that the autovacuum has less work to do.</li><li><code>CLUSTER ON INDEX</code> will re-order the data in the table to the same order as an index, thus "vacuuming" the table along the way.</li><li><code>VACUUM FULL</code>, which blocks updates while vacuuming but will finish without interruption. Let me say that again. There will be no writes while <code>VACUUM FULL</code> is running, and you can't interrupt it. 😠</li><li>Switch over to a secondary. The transaction counter will be reset to one when the system is restarted. (There are no transactions in flight, are there? 😄)</li><li>Use batching for  <code>INSERT</code>, <code>UPDATE</code>, and <code>DELETE</code> operations.  The counter is issued per transaction (not row), so grouping operations helps reserve counters.</li></ul><p>All of these strategies are basically the same thing. The objective is to ensure the tail number (oldest transaction) moves forward as quickly as possible. This will prevent you from ending up in a "transaction ID wraparound" scenario. <strong>🙂 ♥️ 👍</strong></p><h2 id="documentation-and-resources">Documentation and Resources</h2><ul><li>Check out the PostgreSQL documentation on <a href="https://www.postgresql.org/docs/15/routine-vacuuming.html">routine vacuuming</a> to prevent transaction ID wraparound failures.</li><li>The Timescale Docs also <a href="https://docs.timescale.com/mst/latest/troubleshooting/">troubleshoot transaction ID wraparound exhaustion</a>.</li></ul><h2 id="how-timescale-can-help">How Timescale Can Help</h2><p>While Timescale—also built on the rock-solid foundation of PostgreSQL— does not solve transaction ID wraparound failure, it can help you prevent it since our ingestion inherently batches the data by design after you<a href="https://docs.timescale.com/use-timescale/latest/ingest-data/about-timescaledb-parallel-copy/"> install <code>timescaledb-parallel-copy</code></a>.<br></p><p>Of course, you can do this for yourself with transaction blocks, but our tools will do the right thing automatically.<br></p><p>We also provide a <a href="https://timescale.ghost.io/blog/the-postgresql-job-scheduler-you-always-wanted-but-be-careful-what-you-ask-for/">general-purpose job scheduler</a> that can be useful for adding <code>VACUUM</code> and <code>CLUSTER</code> operations.<br></p><p>So, if you want to mitigate the chances of ever dealing with XID wraparound problems while enjoying <a href="https://timescale.ghost.io/blog/postgresql-timescaledb-1000x-faster-queries-90-data-compression-and-much-more/">superior query performance and storage savings compared to vanilla PostgreSQL</a> or <a href="https://timescale.ghost.io/blog/timescale-cloud-vs-amazon-rds-postgresql-up-to-350-times-faster-queries-44-faster-ingest-95-storage-savings-for-time-series-data/">Amazon RDS for PostgreSQL</a>, try Timescale. <a href="https://console.cloud.timescale.com/signup">Sign up now </a>(30-day free trial, no credit card required) for fast performance, seamless user experience, and the best compression ratios.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The PostgreSQL Job Scheduler You Always Wanted (Use it With Caution)]]></title>
            <description><![CDATA[We created a job scheduler built into PostgreSQL with no external dependencies. This is the power you always wanted, but with a few caveats.]]></description>
            <link>https://www.tigerdata.com/blog/the-postgresql-job-scheduler-you-always-wanted-but-be-careful-what-you-ask-for</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/the-postgresql-job-scheduler-you-always-wanted-but-be-careful-what-you-ask-for</guid>
            <category><![CDATA[Cloud]]></category>
            <category><![CDATA[PostgreSQL]]></category>
            <category><![CDATA[Job Scheduler]]></category>
            <category><![CDATA[Announcements & Releases]]></category>
            <dc:creator><![CDATA[Kirk Laurence Roybal]]></dc:creator>
            <pubDate>Thu, 19 Jan 2023 16:28:52 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2023/10/Screenshot-2023-10-12-at-6.22.47-PM.png">
            </media:content>
            <content:encoded><![CDATA[<p>As a PostgreSQL guy, it really makes you wonder why a built-in job scheduler is not a part of the core PostgreSQL project. It is one of the most requested features in the history of ever. Yet, somehow, it just isn’t there.</p><p>Essentially, a job scheduler is a process that kicks off in-database functions and procedures at specified times and runs them independently of user sessions. The benefits of having a scheduler built into the database are obvious: no dependencies, no inherent security leaks, fits in your existing high availability plan, and takes part in your data recovery plan, too.</p><p>The <a href="https://www.postgresql.org/">PostgreSQL Global Development Group</a> has been debating for years about including a built-in job scheduler. Even after the addition of background processes that would support the feature (<a href="https://www.postgresql.org/about/news/postgresql-96-released-1703/">all the way back in 9.6</a>), background job scheduling is unfortunately not a part of core PostgreSQL.</p><p>So being the PostgreSQL lovers we are at <a href="https://www.timescale.com" rel="noreferrer">Timescale</a>, <strong>we decided to build such a scheduler</strong> so that our users and customers can benefit from a job scheduler in PostgreSQL. In TimescaleDB 2.9.1, we extended it to allow you to schedule jobs with flexible intervals and <a href="https://docs.timescale.com/api/latest/informational-views/job_errors/">provide you with better visibility of error logs</a>.</p><p>The flexible intervals enable you to determine whether the next run of the job occurs based on the scheduled clock time or the end of the last job run. And by “better visibility” of the job logs, we mean that they are also being logged to a table where they can be queried internally. These were extended to prevent overlapping job executions, provide predictable job timing, and provide better forensics.</p><p>We extensively use the advantage of this internal scheduler for our core features, enabling us to defer <a href="https://timescale.ghost.io/blog/allowing-dml-operations-in-highly-compressed-time-series-data-in-postgresql/" rel="noreferrer">compression</a>, <a href="https://docs.timescale.com/use-timescale/latest/data-retention/about-data-retention/" rel="noreferrer">data retention</a>, and refreshing of continuous aggregates to a background process (among other things).</p><p><em>📝 Editor's note: </em><a href="https://www.timescale.com/learn/is-postgres-partitioning-really-that-hard-introducing-hypertables" rel="noreferrer"><em>Learn more about how TimescaleDB's hypertables enable all these features above as a PostgreSQL extension, plus other awesome things like automatic partitioning. </em></a><em>  </em></p><p>This scheduler makes Timescale much more responsive to the caller and results in more efficient processing of these tasks. For our own benefit, the job scheduler needs to be internal to the database. It also needs to be efficient, controllable, and scale with the installation.</p><p>We made all this power available to you as a PostgreSQL end user. If you're running PostgreSQL in your own hardware, you can <a href="https://docs.timescale.com/self-hosted/latest/install/" rel="noreferrer">install the TimescaleDB extension</a>. If you're running in AWS, <a href="https://console.cloud.timescale.com" rel="noreferrer">you can try our platform for free</a>. </p><h2 id="the-postgresql-job-scheduler-debate">The PostgreSQL Job Scheduler Debate</h2><p>But not so fast. Before you start rejoicing, let’s review the reasons that the PostgreSQL Global Development Group chose not to include a scheduler in the database—there'll be educational for you as a word of caution. </p><p>Rather than rehashing the discussion list on the subject, let's summarize the obstacles that came up in the <a href="https://www.postgresql.org/list/pgsql-hackers/">mailing list</a>: </p><p><strong>PostgreSQL is multi-process, not multi-thread.</strong> This simple fact makes having a one-to-one relationship of processes to user-defined tasks a fairly heavy implementation issue. Under normal circumstances, PostgreSQL expects to lay a process onto a CPU (affinity), load the memory through the closest non-uniform memory access (NUMA) controller, and do some fairly heavy data processing. </p><p>This works great when the expectation is that the process will be very busy the majority of the time. Schedulers do not work like that. They sit around with some cheap threads waiting to do something for the majority of the life of the thread. Just the context switching alone would make using a full-blown process very expensive.</p><p><strong>Background workers' processes are a relatively small pool by design.</strong> This has a lot to do with the previous paragraph, but also that each process allocates the prescribed memory at startup. So, these processes compete with SQL query workers for CPU and memory. And the background processes have priority over both resources since they are allocated at system startup.</p><p><strong>The next issue is more semantic.</strong> There are quite a few external schedulers available. Each one of them has a different implementation of the time management system. That is, there is a question about just how exactly the job should be invoked. Should it be invoked again if it is still running from the last time? Should the job be started again based on clock time or relative to the previous job run? From the beginning or the end of the last run? </p><p>There are quite a few more questions of this nature, but you get the idea. No matter how the community answers these questions, somebody will complain that the implementation is the wrong answer because <code>\&lt;insert silly mathematician answer here\&gt;</code>.</p><h2 id="why-we-still-need-a-postgresql-job-scheduler">Why We Still Need a PostgreSQL Job Scheduler</h2><p>Timescale doesn't have the luxury of debating how many angels can dance on the head of a pin. As a database service working with large volumes of data in PostgreSQL, we face a hard requirement of background maintenance for the actions of archival, compression, and general storage. Timescale's core features, excluding <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/hyperfunctions/">hyperfunctions</a>, depend on the job scheduler.</p><p>But, rather than create a bespoke scheduler for our own purposes we built a general-purpose scheduler with a public application programming interface.</p><p>This general-purpose scheduler is generally available as part of TimescaleDB. You may use it to set a schedule for anything you can express as a procedure or function. In PostgreSQL, that's a huge advantage because you have the full power of the <a href="https://www.tigerdata.com/blog/top-8-postgresql-extensions" rel="noreferrer">PostgreSQL extension</a> system at your disposal. This list includes plug-in languages, which allow you to do anything the operating system can do.</p><p>Timescale assumes that the developer/administrator is a sane and reasonable person who can deal with a balance of complexity. That is longhand for "we trust you to do the right thing."</p><h2 id="with-great-power-comes-great-responsibility">With Great Power Comes Great Responsibility</h2><p>So, let's talk first about a few best design practices for using the Timescale (PostgreSQL) built-in job scheduler.</p><ol><li><strong>Keep it short.</strong> The dwell time of the background process can lead to high concurrency.  You are also using a process shared by other system tasks such as sorting, sequential scans, and other system tasks.</li><li><strong>Keep it unlocked.</strong> Try to minimize the number of exclusive locks you create while doing your process.</li><li><strong>Keep it down.</strong> The processes that you are using are shared by the system, and you are competing for resources with SQL query worker processes. Keep that in mind before you kick off hundreds or thousands of scheduled jobs.</li></ol><p>Now, assuming we are using the product fairly and judiciously, we can move on to the features and benefits of having an internal scheduler.</p><h2 id="built-in-postgresql-job-scheduler-all-the-nice-stuff"><br>Built-In PostgreSQL Job Scheduler: All the Nice Stuff</h2><p>Now that we've covered the things that demand caution, here's a list of some of the benefits of using this scheduler: </p><ul><li>Physical streaming replication will also replicate the job schedule. When you go to switch over to your replica, everything will already be there.</li><li>You don't need a separate high-availability plan for your scheduler. If the system is alive, so are your scheduled jobs.</li><li>The jobs can report on their own success or failure to internal tables and the <a href="https://www.tigerdata.com/learn/what-is-audit-logging-and-how-to-enable-it-in-postgresql" rel="noreferrer">PostgreSQL log</a> file.</li><li>The jobs can do administrative functions like dropping tables and changing table structure by monitoring the existing needs and structures.</li><li>When you install Timescale, it's already there.</li></ul><p>📝<em> Editor's note: Quick reminder that you can </em><a href="https://docs.timescale.com/self-hosted/latest/install/" rel="noreferrer"><em>install the TimescaleDB extension</em></a><em> if you're running your own PostgreSQL database, or </em><a href="https://console.cloud.timescale.com" rel="noreferrer"><em>sign up for the Timescale platform</em></a><em> (free for 30 days). </em></p><h2 id="how-the-job-scheduler-works">How The Job Scheduler Works</h2><p>There is <a href="https://docs.timescale.com/use-timescale/latest/jobs/" rel="noreferrer">a quick introductory article in the Timescale documentation</a>. Click that link if you want more detailed information.</p><p>The TL;DR version is that you make a <a href="https://www.tigerdata.com/learn/understanding-postgresql-user-defined-functions" rel="noreferrer">PostgreSQL function</a> or procedure and then call the <code>add_job()</code> function to schedule it. Of course, you can remove it from the schedule using… Wait for it... <code>delete_job()</code>.</p><p>That's it. Really. All that power is at your fingertips, and all you need to know is two function signatures.</p><p>Something to be aware of while you're using the scheduler is that the job may be scheduled to repeat from the end of the last run or from the scheduled clock time (in TimescaleDB 2.9.1 and beyond). This allows you to ensure that the previous job has completed (by picking from the end of the run) or that the job executes at a prescribed time (making job completion your responsibility). </p><p>If you feel a bit homesick and just want to look at your adorable job, there's also:</p><pre><code class="language-SQL">SELECT * FROM timescaledb_information.jobs;
</code></pre>
<p>And, of course, for completeness, there's always <code>alter_job()</code> for rescheduling, renaming, etc.</p><p>Once your job has been created, it becomes the responsibility of the job scheduler to invoke it at the proper time. The job scheduler is a PostgreSQL background process. It wakes up every 10 seconds and checks to see if any job is scheduled in the near future.</p><p>If such a job is queued up, it will request another background process from the PostgreSQL master process. The database system will provide one (provided there are any available). The provided process becomes responsible for the execution of your job.</p><p>This basic operation has some ramifications. We have already mentioned that we need to use these background processes sparingly for resource allocation reasons. Also, there are only a few of them available. The maximum parallel count of background processes is determined by <a href="https://www.tigerdata.com/blog/timescale-parameters-you-should-know-about-and-tune-to-maximize-your-performance" rel="noreferrer"><code>max_worker_processes</code></a>. If you need help configuring TimescaleDB background workers, <a href="https://docs.timescale.com/use-timescale/latest/configuration/advanced-parameters/#timescaledbmax_background_workers-int" rel="noreferrer">check out our documentation</a>.</p><p><a href="https://timescale.ghost.io/blog/timescale-parameters-you-should-know-about-and-tune-to-maximize-your-performance/" rel="noreferrer"><em>📝 You can also check out this blog post on tuning TimescaleDB parameters. </em></a></p><p>On my system (Kubuntu 22.04.1, PostgreSQL 14.6), the default is 43. That number is just an example, as the package manager for each distribution of PostgreSQL has discretion about the initial setting. Your mileage **will** vary.</p><p>Changing this parameter requires a restart, so you will need to make a judgment call about how many concurrent processes you expect to kick off. Add that to this base number and restart your system. Of course, a reasonable number has been added for you in Timescale. Remember the CPU and memory limitations while you are making this adjustment.</p><h2 id="what-to-do-with-a-postgresql-job-scheduler-a-few-ideas">What to Do With A PostgreSQL Job Scheduler: A Few Ideas </h2><p>The original reasons for creating this scheduler involve building out-of-the-box features involving data management. That includes <a href="https://docs.timescale.com/timescaledb/latest/overview/core-concepts/compression/">compression</a>, <a href="https://docs.timescale.com/timescaledb/latest/overview/core-concepts/continuous-aggregates/">continuous aggregates</a>, <a href="https://docs.timescale.com/timescaledb/latest/overview/core-concepts/data-retention/">retention policy implementation</a>, <a href="https://docs.timescale.com/api/latest/hyperfunctions/downsample/">downsampling</a>, and backfilling.</p><p>You may want to use this for event notifications, sending an email, clustered index maintenance, partition creation, pruning, archiving, refreshing materialized views, or summarizing data somewhere to avoid the need for triggers. These are just a few of the obvious ideas that jump into my consciousness. You can literally do anything that the operating system allows.</p><h2 id="what-not-to-do">What <strong>Not</strong> to Do </h2><p>This would be a bad place to gum up the locking tables. That is, be sure that whatever you do here is done in a concurrent manner.</p><p><code>REFRESH INDEX CONCURRENTLY</code> is better than <code>DROP</code> / <code>CREATE INDEX</code>.  <code>REFRESH MATERIALIZED VIEW CONCURRENTLY</code> is better than <code>REFRESH MATERIALIZED VIEW</code>. You get it. Use <code>CONCURRENTLY</code>, or design concurrently. Better yet, do things in a tiny atomic way that takes little time anyway.</p><p>Long-running transactions that create a lot of locks will interfere with the background writer, the planner, and the vacuum processes. If you crank up too many concurrent processes, you may also run out of memory. Please try to schedule everything to run in series. You’ll thank me later.</p><h2 id="well-wishes-to-the-newly-crowned-emperor">Well Wishes to the Newly Crowned Emperor</h2><p>Now you have the power to do anything your little heart desires in the background of PostgreSQL without having any external dependencies. We hope you feel empowered, awed, and a little bit special. We also hope you will use your new powers for good! </p><h2 id="try-the-updated-job-scheduler">Try the Updated Job Scheduler</h2><p>The job scheduler is available in TimescaleDB 2.9.1 and beyond. If you’re self-hosting TimescaleDB, follow the <a href="https://docs.timescale.com/timescaledb/latest/how-to-guides/upgrades/#upgrade-timescaledb">upgrade instructions</a> in our documentation. If you are using the <a href="https://www.timescale.com/cloud" rel="noreferrer">Timescale platform</a>, upgrades are automatic, meaning that you already have the scheduler at your fingertips. </p><h2 id="keep-learning">Keep Learning </h2><p>If this article has inspired you to keep going with your PostgreSQL hacking, <a href="https://www.timescale.com/learn/postgresql-performance-tuning-key-parameters" rel="noreferrer">check out our collection of articles on PostgreSQL fine tuning.</a> </p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Read Before You Upgrade: Best Practices for Choosing Your PostgreSQL Version]]></title>
            <description><![CDATA[PostgreSQL upgrades have been known to be a bit of a controversial issue in the community. In this article, we will take the mystery out of the question of when an upgrade is appropriate and how Timescale allows you to do it as swiftly as possible.]]></description>
            <link>https://www.tigerdata.com/blog/read-before-you-upgrade-best-practices-for-choosing-your-postgresql-version</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/read-before-you-upgrade-best-practices-for-choosing-your-postgresql-version</guid>
            <category><![CDATA[PostgreSQL]]></category>
            <category><![CDATA[Cloud]]></category>
            <dc:creator><![CDATA[Kirk Laurence Roybal]]></dc:creator>
            <pubDate>Fri, 11 Nov 2022 18:35:31 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2022/11/Best-Practices-PostgreSQL-version_Hero--1-.png">
            </media:content>
            <content:encoded><![CDATA[<p>PostgreSQL has a long-standing reputation for having a miserable upgrade process. So, when the community heartily recommends that you should upgrade as soon as possible to the latest and greatest PostgreSQL version, it's not really surprising that your heart sinks, your mouth goes dry, and the outright dread of another laborious job takes over.</p><p>It's almost like finishing a long hike or trying to convince somebody that Betamax was better than VHS. Eventually, you just want it to be over so you can take a nap. There's not even any joy about all the new features and speed. It's just too exhausting to generate emotion anymore.</p><p>This blog post will hopefully serve as a guide for when to pull off the old band-aid. That is, when you should upgrade and what PostgreSQL version you should select as a target. By the end of this post, we will introduce you to our best practices for upgrading your PostgreSQL version in Timescale, so you can get over this process of upgrading as quickly and safely as possible.</p><h2 id="when-to-upgrade-postgresql-common-myths">When to Upgrade PostgreSQL: Common Myths</h2><p>The <a href="https://www.linkedin.com/company/postgresql-global-development-group">PostgreSQL Global Development Group</a> has simplified the upgrade process quite a bit with more explicit version numbering. Since there are only two external stimuli, there are only two choices: upgrade the binaries (minor version change) or upgrade the data on disk (major version change).</p><p>The developers of PostgreSQL never really had a plan in mind for when and how to upgrade.  This seems a bit of a harsh statement when tools like pg_upgrade exist but bear with me.   These tools were meant to make upgrades <strong>possible</strong>, not to imply any particular schedule or recommendations for an upgrade plan. The actual upgrade implementation was always left as an exercise for the administrator.</p><p>Let's start with some of the community's conventional wisdom and pretend that those ideas were actually a plan of sorts.</p><h3 id="myth-1-%E2%80%9Cupgrade-as-fast-as-possible-every-time%E2%80%9D">Myth 1: “Upgrade as fast as possible, every time”</h3><p>This "plan" is based on the fear of existing bugs. It is a very Rumsfeldian plan that assumes you don't know what the bugs are, but you're certainly better off if they're fixed. This makes for a very aggressive upgrade pace and hopes for a better tomorrow rather than a stable today.</p><h3 id="myth-2-upgrade-when-you-have-to">Myth 2: "Upgrade when you have to"</h3><p>The complete opposite fear-based pseudo-plan is to stick to the existing version—come hell or high water—unless you run into an otherwise unfixable bug that affects your installation. This is based on the idea that the bugs we know are better than the bugs we don't know. Unfortunately, it ignores the bugs you don't even know exist.</p><h3 id="myth-3-%E2%80%9Cupgrade-for-every-minor-version%E2%80%9D">Myth 3: “Upgrade for every minor version”</h3><p>This is the <a href="https://www.postgresql.org/support/versioning/">general recommendation</a> of the PostgreSQL Global Development Group. The general idea is that all software has bugs, and upgrading is better than not upgrading. That is a bit over-optimistic about new bugs being introduced and kind of ignores that new features that you don’t care about have to be configured—or else.</p><p>This comes a bit closer to planning than guessing for minor versions, as the minor versions of PostgreSQL do not change the file system; they only change the binaries. These upgrades tend to be super heavy on bug fixes and very low on new features, which is where bugs tend to get introduced. It doesn't say anything about bugs you have actually encountered, nor does it say anything about any improvements from which you might be able to benefit.</p><h3 id="myth-4-%E2%80%9Cupgrade-when-you-have-time-to-kill%E2%80%9D">Myth 4: “Upgrade when you have time to kill”</h3><p>Probably the most dangerous plan since you will never have more time in the future and will probably never upgrade. Experience says that this is a completely silly plan that never gets implemented.</p><h3 id="myth-5-%E2%80%9Cupgrade-when-there-are-security-fixes%E2%80%9D">Myth 5: “Upgrade when there are security fixes”</h3><p>Okay, this makes some kind of sense. Unfortunately, it ignores the rest of your installation and puts the application development team into tailspin mode for your DevOps enjoyment. It is the kind of policy you end up with when the DevOps team doesn’t really care about the Apps team.</p><h2 id="when-to-upgrade-postgresql">When to Upgrade PostgreSQL </h2><p>Much of this guide is based on personal experience with PostgreSQL upgrades over the years. In some cases, the old was better than the new, and in others, the other way around. In some cases, the fixes worked immediately. In others, well, not so much.</p><p>Very few hard and fast rules can be drawn when coming up with a plan of this nature, but I'll try to bring the experience to bear in a way that helps to make a decision in the future. That being said, this is a "best practice" based on experience, not a "sure-fire thing."</p><p>As a way to reduce the amount of just sheer subjectivity and opinion around choosing the moment to upgrade, I've taken a look through the release notes of PostgreSQL. In this lookie-look, I've attempted to note where bug fixes occurred and mentally move them back to the version where they were discovered. Unfortunately, this task is also somewhat subjective, as I was not a part of the bug fix development or the bug discovery. So these are just educated guesses, but I hope rather good ones.</p><p>Then I looked at the mental list that I had made and thought about whether it matched my personal experience with successful versus unsuccessful upgrades. It (again) seemed a subjectively good indicator of when an upgrade succeeded or failed.</p><p>So, on to the findings.  </p><p>The first thing I noticed in my research is that the biggest upgrade failures were with a new major version containing updates to the <a href="https://www.postgresql.org/docs/current/wal-intro.html">write-ahead log (WAL)</a>. These were most notable for versions 10 and 12.</p><p>Version 10 would make a book by itself. It was a major undertaking, with quite a few subsystem rewrites. In these version upgrades, there were numerous additions to items (like WAL for hash indexes), as well as improvements and changes to the background writer to support structural changes on disk. These major updates introduced the largest number of unintended behaviors, which lasted the longest before being detected and fixed.</p><p>The next most striking failures came from logical replication between 10 and 11.  Of course, logical replication was invented for 10, so there had never been an attempt to use it for production upgrades before. This first use in the field was—how should I put it?—interesting.</p><p>After that, the bugs died down a lot but were never quite gone.</p><h2 id="upgrade-plan">Upgrade Plan</h2><p>Here is my list of questions to ask before an upgrade.</p><p>1. How big is the change? Was it a major refactor, and did it involve any of the following?</p><ul><li><strong>Query planner:</strong> minor.</li><li><strong>WAL:</strong> major.</li><li><strong>Background writer: </strong>major.</li><li><strong>Memory, caching, locks, or anything else managed by the parent process:</strong> minor.</li><li><strong>Index engine</strong>: major (or just rebuild all your indexes anyway).</li><li><strong>Replication</strong>: major.</li><li><strong>Logging</strong>: minor.</li><li><strong>Vacuuming</strong>: minor.</li></ul><p>2. Were there any huge performance gains?</p><p>3. Does it include major security fixes?</p><p>4. Are there major built-in function() improvements/enhancements?</p><p>5. Do all of my extensions exist for the new version?</p><p>These are my rules of thumb for whether a new PostgreSQL version is compelling for upgrade.  Unfortunately, this still requires some subjective evaluation and a bit of professional knowledge. For instance, just because vacuum is a major feature, it doesn't mean it has ever been a problem with an upgrade. It <strong>could</strong> be, though, and we should look at its major changes with a bit of a wry mouth hold.</p><p>This brings me to my personal procedure that has (so far) followed the above guidelines.</p><ol><li><strong>Upgrade major versions when they reach the minor version .2.</strong> That is, 10.2, 11.2, 12.2, etc. This technique avoids the most egregious bugs introduced in major versions but still allows for staying reasonably close to the current.</li><li><strong>Upgrade minor versions as they are available.</strong> Minor upgrades have not created major issues thus far in my personal experience. The speed increases, bug fixes, security patches, and internationalization have been worth the minor risk.</li><li><strong>Upgrade immediately if your version is nearing the five-year mark</strong>. <a href="https://www.postgresql.org/support/versioning/" rel="noreferrer">The PostgreSQL Global Development Group releases a new major version every year</a> and supports it for five years after its release. You don't want to be left with an unsupported version.</li><li><strong>Upgrade when the security team tells you to</strong>. It doesn't happen very often, but when it does, it's a major event.</li><li><strong>Upgrade because you need functionality</strong>. Things to upgrade for: <code>CONCURRENTLY</code>, <code>SYSTEM</code>, and performance. Things not to upgrade for: functions(), operators, and libraries.</li></ol><p>That's all there is to it.  </p><p>I hope this blog post has helped you to make a decision for when PostgreSQL has compelling new features for you.</p><p>Of course, this is only a general rule of thumb. If you feel compelled to upgrade for some other reason, don't let my guide tell you what <strong>not</strong> to do. It only intends to help in the absence of any other stimuli for upgrade. You do you.</p><h2 id="i-am-ready-to-upgrade-now-what">I Am Ready to Upgrade. Now, What?</h2><p>So now you have followed the checklist above and determined that it’s time for you to upgrade your PostgreSQL version. If you’re running a production database, this may be easier said than done, especially if we are talking about upgrading your major version (e.g., from PostgreSQL 13 to PostgreSQL 14): </p><ul><li>Minor versions of PostgreSQL (e.g., from PostgreSQL 13 to PostgreSQL 13.2) are always backward compatible with the major version. That means that if you upgrade your production database, it is unlikely that anything is going to break due to the upgrade.</li><li>However, major versions of PostgreSQL are not backward compatible. That means that when you upgrade the PostgreSQL version of a database behind a mission-critical application, this may introduce user-facing incompatibilities which might require code changes in your application to ensure no breakage.</li></ul><p>Practical example: if you are upgrading from PostgreSQL 13 to 14, in PostgreSQL 14, the factorial operators ! and !! are no longer supported, nor is running the factorial function on negative numbers. What may seem like a silly example is, in fact, illustrative that assumptions made about how certain functions (or even operators) work between versions may break once you update. </p><p>Fortunately, PostgreSQL is awesome enough to provide clear <a href="https://www.postgresql.org/docs/current/release.html">Release Notes</a> stating the changes between versions. But this doesn’t solve our problem: how to upgrade production databases safely? </p><h2 id="timescale-to-the-rescue">Timescale to the Rescue</h2><p>This is one of the many areas in which choosing a cloud database will help. If you are self-hosting your mission-critical PostgreSQL database and want to run a major upgrade, you would have first to create a copy of your database manually, dumping your production data and restoring it in another database with the same config as your production database. </p><p>Then, you would have to upgrade this database and run your testing there. This process can take a while depending on your database's size (and if we’re talking about a time-series application, it’s probably pretty big). </p><p>Timescale makes the upgrading process way more approachable. Timescale is a database cloud for time-series applications built on TimescaleDB and PostgreSQL. In other words, this is PostgreSQL under the hood—with a sprinkle of TimescaleDB as the time-series secret sauce. </p><p>Timescale databases (which are called “services”) run on a particular version of TimescaleDB and PostgreSQL:</p><ul><li>As a user of Timescale, you don’t have to worry about the TimescaleDB upgrades: they will be handled automatically by the platform during a maintenance window picked by you. These upgrades are backward compatible and nothing you should worry about. They require no downtime.</li><li>The upgrades between minor versions of PostgreSQL are also automatically handled by the platform during your maintenance window. As we mentioned, these upgrades are also backward compatible. However, they require a service restart, which could cause a small (30 seconds to a few minutes) of downtime if you do not have a replica. We always alert users ahead of these in advance.</li></ul><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">✨</div><div class="kg-callout-text"><b><strong style="white-space: pre-wrap;">Editor's Note: </strong></b><i><em class="italic" style="white-space: pre-wrap;">For security reasons, we always run the latest available minor version within a major version on PostgreSQL in Timescale. These minor updates may contain security patches, data corruption problems, and fixes to frequent bugs—as a managed service provider, we have to store our customers’ data as safely as possible.</em></i></div></div><p>But what about upgrades between major versions of PostgreSQL? Since these are often not backward compatible, we cannot automatically upgrade your service in Timescale from, let’s say, PostgreSQL 13 to 14, which may introduce problems in your code and cause major issues! </p><p>Also, upgrading between major versions of PostgreSQL can (unfortunately but unavoidably) introduce some downtime. If you are running a mission-critical application, you want complete control over <em>when</em> that unavoidable downtime will occur. And you certainly want to test that upgrade first. </p><p>A database platform like Timescale can certainly help solve this issue. Upgrading your major version of Postgres will always be a decent lift—but a hosted database platform can make this process way smoother, helping you automate what can be automated and also facilitating your testing:</p><ul><li>In Timescale, you can upgrade the PostgreSQL version that’s running on your service by simply clicking a button.</li><li>You can use database forks to test your upgrade safely. Also, by clicking a button, Timescale allows you to create a database fork (a.k.a. an exact copy of your database) which you can then upgrade to estimate the required downtime to upgrade your production instance.</li><li>You can also use forks to test your application changes. Once your fork is upgraded, you can run some of your production queries—you can find some of these using <a href="https://timescale.ghost.io/blog/identify-postgresql-performance-bottlenecks-with-pg_stat_statements/"><code>pg_stat_statements</code></a>—on the fork to ensure they don’t contain any breaking changes to the new major version. </li></ul><p>Let’s explore this more in the next section. If you’re not using Timescale, you can create a <a href="https://console.cloud.timescale.com/signup">free account here</a>—you’ll have free access for 30 days, no credit card required.  </p><h2 id="safely-upgrading-major-postgresql-versions-in-timescale">Safely Upgrading Major PostgreSQL Versions in Timescale </h2><p>Here’s how you can safely upgrade your Timescale service:</p><ul><li>First, fork your service. Timescale allows you to fork (a.k.a. copy) your databases in one click—a fast and cost-effective process. You will only be charged when your fork runs, and you can immediately delete it after your testing is complete.</li><li>Now that you have a perfect copy of your production database ready for testing (with the click of a button), it’s time to click another button to tell the platform to upgrade your major PostgreSQL version automatically. You can do this in Timescale—we’ll tell you exactly how in a minute.</li><li>Once the upgrade is complete in your fork, run your tests.</li><li>In order to see how long the upgrade took on the fork, you can go to your metrics tab and check how long your service was unavailable (the grey zone in your CPU and RAM graphs). This will give you an estimate as to how long your primary service will be down when you choose to upgrade it.</li><li>When you’re sure that nothing breaks, you can upgrade your primary service. Make sure to plan accordingly! Upgrading will cause downtime, so make sure you have accounted for that as a part of your upgrade plan. </li></ul><p>Let’s see how this looks in the console. </p><p>First, check which TimescaleDB and PostgreSQL version your database is running on your service Overview page.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/11/Best-practices-upgrade-PostgreSQL_img-1.png" class="kg-image" alt="" loading="lazy" width="1095" height="635" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/11/Best-practices-upgrade-PostgreSQL_img-1.png 600w, https://timescale.ghost.io/blog/content/images/size/w1000/2022/11/Best-practices-upgrade-PostgreSQL_img-1.png 1000w, https://timescale.ghost.io/blog/content/images/2022/11/Best-practices-upgrade-PostgreSQL_img-1.png 1095w" sizes="(min-width: 720px) 720px"></figure><p>To fork your service is as easy as going to the Operations tab and clicking on the Fork service option. This will automatically create an exact snapshot of your database.</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/11/Best-practices-upgrade-PostgreSQL_img2.png" class="kg-image" alt="" loading="lazy" width="1170" height="553" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/11/Best-practices-upgrade-PostgreSQL_img2.png 600w, https://timescale.ghost.io/blog/content/images/size/w1000/2022/11/Best-practices-upgrade-PostgreSQL_img2.png 1000w, https://timescale.ghost.io/blog/content/images/2022/11/Best-practices-upgrade-PostgreSQL_img2.png 1170w" sizes="(min-width: 720px) 720px"></figure><p>To upgrade your major version of PostgreSQL, go to your Maintenance tab. Under Service upgrades, you will see a Service upgrades button. If you click that button, your service will be updated to the next major version of Postgres (in the example below, the service would be upgraded from PostgreSQL 13.7 to PostgreSQL 14).</p><figure class="kg-card kg-image-card"><img src="https://timescale.ghost.io/blog/content/images/2022/11/Best-practices-upgrade-PostgreSQL_img3.png" class="kg-image" alt="" loading="lazy" width="964" height="623" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/11/Best-practices-upgrade-PostgreSQL_img3.png 600w, https://timescale.ghost.io/blog/content/images/2022/11/Best-practices-upgrade-PostgreSQL_img3.png 964w" sizes="(min-width: 720px) 720px"></figure><h2 id="your-upgrade-is-complete">Your Upgrade Is Complete</h2><p>That’s it! You can now use the latest and greatest that PostgreSQL has to offer. That said, choosing to upgrade is no small feat. Before going through the upgrade process, there is a lot to consider, and it is important to have a plan to account for the downtime you will experience. </p><p>While the upgrade process can be a bit painful, you can at least rely on Timescale to handle the technical orchestration of the upgrade. In the future, we hope to offer even better tooling to make the upgrade process entirely pain-free (but we have to walk before we can run, right?).</p><p><br><br>If you’d like to see what Timescale has to offer, <a href="https://www.timescale.com/timescale-signup">start a free trial if you haven’t already. There’s no credit card required!</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Database Scaling: PostgreSQL Caching Explained]]></title>
            <description><![CDATA[Caching is integral to improving PostgreSQL performance. A look at how caching works in PostgreSQL—and how to make it work even better.]]></description>
            <link>https://www.tigerdata.com/blog/database-scaling-postgresql-caching-explained</link>
            <guid isPermaLink="true">https://www.tigerdata.com/blog/database-scaling-postgresql-caching-explained</guid>
            <category><![CDATA[PostgreSQL]]></category>
            <dc:creator><![CDATA[Kirk Laurence Roybal]]></dc:creator>
            <pubDate>Tue, 13 Sep 2022 14:34:51 GMT</pubDate>
            <media:content medium="image" href="https://timescale.ghost.io/blog/content/images/2022/09/caching-explained-timescale.png">
            </media:content>
            <content:encoded><![CDATA[<p>Follow us, friends, as we take a journey backward in time. We're going back to 1990, when soft rock was cool, and fanny packs were still okay. But we're not going there to enjoy the music and hang out at the mall. We’re going there to talk about database scaling and PostgreSQL caching. We’re going there because that was the last time PostgreSQL made simple sense—at least when it comes to resource management.</p><p>It was a time when the network was slower than a hard drive, the hard drives were slower than memory, and memory was slower than CPU. Back then, there was no such thing as a file system cache, hard drive cache, or operating system cache. Stuff like the Linux kernel was just a gleam in Linux Torvalds eye.</p><p>Why are we going there, you might ask? To be honest, because your poor author is a bit lazy. And because it's less likely that you'll be overwhelmed by the description we're about to give you.</p><p>PostgreSQL implemented a strategy to speed up access to data in those special years of clarity and simplicity. The basic idea was simple. Memory is faster than disk, so why not keep some of the most used stuff in memory to speed up retrieval? (Cue the sinister laughter from the future.)&nbsp;</p><p>This improvement has proven far more effective and valuable than the original authors probably envisioned. As PostgreSQL has matured over the years, the shared memory system matured with it. This most basic idea, what we commonly know as caching, continues to be very useful—in fact, the second most useful thing in PostgreSQL besides the working memory for each query. However, it is becoming less and less accurate over time, and other factors are becoming more prominent.</p><p>We are going to start at the beginning and then introduce the pesky truth—much in the same way that it blindsided the developers of PostgreSQL caching. As we go along, I can hear the advanced users of PostgreSQL. They are saying things like "except when," "unless," and "on the such-and-such platform." Yes, yes. We may or may not get around to your favorite exception to the rule. If we don't, apologies in advance for skipping it in the name of clarity and simplicity. This is not the last article we will ever write (in fact, there are already two more planned in the series, so stay tuned!). Please share your thoughts in the <a href="https://www.timescale.com/forum/c/conversation-community/events-blogs-and-live-streams/12"><u>Timescale Forum blog channel</u></a> and we'll try to get there in the next few go-arounds.</p><p>The good news is that I hope to introduce you to the concepts in a digestible format and pace. The bad news is that caching is a huge problem domain, and it will take a while to introduce you to all those concepts if you want to learn more about database scaling. Keep reading, and the information will get more useful and accurate over time.</p><h2 id="scaling-your-database-a-trip-down-shared-memory-lane">Scaling Your Database: A Trip Down Shared Memory Lane</h2><p>Back to 1990. There were basically two problems to solve to have a practical design for shared memory. The first one is that PostgreSQL is a multi-process system by design, so things happening in parallel processes can (and do) affect each other. The other is that the overhead of managing the memory system can't take more time than it would have just to retrieve the data anyway.</p><p>The good news for the first problem is that we already had a similar problem in the form of file system access. To solve that problem, we used an in-memory locking table.&nbsp;&nbsp;</p><p>Access to the file system is doled out by the postmaster process (the main one that creates all the other processes). Any other PostgreSQL process that wants to access a file has to ask the postmaster "pretty please" first. These locks are maintained in memory associated with the postmaster process. There isn't much reason to maintain them elsewhere because if the main process dies, the database will be unlocked. In other words, all the other processes working on files will be closed, and all locks released.</p><p>These requirements are suspiciously close to the same requirements for shared memory access. For the file system, we call this "locking" or, for memory, "latching."&nbsp; For the Postgres shared memory system, we call them "pins." Pins are very similar to locks but much simpler. All we have to care about is reading or writing to memory. So there are only two types of pins.</p><p>Now that we have the cooperation system down to two actions and a bit of memory, the next issue to solve is finding what you want when you need it. This is a simple matter of a memory scan. In PostgreSQL, the files on disk that store the actual table data are managed already with a page and leaf descriptor.&nbsp;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://timescale.ghost.io/blog/content/images/2022/09/Caching.jpg" class="kg-image" alt="" loading="lazy" width="1200" height="1240" srcset="https://timescale.ghost.io/blog/content/images/size/w600/2022/09/Caching.jpg 600w, https://timescale.ghost.io/blog/content/images/size/w1000/2022/09/Caching.jpg 1000w, https://timescale.ghost.io/blog/content/images/2022/09/Caching.jpg 1200w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Table files in PostgreSQL</span></figcaption></figure><p>These descriptors are simply an indicator of the location of a row within a database file. The format of a page is <a href="https://www.postgresql.org/docs/current/storage-page-layout.html"><u>described in the manual</u></a>.</p><p>Curiously, in that description it also says:</p><p>&gt; <a href="https://github.com/postgres/postgres/blob/master/src/include/storage/bufpage.h"><u>All the details can be found</u></a> in src/include/storage/bufpage.h.</p><p>Which is a reference to the shared memory code. It turns out that every disk write operation is handled in memory first. The ctid (page and leaf location of the eventual location in the data files for the table) is assigned <strong>before</strong> the data is written to disk.</p><p>That allows the pages in memory to be "looked up" using the same description the file system uses, even if the data hasn't yet been written to the file system. Clever, eh?<br></p><div class="kg-card kg-callout-card kg-callout-card-purple"><div class="kg-callout-emoji">💡</div><div class="kg-callout-text">We could go off on a tangent here about how the journaling system works and why a page could be in the journal (known as the write-ahead log) and memory but not written to the data file system yet. That is a topic for another day. Suffice it to say for now that durability is guaranteed by writing to the journal, so this is fine and dandy. In a future article, we'll also talk about how this buffering of writes acts as a backup for the journal. Again, that's getting ahead of ourselves.</div></div><p><br></p><p></p><p></p><h3 id="accessing-shared-memory">Accessing shared memory</h3><p>Each connection to the database is handled by a process that the PostgreSQL developers affectionately call a “backend.” This process is responsible for interpreting a query and providing the result. In some cases, it can retrieve that result from the shared memory held by the postmaster process. To access shared memory, we have to ask if the buffer system in the postmaster keeps a copy. The postmaster responds with one of two options: </p><ol><li>No, these aren’t the pages you’re looking for.</li><li>Yes, and this is what it might look like.</li></ol><p>"Might" in this case, because we are now beginning to see the effects of the first issue mentioned above. No, don't look back there; we'll repeat it here. The issue is that the processes are also affecting each other. A buffer may change based on any process still in flight acting on it. So, if we want to know that the buffer is valid, we have to read it while we "pin" it.</p><p>The semantics of this are much the same as the file system. Any number of processes may access the buffers for reading purposes. The postmaster simply keeps a running list of these processes. When any process comes along with a write operation and makes a change to the buffer, all of the ones that were reading it get a notice that the contents changed in flight. It is up to each "backend" (process handling connections to the user) to reread the buffer and validate that the data continues to be "interesting."&nbsp; That is, the row still matches the criteria of the query.</p><p>Since the data in shared memory is managed in pages, not rows, the particular row that a query was accessing may or may not have actually changed at all. It may have just had the misfortune of being in roughly the same place as something else that changed. It may have changed, but none of the columns that are a part of the query criteria were affected. It may have changed those columns, but in a way that still matches. Or the row may now no longer be a part of what the query was searching for. This is all up to the parallel processes handling the user query to decide.&nbsp;&nbsp;</p><p>Assuming that the data has made it through this gauntlet, it can be returned to the caller. We can reasonably assume that the row looks exactly like what would have been returned had we looked it up in the file system instead of memory. Despite having to work with a form of locking and lookup, we also presume that this was cheaper than spinning up a disk and finding and reading the data.</p><h2 id="postgresql%E2%80%99s-shared-memory-the-design-principles">PostgreSQL’s Shared Memory: The Design Principles&nbsp;</h2><p>Now that we know how the basic process of accessing shared data works, let's have a few words about why it was originally designed this way. PostgreSQL is an MVCC (multi-version concurrency control) system—another topic beyond this article's scope to explain. For the moment, we'll condense this to the point of libel. INSERT, DROP, TRUNCATE and SELECT are cheap. UPDATE, DELETE and MERGE are expensive. This is largely due to the tracking system for row updates. And yes, DELETE is considered an UPDATE for tracking purposes.</p><div class="kg-card kg-callout-card kg-callout-card-purple"><div class="kg-callout-emoji">💡</div><div class="kg-callout-text">PostgreSQL actually doesn’t UPDATE or DELETE rows. For tracking purposes, it maintains a copy of every version of a row that existed. On UPDATE, it creates a new row, makes the changes in the new row, and marks the row as current for any future transactions. For DELETE, it just marks the existing row as no longer, well, uh, existing. This is called a tombstone record. It allows all transactions in flight (and future transactions) to know that the row is dead, creating yet another cleanup problem, which we’ll (hopefully) talk about in future articles.</div></div><p><br></p><p></p><p></p><p>The caching system follows the same coding paradigm to have the same performance characteristics.&nbsp; It is possible in an alternate universe that there is a cheaper solution for caching that provides "better" concurrency. That being said, the overall system is as fast as the slowest part. If the design of the caching system wildly diverged from the file system, the total response to the caller would suffer at the worst-performing points of both systems.</p><p>Also, this system is tightly integrated into the PostgreSQL query planner. A secondary system (of nearly any kind) would likely introduce inefficiencies that are far greater than any benefits would be likely to cover.</p><p>And lastly, the system acts not only as a way to return the most-sought data to the caller but also as a change buffer for data modifying queries. Multiple changes may be made to the same row before being written to the file system. The background writer (responsible for making the journal changes permanent in the data files) is intelligent enough to save the final condition of the row to disk. This added efficiency alone pays for a lot of the complexity of caching.</p><h3 id="cache-eviction">Cache eviction</h3><p>There are a few outstanding things to consider in the design of PostgreSQL caching. The first is that, eventually, <em>something</em> has to come along and evict the pages out of the cache. It can't just grow indefinitely. That <em>something</em> is called the background writer. We know from the design above that PostgreSQL keeps track of which processes find the data interesting. When that list of processes is at zero entries, and the data hasn't been accessed in a while, the background writer will mark the block as "reusable" by putting it in a list called the "free space map." (Yes, this is much the same as the autovacuum process for the file system).&nbsp;&nbsp;</p><p>In the future, the memory space will be overwritten by some other (presumably more active) data. It's the circle of life. Buffer eviction is a garbage collection process with no understanding of what queries are in flight or why the data was put into the buffer. It just comes along on a timer and kicks things out that haven't been active in a while.</p><h3 id="forced-eviction-considered-evil">Forced eviction considered evil</h3><p>Also, the backend processes we have already mentioned may decide that they need a lot of memory to do some huge operation and request the postmaster commit everything currently staged to disk. This is called a buffer flush, and it is immediate. It is miserable for performance to get into a position where a buffer flush is necessary. All concurrent processes will halt until the flush is completed and verified.&nbsp; &lt;== A horrible statement in a concurrent database.</p><p>The postmaster may decide to flush the buffer cache to respond to some backend process. This is just as horrible as the previous paragraph for the same reasons.</p><h2 id="hey-i-was-using-that">Hey, I Was Using That</h2><p>PostgreSQL is paying attention (by the autovacuum process) to which data blocks are being accessed in the file system. If these accesses reach a threshold, PostgreSQL will read the block from the disk and stick it back in the cache because it seems to answer many questions. This process is blind to the queries that access the data, the eviction process, and anything else, for that matter. It's the old late-night kung-fu flick version of sticking the commercials in there anywhere. There is no rhyme or reason to where these blocks will end up in the buffer memory space, but they seem interesting, so in you go.</p><p>In fact, the blocks in memory are effectively unordered. Because of the process of eviction and restoration, no spatial order is guaranteed (or even implied.) This means that a "cache lookup" is effectively reading the page and leaf location for each block every time the cache is accessed. The postmaster holds a list of pages in the cache in order—much like a hash index—with the memory location of each block.&nbsp; As the size of the cache increases, additional lookups in the "cache index" are implied.</p><h2 id="more-postgresql-scaling-and-caching-coming-your-way">More PostgreSQL Scaling and Caching Coming Your Way</h2><p>Now that we have the PostgreSQL caching basics behind us, in the next few articles we can fast forward and explain some of the caveats that have come up along the way:</p><ul><li>We'll explain how improvements in disk and memory have affected the assumptions made in the original design.&nbsp;</li><li>We'll look at the expense of each of the caching operations. We can look at the journaling system and see how it interacts with the caching system.&nbsp;</li><li>We'll examine how valuable caching is today and how it could benefit from improvements already under development.&nbsp;</li><li>We'll look at how to tune caching for better performance and determine how much more performance you're likely to get based on your hardware and software choices.</li></ul><p>Stay tuned; there's a lot more where this article came from.</p><p>In the meantime, if you’re looking to expand your <a href="https://www.tigerdata.com/learn/building-a-scalable-database" rel="noreferrer">database scalability</a>, try our hosted service, <a href="https://www.timescale.com/cloud"><u>Timescale</u></a>. You will get all the PostgreSQL juice with extra features for <a href="https://www.tigerdata.com/blog/time-series-introduction" rel="noreferrer">time series</a> (continuous aggregation, compression, automatic retention policies, hyperfunctions). Plus, a platform with automated backups, high availability, automatic upgrades, and much more. <a href="http://tsdb.co/cloud-signup"><u>You can use it for free for 30 days; no credit card required.</u></a></p>]]></content:encoded>
        </item>
    </channel>
</rss>