Re: aio: worker: Free SMGR objects when idle

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Nazir Bilal Yavuz <byavuz81(at)gmail(dot)com>
Cc: shihao zhong <zhong950419(at)gmail(dot)com>, Andres Freund <andres(at)anarazel(dot)de>, Thomas Munro <thomas(dot)munro(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: aio: worker: Free SMGR objects when idle
Date: 2026-09-24 13:02:37
Message-ID: CAE8JnxMZx7g+miqKwQKdy8nu6+nRpWNpxNXM2oCp5yZ53zfsWg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Nazir,

Andres, Shihao -- please check this is flawed.

You already proposed to use PGAIO_WORKER_SMGR_CLEANUP_INTERVAL and I think
that is better than PGAIO_WORKER_SMGR_CLEANUP_THRESHOLD from v3.

And I think that instead of trying to cover corner cases we have to design
a solution that has clear operation conditions (invariants).

Here I outline one solution, if you want to implement yourself.

Invariants (unless the process is sleeping):
Any open file was used at least once during the last two checkpoints.
Any open file was used at least once over the last OPEN_FILE_EXPIRE_COUNT
operations.

Actions:
- On a checkpoint destroy files that were not used since the previous
checkpoint
less destructive than destroy all.
- After an I/O closes the LRU file if it was not used during the last
OPEN_FILE_EXPIRE_COUNT

All you need to add is an I/O stamp on each entry.

This should keep the files that are actively used always open,
while setting a clear limit on how long a file can remain open.
io_count wraps safely,

Here I present a pseudo-code, suggesting the loop, but it could be called
from
the I/O functions themselves, integrating with the LRU update and protecting
not only I/O workers, but any process that uses this API.

Since we are only closing old files it is unlikely that we have to wait.

ckpt_io = 0
io_count = 0
loop (io) {
++io_count;
newest = get_file(io.rlocator);
newest.last_use = io_count;
lru_tounch_file(newest); // move to the top of the list.
oldest = lru.tail;
cleanup(cutoff);
}

cleanup(cutoff) {
// O(1) amortized, always check at most one surviving
// file per call, and the number destroyed files
// is less than the number of I/O.
// Closes at most one file except when crossing a checkpoint.

// entries not used over the last operations
cutoff = io_count - OPEN_FILE_EXPIRE_COUNT;
if FirstCallSinceLastCheckpoint() {
// destroys entries not used since the previous checkpoint.
cutoff = max(cutoff, ckpt_io - 1);
ckpt_io = io_count;
}

for(;;) {
oldest = lru.tail;
if (lru.tail == NULL) never
// wrap-safe comparison
if (lru.last_use - cutoff >= 0)
break; // always true for the last file used.
destroy(oldest);
}
}

I would remove BasicOpenFile from fd.c, that function is dangerous.

And of course remove the spin lock inside FirstCallSinceLastCheckpoint()

Regards,
Alexandre

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Egor Ivkov 2026-09-24 13:05:22 Re: [PATCH] pg_combinebackup: make the OID range check in parse_oid() effective
Previous Message Nurlan Tulemisov 2026-09-24 12:50:05 Re: Import Statistics in postgres_fdw before resorting to sampling.