Orphaned Files in PostgreSQL

Started by Ashutosh Sharmaover 1 year ago13 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

needs rebasesuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t51091
psql -h localhost -U postgres

Built from patchset v6 (message #6), September 23, 2026 at 11:28 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t51091_6 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t51091_6 && git checkout t51091_6

Patchset v6 (message #6) is on t51091_6

Jump to latest
#1Ashutosh Sharma
ashu.coek88@gmail.com

Hi All,

While investigating one of our customer issues, we discovered several
orphaned data files on the disk that do not have corresponding entries
in the pg_class table. Upon further analysis, we identified specific
scenarios in PostgreSQL where this issue can occur. One such scenario
is as follows:

Consider a situation where a table is being created within a
transaction, and data is being loaded into it. If PostgreSQL
unexpectedly crashes while the transaction is still in progress, an
orphaned file may be left behind on the disk. In cases where multiple
such transactions occur, this can lead to the accumulation of numerous
orphaned files, resulting in significant disk space consumption.
Unfortunately, these files are not cleared during PostgreSQL's restart
process.

We have discussed this issue internally, and one proposed solution
involves adding a marker file to the disk for any table created within
a transaction, immediately upon its creation. This marker file would
then be removed during the commit process. If the transaction is
aborted due to a server crash, the marker file and the corresponding
disk file would be cleared at the end of the recovery process during
server startup.

I would appreciate your thoughts on this solution. Should you have any
suggestions or alternative approaches, I would be grateful to hear
them.

Additionally, I am unsure if this issue has already been reported or
if it is currently being addressed. If that is the case, I would be
grateful if you could point me to the relevant discussion thread so I
can follow the progress and contribute if needed.

Thank you for your time and assistance.

--
With Regards,
Ashutosh Sharma.

#2Bertrand Drouvot
bertranddrouvot.pg@gmail.com
In reply to: Ashutosh Sharma (#1)
Re: Orphaned Files in PostgreSQL

Hi,

On Tue, Feb 18, 2025 at 04:16:02PM +0530, Ashutosh Sharma wrote:

Additionally, I am unsure if this issue has already been reported or
if it is currently being addressed. If that is the case, I would be
grateful if you could point me to the relevant discussion thread so I
can follow the progress and contribute if needed.

I think that it is a known issue (see [1]/messages/by-id/7ff08868-843a-c39c-c96d-7e7f77fe5f5c@amazon.com). The thread links to a discussion that
could provide a fix (If I understood correctly) and introduces a way/extension to
clean those orphaned files in a clean way (using a dirty snapshot). Idea was to
gauge if there is interest to add this extension in contrib (would certainly
need some polishing and code work to meet the "contrib" expectations though).

[1]: /messages/by-id/7ff08868-843a-c39c-c96d-7e7f77fe5f5c@amazon.com

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

#3Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Ashutosh Sharma (#1)
Re: Orphaned Files in PostgreSQL

Hi All,

I am revisiting this thread to propose a possible fix for this known issue.

Issue:
=====
When PostgreSQL undergoes an unclean shutdown while a transaction is
creating a relation and loading a large amount of data into it, the
transaction is considered aborted during recovery. However, the
relation files created by that transaction can remain in the data
directory, occupying disk space without any visible catalog entry.

This causes several problems:

1) It can fill up the disk space and take the server down.
2) It can increase backup size and backup duration.
3) It adds unnecessary file system scanning, syncing, and maintenance overhead.
4) It forces manual work to tell real orphaned files apart from valid ones.
5) It wastes disk space until an admin finds and removes the files.

At present, when a transaction creates WAL-logged relation storage,
PostgreSQL adds it to a backend-local "pending delete" list, so the
files get cleaned up if the transaction aborts normally. The problem
is that this list only lives in memory. If the server crashes
mid-transaction, that list is lost and since WAL recovery replays
actions forward (redo) rather than undoing them, there may be no abort
record left to trigger cleanup of those files.

Proposed solution:
==============
To address this, I propose maintaining a durable relation-creation
marker for every transactionally created permanent relation.

The marker would be created under a new pg_relcreate directory and
store the following information:

1) The complete RelFileLocator.
2) The XID of the transaction that created the storage.
3) A marker format version.
4) A magic value identifying the file type.
5) A CRC protecting the marker contents.

typedef struct RelationCreateMarker
{
uint32 magic;
uint32 version;
RelFileLocator rlocator;
TransactionId xid;
pg_crc32c crc;
} RelationCreateMarker;

The marker filename would be derived from the tablespace OID, database
OID, and relfilenumber. For example it would be something like:
<tblspc_oid>_<dbid>_<relfilenode>. This marker is created, used, and
removed at different points in a relation's lifecycle - creation,
commit, abort, and recovery. Let's look at how each step would work.

A) Relation creation would proceed in this order:
----------------------------------------------------------------
For every persistent relation that needs storage, would go through
following steps inside RelationCreateStorage():

1) Insert an XLOG_SMGR_CREATE record marked as requiring a creation marker.
2) Create and fsync the marker file.
3) Create the physical relation file.
4) Register the existing delete-on-abort pending-delete entry.

This ordering guarantees that relation storage cannot become durable
without either a durable marker or WAL capable of reconstructing that
marker.

B) WAL replay:
--------------------
The XLOG_SMGR_CREATE payload would include a flag indicating whether a
marker is required. The creating XID remains in the common WAL record
header.

During redo, smgr_redo() would:

1) Obtain the XID from the WAL record header.
2) Recreate or validate the marker.
3) Recreate the relation fork as it does today.

If an identical marker already exists, redo accepts it. A conflicting
or corrupt marker may cause the recovery to fail rather than overwrite
unresolved cleanup state.

C) Normal commit:
-------------------------
For a committing transaction:

1) Force the transaction's commit WAL record to local durable storage.
2) Keep the relation.
3) Remove the marker durably when processing the "pending-delete" list.

Forcing synchronous commit is important here. Otherwise, PostgreSQL
could remove the marker and then crash before the asynchronous commit
record reaches disk. Recovery would subsequently consider the
transaction aborted, but no marker would remain to identify its
relation files.

The end result of the successful commit is as follows:

relation file: present
catalog row: committed
marker: removed

One subtle but important case: if the server crashes after the commit
record is durable but before the marker gets removed, that's fine.
When PostgreSQL restarts, its recovery process sees that this
transaction committed, keeps the table file, and simply cleans up the
now-unnecessary leftover marker.

D) Normal abort:
----------------------
For a transaction that aborted:

The existing pending-delete mechanism remains responsible for deleting
relation storage. It calls mdunlink() which truncates the main-fork to
make it a tombstone file and lets the next checkpoint remove the
tombstoned file and the marker file. The subsequent checkpoint unlinks
both the relation file and the marker file.

The end result is:

relation file: removed
catalog row: aborted / invisible
marker: removed

E) End-of-recovery cleanup:
-------------------------------------
After WAL replay and prepared-transaction recovery have completed,
PostgreSQL scans pg_relcreate.

For every valid marker:

1) If the creating XID committed, retain the relation and remove the
stale marker.
2) If the XID belongs to a prepared transaction, retain both the
relation and marker.
3) Otherwise, treat the transaction as crash-aborted and remove all
relation forks and the marker.

If PostgreSQL crashes after deleting the relation but before deleting
the marker, the next recovery attempts the relation deletion again and
then removes the marker.

F) Prepared transactions:
----------------------------------
Markers belonging to prepared transactions must survive recovery.

1) COMMIT PREPARED retains the relation and removes its marker after
the commit record is durable.
2) ROLLBACK PREPARED deletes the relation through the existing
two-phase pending-delete information, with marker removal following
physical tombstone cleanup.

G) Subtransactions:
---------------------------
1) On a subtransaction commit, its pending-delete entry transfers to
the parent transaction, so the marker remains until the top-level
transaction finishes.
2) On subtransaction abort, relation deletion starts immediately, but
the marker is retained until checkpoint processing removes the
relation tombstone.

Performance considerations:
---------------------------------------
The principal cost is additional I/O for transactional permanent
relation creation:

1) Writing and fsyncing a small marker file.
2) Fsyncing the marker directory.
3) Forcing local synchronous commit for transactions that created
marked storage.

This affects operations that create new permanent relfilenumbers, such
as CREATE TABLE, CREATE INDEX, REINDEX, VACUUM FULL, CLUSTER, and some
relation rewrites. Ordinary DML does not incur this cost.

The solution described above is implemented in the attached patch,
please take a look and share your feedback.

--
With Regards,
Ashutosh Sharma.

Show quoted text

On Tue, Feb 18, 2025 at 4:16 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

Hi All,

While investigating one of our customer issues, we discovered several
orphaned data files on the disk that do not have corresponding entries
in the pg_class table. Upon further analysis, we identified specific
scenarios in PostgreSQL where this issue can occur. One such scenario
is as follows:

Consider a situation where a table is being created within a
transaction, and data is being loaded into it. If PostgreSQL
unexpectedly crashes while the transaction is still in progress, an
orphaned file may be left behind on the disk. In cases where multiple
such transactions occur, this can lead to the accumulation of numerous
orphaned files, resulting in significant disk space consumption.
Unfortunately, these files are not cleared during PostgreSQL's restart
process.

We have discussed this issue internally, and one proposed solution
involves adding a marker file to the disk for any table created within
a transaction, immediately upon its creation. This marker file would
then be removed during the commit process. If the transaction is
aborted due to a server crash, the marker file and the corresponding
disk file would be cleared at the end of the recovery process during
server startup.

I would appreciate your thoughts on this solution. Should you have any
suggestions or alternative approaches, I would be grateful to hear
them.

Additionally, I am unsure if this issue has already been reported or
if it is currently being addressed. If that is the case, I would be
grateful if you could point me to the relevant discussion thread so I
can follow the progress and contribute if needed.

Thank you for your time and assistance.

--
With Regards,
Ashutosh Sharma.

Attachments:

t51091_3
0001-Remove-files-left-by-crash-aborted-relation-creation.patchapplication/octet-stream; name=0001-Remove-files-left-by-crash-aborted-relation-creation.patchDownload+362-4
#4Bertrand Drouvot
bertranddrouvot.pg@gmail.com
In reply to: Ashutosh Sharma (#3)
Re: Orphaned Files in PostgreSQL

Hi,

On Thu, Aug 20, 2026 at 02:40:25PM +0530, Ashutosh Sharma wrote:

Hi All,

I am revisiting this thread to propose a possible fix for this known issue.

Thanks for working on this!

This ordering guarantees that relation storage cannot become durable
without either a durable marker or WAL capable of reconstructing that
marker.

I wonder if logging XLOG_SMGR_CREATE before smgrcreate() could interact badly
with a concurrent checkpoint? I looked at [1]/messages/by-id/CAEepm=0ULqYgM2aFeOnrx6YrtBg3xUdxALoyCG+XpssKqmezug@mail.gmail.com and it looks like it used a separate
PRECREATE record and kept the usual CREATE record after physical creation.

Could this be reused here, independently of the rest of its undo infrastructure?

[1]: /messages/by-id/CAEepm=0ULqYgM2aFeOnrx6YrtBg3xUdxALoyCG+XpssKqmezug@mail.gmail.com

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

#5Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Bertrand Drouvot (#4)
Re: Orphaned Files in PostgreSQL

Hi,

On Fri, Aug 21, 2026 at 12:25 PM Bertrand Drouvot
<bertranddrouvot.pg@gmail.com> wrote:

Hi,

On Thu, Aug 20, 2026 at 02:40:25PM +0530, Ashutosh Sharma wrote:

Hi All,

I am revisiting this thread to propose a possible fix for this known issue.

Thanks for working on this!

This ordering guarantees that relation storage cannot become durable
without either a durable marker or WAL capable of reconstructing that
marker.

I wonder if logging XLOG_SMGR_CREATE before smgrcreate() could interact badly
with a concurrent checkpoint? I looked at [1] and it looks like it used a separate
PRECREATE record and kept the usual CREATE record after physical creation.

Thanks, that's a valid concern. Logging XLOG_SMGR_CREATE before
smgrcreate() would break the established ordering, and could allow a
concurrent checkpoint's redo pointer to advance past the create record
before the physical file has actually been created and registered for
synchronization.

The separate PRECREATE approach in [1] looks like it could be reused
independently of its undo infrastructure. PRECREATE could represent
just the intent to create the durable marker, while the existing
CREATE record would retain its normal position after physical file
creation. I'll explore this possibility further and incorporate it
into the next version of the patch.

Could this be reused here, independently of the rest of its undo infrastructure?

[1]: /messages/by-id/CAEepm=0ULqYgM2aFeOnrx6YrtBg3xUdxALoyCG+XpssKqmezug@mail.gmail.com

--
With Regards,
Ashutosh Sharma.

#6Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Bertrand Drouvot (#4)
Re: Orphaned Files in PostgreSQL

Hi,

On Fri, Aug 21, 2026 at 12:25 PM Bertrand Drouvot
<bertranddrouvot.pg@gmail.com> wrote:

Hi,

On Thu, Aug 20, 2026 at 02:40:25PM +0530, Ashutosh Sharma wrote:

Hi All,

I am revisiting this thread to propose a possible fix for this known issue.

Thanks for working on this!

This ordering guarantees that relation storage cannot become durable
without either a durable marker or WAL capable of reconstructing that
marker.

I wonder if logging XLOG_SMGR_CREATE before smgrcreate() could interact badly
with a concurrent checkpoint? I looked at [1] and it looks like it used a separate
PRECREATE record and kept the usual CREATE record after physical creation.

This has been addressed in the attached patch.

The patch also replaces the earlier design of creating one marker file
per relation file with a manifest file per transaction XID. Each
manifest records the durable relations created by that transaction.

On transaction completion, the corresponding manifests are retired and
removed. If PostgreSQL crashes before transaction cleanup completes,
recovery examines the remaining manifests after WAL replay. Manifests
belonging to committed or prepared transactions are preserved or
cleaned up appropriately, while relation files recorded for aborted or
incomplete transactions are removed. The processed manifests are then
removed.

The patch also handles subtransactions, prepared transactions, standby
replay, truncated manifests, and failures that leave retired manifests
behind.

Please take a look and let me know.

--
With Regards,
Ashutosh Sharma.

Attachments:

t51091_6
v2-0001-Remove-relation-files-left-by-crash-aborted-transact.patchapplication/octet-stream; name=v2-0001-Remove-relation-files-left-by-crash-aborted-transact.patchDownload+826-8
#7Andrey Borodin
amborodin@acm.org
In reply to: Ashutosh Sharma (#6)
Re: Orphaned Files in PostgreSQL

Hi Ashutosh,

On 23 Sep 2026, Ashutosh Sharma wrote:

Please take a look and let me know.

At the design level, one manifest per XID still means a pg_fsync() for
each appended record, including during redo. Have you considered WAL
plus delayed manifest synchronization, along the lines Andres
suggested [0]/messages/by-id/20170814185632.zodm5qykgss7ud32@alap3.anarazel.de? It would be useful to compare small-DDL and replay
costs before settling on synchronous per-record writes.

From reading v2, I am concerned about mapped catalog rewrites.
write_relmap_file() flushes XLOG_RELMAP_UPDATE before calling
RelationPreserveStorage(), where the patch now records PRESERVE.
A crash between those steps leaves the new mapping durable, but the
creating transaction uncommitted and its manifest without PRESERVE.
relmap_redo() does not preserve the storage either. Wouldn't the new
end-of-recovery cleanup then remove files needed by the mapped catalog?

Could preservation be part of the relmap update's recovery semantics?
A crash test in that window during VACUUM FULL of a mapped catalog
seems particularly important. I haven't run that reproducer yet.

The truncated-manifest test expects startup to fail. Can a crash during
a normal append leave that state without replay repairing it? If so,
could we retain the uncertain files rather than refuse startup?

Also, Greg recently mentioned renewed UNDO/FILEOPS work [1]/messages/by-id/5d89549c-117e-45ae-b934-a2bb71c82a79@app.fastmail.com. It may
be worth coordinating the scope with him. Preventing new orphans and
handling existing ones, as needed for online checksums, are separate
parts of the problem.

Thank you!

Best regards, Andrey Borodin.

[0]: /messages/by-id/20170814185632.zodm5qykgss7ud32@alap3.anarazel.de
[1]: /messages/by-id/5d89549c-117e-45ae-b934-a2bb71c82a79@app.fastmail.com

#8Greg Burd
greg@burd.me
In reply to: Andrey Borodin (#7)
Re: Orphaned Files in PostgreSQL

On Wednesday, September 23rd, 2026 at 8:39 AM, Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Hi Ashutosh,

On 23 Sep 2026, Ashutosh Sharma wrote:

Please take a look and let me know.

Hey Ashutosh!

Thanks for the excellent email kicking off this thread, I couldn't agree
more about the issue. I started in a different place, asking myself if
I could resurrect UNDO from ZHEAP without modifying HEAP at all (because
I tried and it didn't help, other different table AMs might find benefit
but HEAP is rather solid as it is) and if I could how would I demonstrate
and justify it without adding a new table AM or modifying HEAP?

I used to work on Berkeley DB and one of the features was its ability to
WAL log UNDO/REDO records for "filesystem operations" and then during
recovery tidy up and make things consistent. That struck me as a solid
first application of UNDO in Postgres, so I created that and called it
FILEOPS (because I'm not creative at all).

At the design level, one manifest per XID still means a pg_fsync() for
each appended record, including during redo. Have you considered WAL
plus delayed manifest synchronization, along the lines Andres
suggested [0]? It would be useful to compare small-DDL and replay
costs before settling on synchronous per-record writes.

From reading v2, I am concerned about mapped catalog rewrites.
write_relmap_file() flushes XLOG_RELMAP_UPDATE before calling
RelationPreserveStorage(), where the patch now records PRESERVE.
A crash between those steps leaves the new mapping durable, but the
creating transaction uncommitted and its manifest without PRESERVE.
relmap_redo() does not preserve the storage either. Wouldn't the new
end-of-recovery cleanup then remove files needed by the mapped catalog?

Could preservation be part of the relmap update's recovery semantics?
A crash test in that window during VACUUM FULL of a mapped catalog
seems particularly important. I haven't run that reproducer yet.

The truncated-manifest test expects startup to fail. Can a crash during
a normal append leave that state without replay repairing it? If so,
could we retain the uncertain files rather than refuse startup?

Also, Greg recently mentioned renewed UNDO/FILEOPS work [1]. It may
be worth coordinating the scope with him. Preventing new orphans and
handling existing ones, as needed for online checksums, are separate
parts of the problem.

I'm spending a lot of time this week cleaning up the UNDO and FILEOPS
patches in hopes of posting them soon as either an RFC or a proposed
patch set. I do have new table AMs that use it, but I don't think
they are ready for prime time yet. There are other use cases for UNDO
also like BLOB/CLOBs etc. that I think might be interesting and if I
can get the integration with nbtree and hash correct there are also
benefits for indexes.

That said, it's a large change and one that has philosophical and
technical challenges before the community could even consider merging
it in. I have hope, but it'll be a long road.

Your approach has less overhead/history to deal with. I'll need to
dig into it more to appreciate the direction you've taken but I do
agree that it needs to happen somehow and so I don't see this as a
competing idea at all.

best.

-greg

Show quoted text

Thank you!

Best regards, Andrey Borodin.

[0] /messages/by-id/20170814185632.zodm5qykgss7ud32@alap3.anarazel.de
[1] /messages/by-id/5d89549c-117e-45ae-b934-a2bb71c82a79@app.fastmail.com

#9Zsolt Parragi
zsolt.parragi@percona.com
In reply to: Andrey Borodin (#7)
Re: Orphaned Files in PostgreSQL

Hello!

This issue is also related to a recent discussion about online checksums[1]/messages/by-id/CA+TgmoaOCdjAjr240e_+xoqQCRmLC9MFn3kZOu-7j8w8HBrd8g@mail.gmail.com and I tried to look into possible solutions into it when investigating that, and I agree that it should be improved.

But I think the proposed patch has some issues.

On Wed, 23 Sep 2026, Andrey Borodin <amborodin@acm.org> wrote:

From reading v2, I am concerned about mapped catalog rewrites.
write_relmap_file() flushes XLOG_RELMAP_UPDATE before calling
RelationPreserveStorage(), where the patch now records PRESERVE.
A crash between those steps leaves the new mapping durable, but the
creating transaction uncommitted and its manifest without PRESERVE.

It doesn't need a random crash, PITR to a VACUUM FULL pg_class/pg_database/... with recovery_target_action = 'promote' can crash the server / completely brick the datadir.

For example if waldump shows:

Storage 0/030380B0 PRECREATE base/5/16387
RelMap 0/03040E48 UPDATE database ...
Storage 0/03041080 PRESERVE base/5/16384 xid 664
Storage 0/030410B0 PRESERVE base/5/16387 xid 664
Transaction 0/03041140 COMMIT

Then PITR to that RelMap entry reproduces the issue.

Another recovery issue is that end of recovery reconciliation runs after the cluster already left recovery, and it will unlink the storage of live transactions.
For example retrying BEGIN; CREATE TABLE ... ; loops accross a pg_ctl promote pulls the storage out from the new tables, the transactions can still COMMIT, and then access to that table fails because there's no storage for it.

@@ -262,6 +697,15 @@ RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
 		if (RelFileLocatorEquals(rlocator, pending->rlocator)
 			&& pending->atCommit == atCommit)
 		{
+			if (!atCommit && TransactionIdIsValid(pending->createXid))
+			{

This performs IO inside a critical section and can panic the server if that IO fails. And if that panic happens with a catalog table, the database can't start up again, because recovery unlinks the new file.

The patch also handles subtransactions, prepared transactions, standby
replay, truncated manifests, and failures that leave retired manifests
behind.

It seems to me that the standby keeps crash aborted orphan files, only the primary deletes them properly.

It would be useful to compare small-DDL and replay
costs before settling on synchronous per-record writes.

Other than costs, currently postgres honors synchronous_commit = off for storage creating DDL. With the patch, it no longer does so. That at least needs proper documentation, but I am not so sure that this is an actual requirement for preventing orphan files.

[1]: /messages/by-id/CA+TgmoaOCdjAjr240e_+xoqQCRmLC9MFn3kZOu-7j8w8HBrd8g@mail.gmail.com

#10Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Andrey Borodin (#7)
Re: Orphaned Files in PostgreSQL

Hi,

Thank you for taking a quick look at the proposed changes and sharing
your feedback.

On Wed, Sep 23, 2026 at 6:09 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Hi Ashutosh,

On 23 Sep 2026, Ashutosh Sharma wrote:

Please take a look and let me know.

At the design level, one manifest per XID still means a pg_fsync() for
each appended record, including during redo. Have you considered WAL
plus delayed manifest synchronization, along the lines Andres
suggested [0]? It would be useful to compare small-DDL and replay
costs before settling on synchronous per-record writes.

From reading v2, I am concerned about mapped catalog rewrites.
write_relmap_file() flushes XLOG_RELMAP_UPDATE before calling
RelationPreserveStorage(), where the patch now records PRESERVE.
A crash between those steps leaves the new mapping durable, but the
creating transaction uncommitted and its manifest without PRESERVE.
relmap_redo() does not preserve the storage either. Wouldn't the new
end-of-recovery cleanup then remove files needed by the mapped catalog?

Could preservation be part of the relmap update's recovery semantics?
A crash test in that window during VACUUM FULL of a mapped catalog
seems particularly important. I haven't run that reproducer yet.

The truncated-manifest test expects startup to fail. Can a crash during
a normal append leave that state without replay repairing it? If so,
could we retain the uncertain files rather than refuse startup?

Also, Greg recently mentioned renewed UNDO/FILEOPS work [1]. It may
be worth coordinating the scope with him. Preventing new orphans and
handling existing ones, as needed for online checksums, are separate
parts of the problem.

Thank you!

Best regards, Andrey Borodin.

[0] /messages/by-id/20170814185632.zodm5qykgss7ud32@alap3.anarazel.de
[1] /messages/by-id/5d89549c-117e-45ae-b934-a2bb71c82a79@app.fastmail.com

I have not yet reviewed the earlier discussions in these threads in
enough detail. It appears that the approach I am proposing overlaps
with Chris Travers earlier work, but I do not yet understand why that
work did not proceed, whether because of unresolved technical issues
or other considerations. I will review those discussions before
deciding how to revise this proposal.

The concern raised about mapped relation files looks valid and
requires additional handling. However, before working out that fix, I
would first like to understand the earlier proposals and reconsider
the overall strategy in that context.

--
With Regards,
Ashutosh Sharma.

#11Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Greg Burd (#8)
Re: Orphaned Files in PostgreSQL

Hi,

On Wed, Sep 23, 2026 at 11:55 PM Greg Burd <greg@burd.me> wrote:

Hey Ashutosh!

Thanks for the excellent email kicking off this thread, I couldn't agree
more about the issue.

Thanks to you too for working on this project. I am glad to know that
I am not alone on this path of finding a solution to this problem.

I started in a different place, asking myself if

I could resurrect UNDO from ZHEAP without modifying HEAP at all (because
I tried and it didn't help, other different table AMs might find benefit
but HEAP is rather solid as it is) and if I could how would I demonstrate
and justify it without adding a new table AM or modifying HEAP?

I used to work on Berkeley DB and one of the features was its ability to
WAL log UNDO/REDO records for "filesystem operations" and then during
recovery tidy up and make things consistent. That struck me as a solid
first application of UNDO in Postgres, so I created that and called it
FILEOPS (because I'm not creative at all).

Thanks for sharing all of this. I will surely go through it once you
make it public.

I'm spending a lot of time this week cleaning up the UNDO and FILEOPS
patches in hopes of posting them soon as either an RFC or a proposed
patch set. I do have new table AMs that use it, but I don't think
they are ready for prime time yet. There are other use cases for UNDO
also like BLOB/CLOBs etc. that I think might be interesting and if I
can get the integration with nbtree and hash correct there are also
benefits for indexes.

Sure, please share it, and I will be happy to contribute in whatever
way I can. I see that you shared this information earlier here [0]/messages/by-id/5d89549c-117e-45ae-b934-a2bb71c82a79@app.fastmail.com,
but I somehow missed it, perhaps because I was not part of that
discussion or did not realize that it was related to the work being
discussed here.

That said, it's a large change and one that has philosophical and
technical challenges before the community could even consider merging
it in. I have hope, but it'll be a long road.

Yes, that seems to be pretty obvious considering the complexity involved here.

Your approach has less overhead/history to deal with. I'll need to
dig into it more to appreciate the direction you've taken but I do
agree that it needs to happen somehow and so I don't see this as a
competing idea at all.

Well, it seems like this approach has already been discussed/proposed
earlier here [1]/messages/by-id/CAN-RpxDBA7HbTsJPq4t4VznmRFJkssP2SNEMuG=oNJ+=sxLQew@mail.gmail.com by Chris but somehow it didn't move forward. I still
need to understand the details and any blockers that prevent it from
progressing.

[0]: /messages/by-id/5d89549c-117e-45ae-b934-a2bb71c82a79@app.fastmail.com
[1]: /messages/by-id/CAN-RpxDBA7HbTsJPq4t4VznmRFJkssP2SNEMuG=oNJ+=sxLQew@mail.gmail.com

--
With Regards,
Ashutosh Sharma.

#12Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Zsolt Parragi (#9)
Re: Orphaned Files in PostgreSQL

Hi,

Thanks for taking a quick look at the proposed changes and sharing
your review comments.

On Thu, Sep 24, 2026 at 3:40 AM Zsolt Parragi <zsolt.parragi@percona.com> wrote:

Hello!

This issue is also related to a recent discussion about online
checksums[1] and I tried to look into possible solutions into it when
investigating that, and I agree that it should be improved.

Yes, there is some connection. I haven't had a chance to go through
the previous discussions yet. Let me first go through all the past
discussions related to this and then reconsider the approach.

I am also glad to learn from this discussion that Greg, as mentioned
in his earlier response in this thread, is working on a solution to
this problem as well. We can wait for his solution to become available
and see how things progress from there.

--
With Regards,
Ashutosh Sharma.

Show quoted text

[1] : /messages/by-id/CA+TgmoaOCdjAjr240e_+xoqQCRmLC9MFn3kZOu-7j8w8HBrd8g@mail.gmail.com

#13Bertrand Drouvot
bertranddrouvot.pg@gmail.com
In reply to: Zsolt Parragi (#9)
Re: Orphaned Files in PostgreSQL

Hi,

On Wed, Sep 23, 2026 at 03:10:27PM -0700, Zsolt Parragi wrote:

Hello!

This issue is also related to a recent discussion about online
checksums[1] and I tried to look into possible solutions into it when
investigating that, and I agree that it should be improved.

Indeed. The proposed approach could prevent new orphan files, but it would not
address existing ones or "relation looking" files "manually" added to the data
directory. While the latter can be considered unsupported, they can trigger the
same base backup verification failure.

FWIW, pg_orphaned [1]https://github.com/bdrouvot/pg_orphaned, already provides functions to list, quarantine, restore
and remove such unreferenced files. I wonder if it would be worth considering
adding pg_orphaned, or part of its functionality, to contrib, independently of
this patch?

[1]: https://github.com/bdrouvot/pg_orphaned

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com