From: | Heikki Linnakangas <hlinnaka(at)iki(dot)fi> |
---|---|
To: | pgsql-hackers <pgsql-hackers(at)postgresql(dot)org> |
Subject: | Race condition in GetOldestActiveTransactionId() |
Date: | 2016-08-22 10:46:08 |
Message-ID: | e7258662-82b6-7a45-56d4-99b337a32bf7@iki.fi |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
While hacking on the CSN patch, I spotted a race condition between
GetOldestActiveTransactionId() and GetNewTransactionId().
GetOldestActiveTransactionId() calculates the oldest XID that's still
running, by doing:
1. Read nextXid, without a lock. This is the upper bound, if there are
no active XIDs in the proc array.
2. Loop through the proc array, making note of the oldest XID.
While GetNewTransactionId() does:
1. Read and Increment nextXid
2. Set MyPgXact->xid.
It seems possible that if you call GetNewTransactionId() concurrently
with GetOldestActiveTransactionId(), GetOldestActiveTransactionId() sees
the new nextXid value that the concurrent GetNewTransactionId() set, but
doesn't see the old XID in the proc array. It will return a value that
doesn't cover the old XID, i.e. it won't consider the just-assigned XID
as in-progress.
Am I missing something? Commit c6d76d7c added a comment to
GetOldestActiveTransactionId() explaining why it's not necessary to
acquire XidGenLock there, but I think it missed the above race condition.
GetOldestActiveTransactionId() is not performance-critical, it's only
called when performing a checkpoint, so I think we should just bite the
bullet and grab the lock. Per attached patch.
- Heikki
Attachment | Content-Type | Size |
---|---|---|
fix-GetOldestActiveTransactionId-race.patch | application/x-patch | 993 bytes |
From | Date | Subject | |
---|---|---|---|
Next Message | Tom Lane | 2016-08-22 11:45:46 | Re: Patch: initdb: "'" for QUOTE_PATH (non-windows) |
Previous Message | Adrien Nayrat | 2016-08-22 10:12:58 | Re: LSN as a recovery target |