mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-04 07:37:22 +02:00
Fix decoding of speculative aborts.
During decoding for speculative inserts, we were relying for cleaning toast hash on confirmation records or next change records. But that could lead to multiple problems (a) memory leak if there is neither a confirmation record nor any other record after toast insertion for a speculative insert in the transaction, (b) error and assertion failures if the next operation is not an insert/update on the same table. The fix is to start queuing spec abort change and clean up toast hash and change record during its processing. Currently, we are queuing the spec aborts for both toast and main table even though we perform cleanup while processing the main table's spec abort record. Later, if we have a way to distinguish between the spec abort record of toast and the main table, we can avoid queuing the change for spec aborts of toast tables. Reported-by: Ashutosh Bapat Author: Dilip Kumar Reviewed-by: Amit Kapila Backpatch-through: 9.6, where it was introduced Discussion: https://postgr.es/m/CAExHW5sPKF-Oovx_qZe4p5oM6Dvof7_P+XgsNAViug15Fm99jA@mail.gmail.com
This commit is contained in:
parent
2da5ddf834
commit
602a32a685
@ -800,19 +800,17 @@ DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
|
|||||||
if (target_node.dbNode != ctx->slot->data.database)
|
if (target_node.dbNode != ctx->slot->data.database)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
|
||||||
* Super deletions are irrelevant for logical decoding, it's driven by the
|
|
||||||
* confirmation records.
|
|
||||||
*/
|
|
||||||
if (xlrec->flags & XLH_DELETE_IS_SUPER)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* output plugin doesn't look for this origin, no need to queue */
|
/* output plugin doesn't look for this origin, no need to queue */
|
||||||
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
|
if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
change = ReorderBufferGetChange(ctx->reorder);
|
change = ReorderBufferGetChange(ctx->reorder);
|
||||||
|
|
||||||
|
if (xlrec->flags & XLH_DELETE_IS_SUPER)
|
||||||
|
change->action = REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT;
|
||||||
|
else
|
||||||
change->action = REORDER_BUFFER_CHANGE_DELETE;
|
change->action = REORDER_BUFFER_CHANGE_DELETE;
|
||||||
|
|
||||||
change->origin_id = XLogRecGetOrigin(r);
|
change->origin_id = XLogRecGetOrigin(r);
|
||||||
|
|
||||||
memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
|
memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
|
||||||
|
@ -397,6 +397,9 @@ ReorderBufferReturnTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
|
|||||||
txn->invalidations = NULL;
|
txn->invalidations = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset the toast hash */
|
||||||
|
ReorderBufferToastReset(rb, txn);
|
||||||
|
|
||||||
pfree(txn);
|
pfree(txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,6 +470,7 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
||||||
break;
|
break;
|
||||||
@ -1677,8 +1681,8 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
|
|||||||
change_done:
|
change_done:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Either speculative insertion was confirmed, or it was
|
* If speculative insertion was confirmed, the record isn't
|
||||||
* unsuccessful and the record isn't needed anymore.
|
* needed anymore.
|
||||||
*/
|
*/
|
||||||
if (specinsert != NULL)
|
if (specinsert != NULL)
|
||||||
{
|
{
|
||||||
@ -1720,6 +1724,32 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
|
|||||||
specinsert = change;
|
specinsert = change;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abort for speculative insertion arrived. So cleanup the
|
||||||
|
* specinsert tuple and toast hash.
|
||||||
|
*
|
||||||
|
* Note that we get the spec abort change for each toast
|
||||||
|
* entry but we need to perform the cleanup only the first
|
||||||
|
* time we get it for the main table.
|
||||||
|
*/
|
||||||
|
if (specinsert != NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We must clean the toast hash before processing a
|
||||||
|
* completely new tuple to avoid confusion about the
|
||||||
|
* previous tuple's toast chunks.
|
||||||
|
*/
|
||||||
|
Assert(change->data.tp.clear_toast_afterwards);
|
||||||
|
ReorderBufferToastReset(rb, txn);
|
||||||
|
|
||||||
|
/* We don't need this record anymore. */
|
||||||
|
ReorderBufferReturnChange(rb, specinsert);
|
||||||
|
specinsert = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case REORDER_BUFFER_CHANGE_TRUNCATE:
|
case REORDER_BUFFER_CHANGE_TRUNCATE:
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -1827,16 +1857,8 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* speculative insertion record must be freed by now */
|
||||||
* There's a speculative insertion remaining, just clean in up, it
|
Assert(!specinsert);
|
||||||
* can't have been successful, otherwise we'd gotten a confirmation
|
|
||||||
* record.
|
|
||||||
*/
|
|
||||||
if (specinsert)
|
|
||||||
{
|
|
||||||
ReorderBufferReturnChange(rb, specinsert);
|
|
||||||
specinsert = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clean up the iterator */
|
/* clean up the iterator */
|
||||||
ReorderBufferIterTXNFinish(rb, iterstate);
|
ReorderBufferIterTXNFinish(rb, iterstate);
|
||||||
@ -2640,6 +2662,7 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
||||||
/* ReorderBufferChange contains everything important */
|
/* ReorderBufferChange contains everything important */
|
||||||
@ -2747,6 +2770,7 @@ ReorderBufferChangeSize(ReorderBufferChange *change)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
||||||
/* ReorderBufferChange contains everything important */
|
/* ReorderBufferChange contains everything important */
|
||||||
@ -3032,6 +3056,7 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
|
||||||
|
case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
|
||||||
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
|
||||||
break;
|
break;
|
||||||
|
@ -46,10 +46,10 @@ typedef struct ReorderBufferTupleBuf
|
|||||||
* changes. Users of the decoding facilities will never see changes with
|
* changes. Users of the decoding facilities will never see changes with
|
||||||
* *_INTERNAL_* actions.
|
* *_INTERNAL_* actions.
|
||||||
*
|
*
|
||||||
* The INTERNAL_SPEC_INSERT and INTERNAL_SPEC_CONFIRM changes concern
|
* The INTERNAL_SPEC_INSERT and INTERNAL_SPEC_CONFIRM, and INTERNAL_SPEC_ABORT
|
||||||
* "speculative insertions", and their confirmation respectively. They're
|
* changes concern "speculative insertions", their confirmation, and abort
|
||||||
* used by INSERT .. ON CONFLICT .. UPDATE. Users of logical decoding don't
|
* respectively. They're used by INSERT .. ON CONFLICT .. UPDATE. Users of
|
||||||
* have to care about these.
|
* logical decoding don't have to care about these.
|
||||||
*/
|
*/
|
||||||
enum ReorderBufferChangeType
|
enum ReorderBufferChangeType
|
||||||
{
|
{
|
||||||
@ -62,6 +62,7 @@ enum ReorderBufferChangeType
|
|||||||
REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID,
|
REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID,
|
||||||
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT,
|
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT,
|
||||||
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM,
|
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM,
|
||||||
|
REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT,
|
||||||
REORDER_BUFFER_CHANGE_TRUNCATE
|
REORDER_BUFFER_CHANGE_TRUNCATE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user