Deleting files from new pg_xact okCopying old pg_xact to new server ok…Setting frozenxid and minmxid counters in new cluster SQL command failedVACUUM FREEZEERROR: could not access status of transaction 640DETAIL: Could not open file "pg_xact/0000": No such file or directory.
Hi devs,
We recently found an interesting issue: during VACUUM FREEZE, for every table beingvacuumed & frozen, we would insert an unfrozen row into pg_stat_last_operaiton formetadata tracking. In some sense that breaks the contract of VACUUM FREEZE (w/o a table name)which is supposed to freeze everything in that database.
This caused an issue for a PR in which we tried to import system collations into template0at gpinitsystem time. The issue arose in pg_upgrade after we replaced the target cluster’spg_xact directory with the one from source cluster, then we would have trouble checkingvisibility of the unfrozen row in pg_stat_last_operation (below is from pg_upgrade log):
Deleting files from new pg_xact okCopying old pg_xact to new server ok…Setting frozenxid and minmxid counters in new cluster SQL command failedVACUUM FREEZEERROR: could not access status of transaction 640DETAIL: Could not open file "pg_xact/0000": No such file or directory.
The ERROR happened when we do VACUUM FREEZE for template0. But note that it couldhappen even if we simply SELECT FROM pg_stat_last_operation in template0.
I think we have two questions to discuss here:
QUESTION #1:Should we do anything to the pg_stat_last_operation record for VACUUM FREEZE? Some options:
a) We mark the row frozen as soon as we inserted it to pg_stat_last_operation. We have some ways to dothat - but before we talk about them, is that something worth doing? The contract of VACUUM FREEZEis broken only if we VACUUM FREEZE the entire database. If we just VACUUM FREEZE individual tables,there’s no such contract to respect (unless we VACUUM FREEZE pg_stat_last_operation itself).
b) Or, should we avoid metadata-tracking of VACUUM FREEZE for template0? That would solve our bug for sure.But for that PR we are also thinking about VACUUM FREEZE template1 and postgres too to make everythingequivalent to initdb, so potentially we could have similar bugs in future for template1 and postgres too.
c) Or, should we avoid metadata-tracking of VACUUM FREEZE at all? That might be an over-kill but just for thethought - is that information useful in real life?
d) Or, we do nothing here. For our bug, we just do not import collations to template0 in gpinitsystem. That’s a bitunfortunate but I guess not too impactful because firstly rarely do users create database from template0,and secondly, even if they do it would be just one additional call to pg_import_system_collations() anyway.
QUESTION #2:Why are we even doing VACUUM FREEZE for template0 in pg_upgrade?
Basically, after we replaced pg_xact, VACUUM FREEZE cannot reliably do the job anymore: it cannot freezea row whose xid is missing in pg_xact. So we have to make sure all rows are already frozen before we runVACUUM FREEZE (all the workarounds for QUESTION #1 are essentially for this purpose). But if we havemade sure of that, then why do we need to VACUUM FREEZE again? If freeze is a requirement, perhaps wecould perform VACUUM FREEZE before we replaced pg_xact?
Trying to think through what aspect conveys in PostgreSQL that all tuples in a relation or databasehave been frozen (I believe age dropping to 1 is the reality). And with the situation stated about pg_stat_last_operationthis would never happen in GPDB, correct?
Why these relations continue to have age greater than 0
PostgreSQL===========postgres=# select txid_current();txid_current--------------759
(1 row)
postgres=# vacuum freeze;VACUUM
postgres=# select txid_current();txid_current--------------760(1 row)
GPDB============postgres=# select txid_current();txid_current--------------1275
(1 row)
postgres=# vacuum freeze;VACUUM
postgres=# select txid_current();txid_current--------------1367(1 row)
Please refer to commit from PG 94 STABLE https://github.com/greenplum-db/gpdb-postgres-merge/commit/04634d50ba62df936d818a58dead045d1daa2a6c
which will provide some details as the assumption was broken
On Jun 15, 2023, at 7:19 AM, Ashwin Agrawal <ashwi...@gmail.com> wrote:
!! External Email
!! External Email: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender.
Please refer to commit from PG 94 STABLE https://github.com/greenplum-db/gpdb-postgres-merge/commit/04634d50ba62df936d818a58dead045d1daa2a6cwhich will provide some details as the assumption was brokenThanks for the link! It answers why VACUUM FREEZE is needed in pg_upgrade. And I agree we should keep such a safeguard (but move it prior to replacing pg_xact).But I wonder why it is done only for template0 though? I guess maybe just template1 and postgres didn’t have the problem at that time. But maybe we should do the samefor template1 and postgres for maximum assurance?
In the context of pure VACUUM FREEZE this is not a bug to produce unfrozen rows(given its correctly reflecting the age for the relation and database). The bug is purely in contextof pg_upgrade and all such operations which blindly rely on the assumption that after VACUUM FREEZEthe age of the database will be 0 without actually checking for the same. Based on that assumption, upgradeis replacing transaction files and such seems incorrect - as if age is not 0 means the transaction ID will appearon disk and hence can't perform the operations it's performing.
On Jun 21, 2023, at 8:02 AM, Ashwin Agrawal <ashwi...@gmail.com> wrote:
!! External Email
In the context of pure VACUUM FREEZE this is not a bug to produce unfrozen rows(given its correctly reflecting the age for the relation and database). The bug is purely in contextof pg_upgrade and all such operations which blindly rely on the assumption that after VACUUM FREEZEthe age of the database will be 0 without actually checking for the same. Based on that assumption, upgradeis replacing transaction files and such seems incorrect - as if age is not 0 means the transaction ID will appearon disk and hence can't perform the operations it's performing.Just to get more clarify about table age vs unfrozen row: it seems to me that there’s a difference in the expectation
from VACUUM FREEZE regarding them - it seems that the age doesn’t matter, but the unfrozen row does. Imagineafter VACUUM FREEZE we consumed some more xid w/o adding more rows into the table (e.g. select xid_current()),even in upstream that would make table age > 0. But that’s OK right? pg_upgrade does VACUUM FREEZE becauseit wants to make sure every row is frozen, not every table has age=0.
So if it is hard to avoid increasing table age after VACUUM FREEZE, we still have a way to fix our issue by avoidingunfrozen rows. Here’s a thought: if we can make sure pg_stat_last_operation is always the last one to freeze, thenwe would freeze all the pg_stat_last_operations rows we just inserted during a VACUUM FREEZE. After that, we mayjust skip the VACUUM meta-tracking of pg_stat_last_operation itself. Or, we may freeze pg_stat_last_operation againand only skip tracking of the second freeze. That way we made sure everything is frozen. How does that sound?
I would prefer the option to evaluate VACUUM FREEZEing pg_stat_last_operation at end during database wide vacuumoperation (even if it happens twice is fine) and not tracking its own VACUUM FREEZE to solve the current aspectsdiscussed in this thread.
On Jun 26, 2023, at 2:26 AM, Ashwin Agrawal <ashwi...@gmail.com> wrote:
!! External Email