Re: Adding REPACK [concurrently] - Mailing list pgsql-hackers
From | Mihail Nikalayeu |
---|---|
Subject | Re: Adding REPACK [concurrently] |
Date | |
Msg-id | CADzfLwW3XAFKYkBY7Jktf_rETzcqMj20GXgjESqu+WEoeEMkog@mail.gmail.com Whole thread Raw |
In response to | Re: Adding REPACK [concurrently] (Antonin Houska <ah@cybertec.at>) |
Responses |
Re: Adding REPACK [concurrently]
|
List | pgsql-hackers |
Hello, Antonin! Antonin Houska <ah@cybertec.at>: > > Where exactly should HeapTupleSatisfiesDirty() conclude that the tuple is > visible? TransactionIdIsCurrentTransactionId() will not do w/o the > modifications that you proposed earlier [1] and TransactionIdIsInProgress() is > not suitable as I explained in [2]. HeapTupleSatisfiesDirty is designed to respect even not-yet-committed transactions and provides additional related information. else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple))) { /* * Return the speculative token to caller. Caller can worry about * xmax, since it requires a conclusively locked row version, and * a concurrent update to this tuple is a conflict of its * purposes. */ if (HeapTupleHeaderIsSpeculative(tuple)) { snapshot->speculativeToken = HeapTupleHeaderGetSpeculativeToken(tuple); Assert(snapshot->speculativeToken != 0); } snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple); /* XXX shouldn't we fall through to look at xmax? */ return true; /* in insertion by other */ } So, it returns true when TransactionIdIsInProgress is true. However, that alone is not sufficient to trust the result in the common case. You may check check_exclusion_or_unique_constraint or RelationFindReplTupleByIndex for that pattern: if xmin is set in the snapshot (a special hack in SnapshotDirty to provide additional information from the check), we wait for the ongoing transaction (or one that is actually committed but not yet properly reflected in the proc array), and then retry the entire tuple search. So, the race condition you explained in [2] will be resolved by a retry, and the changes to TransactionIdIsInProgress described in [1] are not necessary. > I understand your idea that there are no transaction aborts in the new table, > which makes things simpler. I cannot judge if it's worth inventing a new kind > of snapshot. Anyway, I think you'd then also need to hack > HeapTupleSatisfiesUpdate(). Isn't that too invasive? It seems that HeapTupleSatisfiesUpdate is also fine as it currently exists (we'll see the committed version after retry).. The solution appears to be non-invasive: * uses the existing snapshot type * follows the existing usage pattern * leaves TransactionIdIsInProgress and HeapTupleSatisfiesUpdate unchanged The main change is that xmin/xmax values are forced from the arguments - but that seems unavoidable in any case. I'll try to make some kind of prototype this weekend + cover race condition you mentioned in specs. Maybe some corner cases will appear. By the way, there's one more optimization we could apply in both MVCC-safe and non-MVCC-safe cases: setting the HEAP_XMIN_COMMITTED / HEAP_XMAX_COMMITTED bit in the new table: * in the MVCC-safe approach, the transaction is already committed. * in the non-MVCC-safe case, it isn’t committed yet - but no one will examine that bit before it commits (though this approach does feel more fragile). This could help avoid potential storms of full-page writes caused by SetHintBit after the table switch. Best regards, Mikhail.
pgsql-hackers by date: