Hot standby, prepared xacts, locks

Поиск
Список
Период
Сортировка
От Heikki Linnakangas
Тема Hot standby, prepared xacts, locks
Дата
Msg-id 4ADF38C8.7080109@enterprisedb.com
обсуждение исходный текст
Ответы Re: Hot standby, prepared xacts, locks  (Simon Riggs <simon@2ndQuadrant.com>)
Список pgsql-hackers
The Hot Standby patch changes lock_twophase_recover() so that when we're
starting up from Hot Standby mode to normal operation, as opposed to
crash recovery, we assume that all AccessExcusiveLocks are already held
by the startup process and instead of acquiring them anew they are
transferred from the startup process to the dummy PGPROC entry.

As the patch stands, that works. However, if we don't see a
running-xacts record, but only some XLOG_RELATION_LOCK records followed
by a PREPARE record, the startup process will hold some of the
transactions locks but not all of them. That's OK because we're not
letting read-only backends in yet. If we then failover and bring the
standby server up as a new master, lock_twophase_recover() will grant
the locks directly for the dummy process. But the startup process is
already holding some of them, so we briefly have a situation where two
processes - the dummy process and the startup process - are both holding
the same AccessExclusiveLock. AFAICS, the lock manager can cope with
that situation just fine, even though it can't normally happen, but it's
worth a mention.

A while back in your branch you changed ProcArrayApplyRecoverInfo() so
that the standby is put into "pending" mode when it sees a running-xacts
record marked as 'locks overflowed', instead of just ignoring the
record. If we see such a record, go into pending mode, and then try to
bring up the standby as the new master, we will run into this error in
lock_twophase_recover():

>         /*
>          * We should already hold the desired lock.
>          */
>         if (!(proclock->holdMask & LOCKBIT_ON(lockmode)))
>             elog(ERROR, "lock %s on object %u/%u/%u by xid %u was not held by startup process",
>                  lockMethodTable->lockModeNames[lockmode],
>                  locktag->locktag_field1, locktag->locktag_field2,
>                  locktag->locktag_field3,
>                  xid);
> 

lock_twophase_recover() assumes that all locks held by the prepared
transactions are already held by the startup process, but that might not
be true in the pending state.

Given that even without that change in ProcArrayApplyRecoveryInfo(), we
can end up in a situation where both the dummy process and the startup
process hold the same AccessExclusiveLock, I think we could simply
revert all the changes in lock_twophase_recover() and rely on that
behavior whenever we end recovery.

BTW, this self-proclaimed ugly hack in lock_twophase_recover() looks
utterly broken:

>         /*
>          * Transfer the lock->procLocks entry so that the lock is now owned
>          * by the new gxact proc.  Nothing else need change.
>          * This can happen safely because the startup process has the lock
>          * and wishes to give the lock to a gxact dummy process that has
>          * no associated backend. So neither proc can change concurrently.
>          * This technique might normally be considered an ugly hack, yet
>          * managing dummy procs all the way through recovery resulted in
>          * more complex code, which was worse. Chiedere il permesso.
>          */
>         SHMQueueInit(&lock->procLocks);
>         SHMQueueInsertBefore(&lock->procLocks, &proclock->lockLink);
>         SHMQueueInsertBefore(&(proc->myProcLocks[partition]),
>                              &proclock->procLink);

It will blow away any PROCLOCKs belonging to other backends waiting for
the lock, and you can't just transfer a PROCLOCK entry from one backend
to another anyway, because the PGPROC pointer is part of its the hash
key. But then again, I don't think it's actually transferring the
startup process' PROCLOCK entry anyway, because the PROCLOCK entry
searched/created just above that belongs to the dummy proc, not the
startup process. So this is really just blowing away all other PROCLOCKs
from the lock's procLock queue, and possibly inserting a duplicate entry
into proc->myProcLocks[partition]. Ugh.

So, I'm quite eager to just revert all those lock_twophase_recover()
changes, and always rely on the "grant lock to dummy proc, then release
it in startup process" method. If we don't want to rely on that,
PostPrepare_Locks is an example of how to transfer lock ownership from
one process to another correctly.

--  Heikki Linnakangas EnterpriseDB   http://www.enterprisedb.com


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: Could regexp_matches be immutable?
Следующее
От: Samuel ROZE
Дата:
Сообщение: Re: URL Managment - C Function help