Avoid O(N^2) behavior when the standby process releases many locks.
authorTom Lane
Sun, 31 Oct 2021 19:31:29 +0000 (15:31 -0400)
committerTom Lane
Sun, 31 Oct 2021 19:31:38 +0000 (15:31 -0400)
When replaying a transaction that held many exclusive locks on the
primary, a standby server's startup process would expend O(N^2)
effort on manipulating the list of locks.  This code was fine when
written, but commit 1cff1b95a made repetitive list_delete_first()
calls inefficient, as explained in its commit message.  Fix by just
iterating the list normally, and releasing storage only when done.
(This'd be inadequate if we needed to recover from an error occurring
partway through; but we don't.)

Back-patch to v13 where 1cff1b95a came in.

Nathan Bossart

Discussion: https://postgr.es/m/CD2F0E7F-9822-45EC-A411-AE56F14DEA9F@amazon.com

src/backend/storage/ipc/standby.c

index aeecaf6cabf5b84e5e191ef0231b71aef57c6964..a3ceec88a1538ec9b8ff00f4ae9fb2f2f5a64271 100644 (file)
@@ -986,9 +986,11 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
 static void
 StandbyReleaseLockList(List *locks)
 {
-   while (locks)
+   ListCell   *lc;
+
+   foreach(lc, locks)
    {
-       xl_standby_lock *lock = (xl_standby_lock *) linitial(locks);
+       xl_standby_lock *lock = (xl_standby_lock *) lfirst(lc);
        LOCKTAG     locktag;
 
        elog(trace_recovery(DEBUG4),
@@ -1002,9 +1004,9 @@ StandbyReleaseLockList(List *locks)
                 lock->xid, lock->dbOid, lock->relOid);
            Assert(false);
        }
-       pfree(lock);
-       locks = list_delete_first(locks);
    }
+
+   list_free_deep(locks);
 }
 
 static void