This year, CYBERTEC is celebrating 25 years since its founding in 2000 by our CEO Hans-Jürgen Schönig. In light of this, we have decided to hold an interview with the man himself to get some insights into how the company started and where it might be going. In addition to this, Hans-Jürgen made some interesting points about leading a business and the importance and future of PostgreSQL as a whole, so buckle up and prepare for some glimpses into the thought processes of an accomplished entrepreneur in our 2-part blog posting.
For our first few questions, we wanted to focus on how the company started out and the key decisions that led to the foundation of what we are today.
Interviewer: Why did you want to start a company?
Hans: Why did I want to start a company? That's a tough question. And actually, it has a simple answer: I never wanted to have a boss, you know?[laughs] Yeah, that's the simple reason. Just never wanted to have a boss. I always wanted to do my thing and I always thought that, well, you can do this in a better way, right? But the core idea, I think, is, I just never wanted to have a boss. And that's what it's all about. It's a freedom thing, I would say.
Interviewer: I see. So, what was your core idea, your vision?
Hans: Core idea? I simply liked the technology. I think it was that simple. I mean, back at that time, I was 22 years old. I think everybody who would say that there is a major vision of “we will change the world”, whatever, would be fairly dishonest. I just liked the technology and it happened to be a good thing, you know? A grand vision exists now, but I think back then I just thought, this is cool technology. I think that was the core motivation, I would say.
Interviewer: I see. So, talking about another vision. If you think back to then, where did you want to be in 2025? Have you reached this goal now?
Hans: I think when we started, the longest period in my head was something more like five years. I believe that ever
[...]The PostgreSQL 18 Press Kit contributors team is formed by translators from different countries covering an important set of languages. Here we have the list for the PostgreSQL 18 release. More information can be foundhere:
This CNPG Recipe explores the latest enhancements to CloudNativePG’s software supply chain and image management. Learn how our new, fully controlled build process—complete with Snyk scanning, image signing, and SBOMs—delivers smaller, more secure PostgreSQL images. We also detail how to leverage the newly streamlined image catalogs for simplified, declarative cluster management and safer fleet-wide upgrades in Kubernetes.
This is the third post about running Jepsen against CloudNativePG. Earlier posts:
First: shout out to whoever first came up with Oracle Data Guard Protection Modes. Designing it to be explained as a choice between performance, availability and protection was a great idea.
Yesterday’s blog post described how the core of all data safety iscopies of the data, and the importance of efficient architectures to meet data safety requirements.
With Postgres, three-node clusters ensure the highest level of availability if one host fails. But two-node clusters are often worth the cost savings in exchange for a few seconds of unavailability during cluster reconfigurations. Similar to Oracle, Postgres two-node clusters can be configured to maximize performance or availability or protection.
Oracle Data Guard mode | Behavior (summary) | Patroni configuration | CloudNativePG configuration |
---|---|---|---|
Max Performance oracle default | Async; fastest commits; possible data loss on failover | patroni default | cnpg default |
Max Availability (NOAFFIRM) | Sync when standby available; acknowledge after standby write (not flush); if none available, don’t block | synchronous_mode: true synchronous_commit: remote_write | method: any number: 1 dataDurability: preferred synchronous_commit: remote_write |
Max Availability (AFFIRM) | Sync when standby available; acknowledge after standby flush; if none available, don’t block |
Many experienced DBAs joke that you can boil down the entire job to a single rule of thumb: Don’t lose your data. It’s simple, memorable, and absolutely true – albeit a little oversimplified.
Mark Porter’s Cultural Hint “The Onion of our Requirements” conveys the same idea with a lot more accuracy:
We need to always make sure we prioritize our requirements correctly. In order, we think about Security, Durability, Correctness, Availability, Scalability via Scale-out, Operability, Features, Performance via Scaleup, and Efficiency. What this means is that for each item on the left side, it is more important than the items on the right side.
But this does not tell the whole story. If we’re honest, there is one critical principle of equal importance to everything on this list: Don’t lose all your money.
Every adult who’s managed their own finances knows we don’t have infinite money. Yes we want to keep the data safe. We also want to be smart about spending our money.
Relational databases are one of the most powerful and versatile places to store your data – and they are also one of the most expensive places to store your data. Just look at the per-GB pricing of block storage with provisioned IOPS and low latency, then compare with the pricing of object storage. No contest. Any time a SQL database is beginning to approach the TB range, we definitely should be looking at the largest tables and asking whether significant portions of that data can be moved to cheaper storage – for example parquet files on S3. (Or F3 files?)
Of course, sometimes we need fast powerful SQL and joins and transactions. So relational databases also should run as efficiently as possible. This has direct implications around how we keep the data safe.
From personal photos to enterprise databases, the core of all data safety is copies of the data. Logs and row-store/column-store files (and indexes) are data copies in different formats. You could almost parse the entire database industry through a lense that co
[...]Moving a Postgres database isn’t a small task. Typically for Postgres users this is one of the biggest projects you’ll undertake. If you’re migrating for a new Postgres major version or moving to an entirely new platform or host, you have a couple options:
Using pg_dump and pg_restore: pg_dump is a very reliable way to collect an entire database and restore it to a new place. This includes the entire schema, all tables, and special database elements. If you’re migrating a small database, that’s 50, 100, 150GB this is probably the easiest way to do it. On modern hardware a dump and restore using this method can be done in less than an hour.
Using WAL: For folks that have a WAL based backup system like pgBackRest or WAL-G/E, you can do a major Postgres migration by running a full base backup and streaming that WAL to your new host. Once you’re ready to do a cutover to the new database, you have an exact copy already standing by. This is a great way for larger databases, folks in the terabyte size, to do a major migration with minimal downtime.
But what if your database is too big for a dump restore and you can’t take the downtime? But you don’t have access to the WAL (i.e. you're on a host like RDS that doesn't share it). There’s a third option:
The architecture of logical replication is straightforward, see our intro post onData To Go: Postgres Logical Replication if you’re brand new to the topic. Your existing database will be thepublisher
, and the receiving database will be thesubscriber
. In the initial load, all data is copied from the publisher to the subscriber. Following the initial dat
PostgreSQL is a very versatile database. It has countless ways of bringing different functionalities to an already very sophisticated piece of software. Today, I’m going to show you how to use Rust and pgrx to bring etcd into your PostgreSQL database through a nice little interface called Foreign Data Wrappers.
(an image representing rust, etcd and postgres working together. Drawn by Jeremy Sztavinovszki)
Etcdis a distributed key-value store. It puts an emphasis on consistency and is most often used when a distributed system or a cluster needs to access the same data. (https://etcd.io/). You will find it in software such as Kubernetes, OpenShift and so on serving as a decentralized configuration store.
In Postgres, a foreign data wrapper is a construct that can be used to access foreign data through Postgres, as if it was right there in the database. A foreign data wrapper consists of 2 functions that Postgres calls:
The handler function is responsible for registering all of the functions that handle planning, reading and modifying the foreign data source. These functions are not PL/pgsql functions, but rather C functions that are passed into a struct, which Postgres then uses to interact with the FDW.
The validator function is responsible for validating the configuration options passed to the foreign data wrapper (e.g. connection strings).
With these functions, a SERVER and FOREIGN TABLE can be created and the data contained in the foreign data source can be retrieved.
Foreign data wrappers can be as simple as writing tuples to a csv file and as complex as e.g. reading the current temperature from some kind of remote sensor.
etcd_fdw is a foreign data wrapper for etcd written in Rust usingpgrx andsupabase-wrappers. It currently supports all of the most common CRUD operations and there’s more to come.
Imagi
[...]Our platform team has a regular meeting where we often use ops issues as a springboard to dig into Postgres internals. Great meeting today – we ended up talking about the internal architecture of Postgres replication. Sharing a few high-quality links from our discussion:
Alexander Kukushkin’s conference talk earlier this year, which includes a great explanation of how replication works
Alexander’s interview on PostgresTV with Nik Samokhvalov
Postgres Documentation forpg_stat_replication
system catalog (most important source of replication monitoring data)
CloudNativePG source code that translatespg_stat_replication
data into prometheus (titan) metrics
Chapter about streaming replication in Hironobu Suzuki’s book, Internals of PostgreSQL
Here is very helpful diagram from Alexander’s slide deck, which we referenced heavily during our discussion.
Can you identify exactly where in this diagram the three lag metrics come from?(write lag, flush lag and replay lag)
Learning Structured Query Language can be frustrating when double-checking that what you wanted to have done is actually what was done. PostgreSQL 18 has 'OLD and NEW support for RETURNING clauses in INSERT, UPDATE, DELETE, and MERGE commands'. Now you can get instant feedback.
The addition of the RETURNING clause in the previous version made MERGE much easier to use. Now it makes other commands easier.
To demonstrate, let's create a table with one column that is designated as a unique, primary key integer and insert a value.
createtablefoo (aintuniqueprimarykey);
insertintofoo (a)values (1);
Now is the point where many of us would run aSELECT a FROM foo, just to double check that indeed there is a 1 in column a.
There is now an option in PG 18 to use RETURNING, and it provides the previous 'old' value of a column along with the 'new' value.
insertintofoo (a)values (2)returningold.a,new.a;
insertintofoo(a)values (2)
on conflict(a) doupdate
set a =99
returningold.aasold,new.aasupdate
If you are reading this hoping that PostgreSQL finally gotUPDATE ... LIMIT
like MySQL, I have to disappoint you. TheLIMIT
clause is not yet supported forDML statements in PostgreSQL. If you want toUPDATE
only a limited number of rows, you have to use workarounds. This article will describe how to do this and how to avoid the pitfalls and race condition you may encounter. Note that most of the following also applies toDELETE ... LIMIT
!
UPDATE ... LIMIT
The following table contains 100 categories of 100 entries each. Since we don't discuss persistence, anUNLOGGED
table will do:
CREATE UNLOGGED TABLE upd ( id bigint GENERATED ALWAYS AS IDENTITY, category integer NOT NULL, flag boolean DEFAULT FALSE NOT NULL);INSERT INTO upd (category)SELECT (i - 1) / 100FROM generate_series(1, 100000) AS i;/* set hint bits, gather optimizer statistics */VACUUM (ANALYZE) upd;/* created after INSERT for performance */ALTER TABLE upd ADD PRIMARY KEY (id);CREATE INDEX ON upd (category);
Now we would like to set theflag
on a single row with category 42:
UPDATE updSET flag = TRUEWHERE category = 42LIMIT 1;
But alas, we cannot do that. We'll have to look for other ways to achieve our goal.
UPDATE ... LIMIT
with a simple subquery Since there is aLIMIT
clause forSELECT
, we can use a subquery as follows:
UPDATE upd AS uSET flag = TRUEWHERE u.category = 42 AND u.id IN (SELECT id FROM upd AS u2 WHERE u2.category = u.category LIMIT 1);
This query works as intended if nobody else is using the table. But it can fail to work as expected in the face of concurrent data modifications:
BEGIN;DELETE FROM upd WHERE id = 4201;
Now run our proposed update statement in a concurrent session, and it will hang. Now, if youCOMMIT
theDELETE
, ourUPDATE
statement will delete nothing:
UPDATE 0
How did this happen? It is true that the entire query sees the same state
[...]This is a follow‑up to the last article:Run Jepsen against CloudNativePG to see sync replication prevent data loss. In that post, we set up a Jepsen lab to make data loss visible when synchronous replication was disabled — and to show that enabling synchronous replication prevents it under crash‑induced failovers.
Since then, I’ve been trying to make data loss happen more reliably in the “async” configuration so students can observe it on their own hardware and in the cloud. Along the way, I learned that losing data on purpose is trickier than I expected.
To simulate an abrupt primary crash, the lab uses a forced pod deletion, which is effectively a kill -9
for Postgres:
kubectl delete pod -l role=primary --grace-period=0 --force --wait=false
This mirrors the very first sanity check I used to run on Oracle RAC clusters about 15 years ago: “unplug the server.” It isn’t a perfect simulation, but it’s a simple, repeatable crash model that’s easy to reason about.
I should note thatthe labelrole
is deprecated by CNPG and will be removed. I originally used it for brevity, but I will update the labs and scripts to use the labelcnpg.io/instanceRole
instead.
After publishing my original blog post, someone pointed out an important Kubernetes caveat with forced deletions:
Irrespective of whether a force deletion is successful in killing a Pod, it will immediately free up the name from the apiserver. This would let the StatefulSet controller create a replacement Pod with that same identity; this can lead to the duplication of a still-running Pod
https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
This caveat would apply to the CNPG controller just like a StatefulSet controller. In practice, for my tests, this caveat did not undermine the goal of demonstrating that synchronous replication prevents data loss. The lab includes an automation script (Exercise 3) to run the 5‑minute Jepsen test in a loop for ma
[...]PostgreSQL 18 is such an exceptional piece of software - it is hard to imagine anything better, right? I remember a similar thought when PostgreSQL 7 was released (“wow, how cool is that - this is it”). Well, let me put it mildly: I was wrong, and things are a lot better than they used to be. So, obviously the idea that PostgreSQL 18 is as far as humanity can go is also wrong. But what might be next?
The question is: While a major step forward has been made in PostgreSQL 18, and an important milestone has been achieved … is there anything out there that is even cooler? Something we might see in a couple of years? Well, maybe: I am of course talking about “Direct I/O”.
Under normal circumstances, a read works like this:
Sounds good? Well it is, and in most cases this is exactly what we want. However, there is an additional method: Direct IO. What it does can be summed up in one sentence: DirectIO bypasses the OS page cache layer.
But let us take a look at more detail: The good part is that it skips the overhead and scalability limitations. The bad part is that it skips the services that layer provi
[...]PostgreSQL 18 was released on schedule this week!
PostgreSQL 18 articles Exploring PostgreSQL 18 – Part 1: The COPY Command Gets More User-Friendly (2025-09-28) - Deepak Mahto Postgres 18: OLD and NEW Rows in the RETURNING Clause (2025-09-26) - Brandur Leach / Crunchy Data PostgreSQL: "UPDATE … RETURNING" made even better (2025-09-25) - Hans-Jürgen Schönig / CYBERTEC PostgreSQL 18: part 5 or CommitFest 2025-03 (2025-09-25) - Pavel Luzanov / PostgresPro Parallel Apply of Large Transactions (2025-09-24) - Amit Kapila Celebrating the PostgreSQL 18 Release (2025-09-24) - EnterpriseDB Tuning AIO in PostgreSQL 18 (2025-09-24) - Tomas Vondra Changes to NOT NULL in Postgres 18 (2025-09-23) - Álvaro Herrera / EnterpriseDB PostgreSQL 19 changes this weekDevelopment continues apace, but no user-visible features were committed this week. See the Commits of Interest list below for more details on changes.
InPostgreSQL 18, the statistics & monitoring subsystem receives a
significant overhaul - extended cumulative statistics, new per-backend I/O
visibility, the ability for extensions to export / import / adjust statistics,
and improvements to GUC controls and snapshot / caching behavior. These changes
open new doors for performance analysis, cross‑environment simulation, and
tighter integration with extensions. In this article I explore what’s new, what
to watch out for, Grand Unified Configuration (GUC) knobs, and how extension
authors can leverage the new C API surface.
Statistics (in the broad sense: monitoring counters, I/O metrics, and planner /
optimizer estimates) lie at the heart of both performance tuning and internal
decision making in PostgreSQL. Transparent, reliable, and manipulable
statistics, among other things, allow DBAs to address the efficiency of
PostgreSQL directly, as well as enable “extensions” to improve the user
experience.
That said, the historic statistics system of PostgreSQL has not been without
points of friction. These include limited ability to clear (relations)
statistics, metrics with units that don’t always align with user goals, and no C
API for using the PostgreSQL Cumulative Stats engine. PostgreSQL 18 addresses
these concerns head on.
Below is a summary of the key enhancements.
While statistics offer incredible value, their collection can take up
significant time and resources. PostgreSQL 18 introduces an important
consideration: with the expanded range of collectible metrics, the hash table
maximum size has been increased. Do keep in mind, especially if you’re designing
large-scale systems with table-per-customer architectures, that 1GB ceilings
have been shown to be hit with some millions of tables.
Here are the major new or improved features relating to statistics and
monitoring. Each item links to the relevant
ThePostgreSQL 18 Release Notes are like a great noir novel, full of surprises and intrigue. But we know who did it - the amazing PostgreSQL community.
If you have not perused the Notes, I advise you to do so now. They contain many of what I call 'Wow!' level items, such as the redesigned I/O subsystem, skip scan lookups, virtual generated columns, and more. The new Uuidv7 function and temporal constraints would be a significant leap forward. The OLD and NEW support for RETURNING will help lessen the learning curve for many who are new to SQL.
But go down to the seemingly more mundane items. Removing redundant self-joins, merge-joins can now use incremental sorts, and internally reordering the keys of SELECT DISTINCT to avoid sorting are the types of improvements that are not as attention-getting as the items in the preceding paragraph, but will make life much nicer for us.
Each release of PostgreSQL is faster than the previous one, and early reports report that this has happened again. This is an astounding delivery for those of us with backgrounds in other databases. All too often, new features came with a trade-off in performance that relegated upgrades to the back burner.
Please read the Acknowledgments. This is a list of the good folks who have worked very hard on our behalf to deliver an improved version of PostgreSQL. And they are already working on 19! If you run into any of them, please give them a big 'thank you'.
PostgreSQL 18 was released on September 25, 2024, introducing a host of exciting new features across all major areas. In this blog series, we’ll delve into these features and explore how they can benefit database developers and migration engineers transitioning to PostgreSQL.
Part 1 of the series focuses on enhancements to theCOPY
command, particularly for use cases involving loading external files into the database.
TheCOPY command is the default native option for loading flat files, primarily in text or CSV format, into PostgreSQL. If you are a data engineer or database developer looking for ways to load flat files into PostgreSQL, you must take care of quality checks and encapsulate a mechanism to report or discard failed records.
Until PostgreSQL 16, if quality checks were planned to be done on the database side, one option was to load flat files into staging tables with all columns as text or a generic data type and later move legitimate and discarded data to the concerned tables.With PostgreSQL 17 onward, the COPY command provides options for handling data type incompatibilities and logging failed rows or records usingon_error
options.
In PostgreSQL 18 these features is further enhances withREJECT_LIMIT
options. If overall discarded or error row data is beyond the reject limit the copy command is discarded as whole.
Let’s start by creating sample target tables and a CSV file with intentional data type mismatch issues. The sample table also has check and NOT NULL constraints defined on it.
CREATE TABLE copy18( col1 integer, col2 text DEFAULT 'x'::text, col3 text NOT NULL, col4 text, col5 boolean, col6 date, col7 timestamp without time zone CONSTRAINT copy18_col4_check CHECK (length(col4) > 2));
The CSV file contains data type incompatibilities in the following lines:
It has been a while since last time I blogged about any real-database-life problems and investigation. Here is one which I am really happy about, because it took a while and it was not obvious, but I found the root cause of a problem!
The problem has been going on for months: one of the Postgres databases WAL file system was growing with an alarming speed, many times faster than the database itself. Not like we can’t promptly remove the WALs, but this situation made any database restore a reali nightmare. At first, I suspected long transactions with suboptimal backfill algorithms. There were, indeed, a lot of jobs running on a regular basis which could be improved, however, I noticed a couple of things.
First, when the users fixed one of the primary suspects jobs, the situation with WAL growth didn’t change. Second, the rate of the growth couldn’t be explained by these suboptimal jobs: the data volumes they were removing and reloading were still magnitudes smaller than the WAL size we were dealing with. Finally, I decided to do what I should have done from the start – to take a look at what exactly was in these super-fast growing WALs.
Here is what I did:
create extension pg_walinspect;
select pg_current_wal_insert_lsn();
/* save the result:'17797/A167C8E8'
*/
select pg_current_wal_insert_lsn()
/* save the result :'17797/FEDE0CC8'
*/
select * from pg_get_wal_stats('17797/A167C8E8', '17797/FEDE0CC8', true)
where count>0;
To my amazement, I realized that 97% of the WALs stored the information about locks! To be honest, I didn’t even know that locks are recorded in the WAL files, so I am thankful for gracious Postgres community enlightening me!
Now that I knew where to look, I ran the following:
select * from pg_get_wal_records_info('17797/A167C8E8', '17797/FEDE0CC8')
where resource_manager='Heap'
and record_type='LOCK'
Zooming in:
Next, I found the table which had all these locks:
select * from pg_class where oid =10229951
When I fo
[...]We get a lot of queries about performance optimization for PostgreSQL from organizations using Odoo. The platform has been built entirely on PostgreSQL, and Odoo’s ORM and database layer have undergone several performance‑oriented changes across releases 17, 18, and 19 to reduce SQL overhead and take better advantage of PostgreSQL.
I have always maintained that SQL optimization is the most significant factor contributing towards database performance improvements, so I am very happy to see these improvements being rolled out.
Last week, Odoo released version 19 and ended support for version 16. If that isn’t motivation enough, I have gathered some of the performance improvements made in the system after version 16. Hopefully, that gives you enough incentive to upgrade Odoo in order to reduce performance woes.
The optimizations below are drawn from the official changelog and pull-request discussions, highlighting the most relevant changes that affect query generation and execution.
1.Combining search and read queries:In Odoo 17.4, the implementation of searching and reading was refactored so thatsearch()/search_read()can perform the search and fetch fields in the same SQL query. Two new methods –search_fetch()andfetch()– were introduced to minimize the number of queries needed forsearch_read. By fetching the required fields at the time of the search, the ORM avoids executing separate SELECT statements for each record.
2. Limit parameter forsearch_count():Odoo 17 added alimitargument tosearch_count(). When a UI element only needs an approximate count, the ORM stops counting after the limit and returns the approximate number, avoiding full table scans.
3. Specific index types:Developers can now specify the type of index when declaring a field. Supported types include:
PostgreSQL 18 is officially released, packed with improvements for performance, authentication, operations, and security. In this article, I’ll show you how to run it on Kubernetes from day one with CloudNativePG. I will summarise key new features like asynchronous I/O and OAuth 2.0, as well as theextension_control_path
parameter. This is a simple but critical feature for operational control in cloud-native environments, and one our team at CloudNativePG and EDB was proud to help contribute to PostgreSQL. You’ll see how this reflects our close collaboration with the upstream community, learn about our new half-sizedminimal
container image, and be able to follow my guide to deploy your first cluster today.
In ourCNPG series, we have mentioned that backups are crucial in every database system in case of any disaster. PostgreSQL has powerful recovery capabilities as well as backup capabilities, including the ability to restore a database cluster to aspecific moment in time. This is extremely useful in scenarios where we need to recover from user errors—such as accidental data deletion, wrong updates, dropped tables, or even dropped databases.
We will now walk through how to performPoint-in-Time Recovery (PITR) withCloudNativePG (CNPG) using a Barman Cloud plugin for backups.
First, we need to simulate a typical disaster. In our CNPG cluster instance:
postgres=# create database pitr_test;CREATE DATABASEpostgres=# \c pitr_testYou are now connected to database "pitr_test" as user "postgres".
Create a sample table and load some data:
CREATE TABLE test_data ( id SERIAL PRIMARY KEY, name TEXT, age INT, created_at TIMESTAMP DEFAULT NOW());INSERT INTO test_data (name, age, created_at)SELECT 'User_' || gs, (random() * 60 + 18)::INT, NOW() - (random() * interval '365 days')FROM generate_series(1, 10000) gs;
Update some rows to check later:
UPDATE test_dataSET age = 1WHERE id<= 15;
Note the current Write-Ahead Log (WAL) position and timestamp to recover exactly to this point with PITR:
SELECT pg_current_wal_lsn(); pg_current_wal_lsn -------------------- 0/85BDBB0SELECT now(); now ------------------------------ 2025-09-19 13:39:18.89106+00
Now, simulate adisaster:
DROP TABLE test_data;
At this point, the table is dropped and the only option we have to recover the table to timestamp we want is PITR.
We already have abackup and WAL archive system in place (as covered in theCNPG Backup with Barman Cloud blog). For point in time recovery, WAL files are essential since they contain all transactions after the last backup.
We can confir
[...]Daria Aleshkova,Oleksii Kliukin, Sergey Dudoladov and Andreas Scherbaum organized the PostgreSQL Berlin September Meetup. Divyendu Singh and Marko Coha presented talks.
ThePrague PostgreSQL meetup met for a September edition on the 22th. Pavel Lukasenko talked about “From SQL to Energy Charts: Scalable Energy Metering with PostgreSQL, Kubernetes, and Longhorn”, Josef Machytka presented “PostgreSQL Connections Memory Usage on Linux: How Much, Why, and When?” Gülçin Yıldırım Jelínek, Mayuresh B, Pavel Hák, Pavel Stěhule, and Tomas Vondra are organizers for the group.
September 25PGDay Napoli took place, we recognized the organizers and talk selection committee in a previous post.
Speakers:
Lightning talk speakers:
Postgres 18was released today. Well down page from headline features like async I/O and UUIDv7 support, we get this nice little improvement:
This release adds the capability to access both the previous (OLD) and current (NEW) values in the RETURNING clause for INSERT, UPDATE, DELETE and MERGE commands.
It's not a showstopper the way async I/O is, but itis one of those small features that's invaluable in the right situation.
A simple demonstration withUPDATE
to get all old and new values:
UPDATE fruitSET quantity = 300WHERE item = 'Apples'RETURNING OLD.*, NEW.*; id | item | quantity | id | item | quantity----+--------+----------+----+--------+---------- 5 | Apples | 200 | 5 | Apples | 300(1 row)
OLD
on upsert Say we're doing an upsert and want to differentiate between whether a row sent back byRETURNING
was one that was newly inserted or an existing row that was updated. This was possible before, but relied on an unintuitive check onxmax = 0
(see the very last line below):
INSERT INTO webhook ( id, data) VALUES ( @id, @data)ON CONFLICT (id) DO UPDATE SET id = webhook.id -- force upsert to return a rowRETURNING webhook.*, (xmax = 0) AS is_new;
The statement relies onxmax
being set to zero for a fresh insert as an artifact of Postgres' locking implementation (see afull explanation for why this happens). It works, but isn't a guaranteed part of the API, and could conceivably change at any time.
In Postgres 18, we can reimplement the above so it's more legible and doesn't rely on implementation details. It's easy too -- just check whetherOLD
is null in the returning clause:
INSERT INTO webhook ( id, data) VALUES ( @id, @data)ON CONFLICT (id) DO UPDATE SET id = webhook.id -- force upsert to return a rowRETURNING webhook.*, (OLD IS NULL)::boolean AS is_new;
Access toOLD
andNEW
will undoubtedly have many other useful cases, but this is one example that lets us imp
I recently attendedPGDay Lowlands 2025, an awesome PostgreSQL Community Event which took place on September 12th in the wondrous environment of the Rotterdam Zoo. It was a fantastic and focused day of PostgreSQL content, made all the more memorable by being in the company of amazing fish, sea turtles, and penguins!
I was honoured to kick off the conference, presenting a talk that I co-wrote withHettie Dombrovskaya. We've been collecting material and ideas for this for over a year, and it was exciting to finally share it.
Our presentation,"How Do We Get Postgres to the Next Level?", was a call to action to think strategically about the future of the project. We argued that to continue its incredible growth and solidify its leadership position, we need to focus on three core pillars: improving code quality, strategically adding new features, and kicking into overdrive our advocacy and community growth. We touched on the need for things like a dedicated performance team, a more modular development process, standardization on what PostgreSQL compatibility means, and improving the developer experience to attract new contributors.
Packing over a year's worth of research into a 20-minute talk was a challenge, but our main goal was to spark a conversation. Based on the reaction, I think we succeeded! The ideas were received positively, and as we'd hoped, they also challenged some existing perspectives.
What was most rewarding were the hours of "hallway track" discussions that followed. It was fantastic to hear that so many of our points resonated with attendees. A recurring theme in these conversations was a shared sense that for Postgres to continue scaling, we have to brin
[...]In this post we’ll cover two types of Postgres internals.
The first internal item is an “SLRU.” The acronym stands for “simple least recently used.” The LRU portion refers to caches and how they work, and SLRUs in Postgres are a collection of these caches.
SLRUs are small in-memory item stores. Since they need to persist across restarts, they’re also saved into files on disk. Alvaro1 calls SLRUs “poorly named” for a user-facing feature. If they’re internal, why are they worth knowing about as Postgres users?
They’re worth knowing about because there can be a couple of possible failure points with them, due their fixed size. We’ll look at those later in this post.
Before getting into that, let’s cover some basics about what they are and look at a specific type.
The main purpose of SLRUs is to track metadata about Postgres transactions.
SLRUs are a general mechanism used by multiple types. Like a lot of things in Postgres, the SLRU system is extensible which means extensions can create new types.
The “least recently used” aspect might be recognizable from cache systems. LRU refers to how the oldest items are evicted from the cache when it’s full, and newer items take their place. This is because the cache has a fixed amount of space (measured in 8KB pages) and thus can only store a fixed amount of items.
Old SLRU cache items are periodically cleaned up by the Vacuum process.
The buffer cache (sized by configuringshared_buffers) is another form of cache in Postgres. Thomas Munro proposed unifying the SLRUs and buffer cache mechanisms.
However, as of Postgres 17 and the upcoming 18 release (released September 9, 2025), SLRUs are still their own distinct type of cache.
What types of data is stored in SLRUs?
Transactions are a core concept for relational databases like Postgres. Transactions are abbreviated “Xact,” and Xacts are one of the types of data stored in SLRU
[...]Number of posts in the past two months
Number of posts in the past two months
Get in touch with the Planet PostgreSQL administrators atplanet at postgresql.org.