PostgreSQL orders lock requests as they arrive. When your ALTER TABLE asks for ACCESS EXCLUSIVE and cannot get it, it does not wait off to the side: it sits at the head of the queue, and every ordinary read and write that arrives afterwards stacks up behind it. RunxBuild's walkthrough of adding a column describes exactly this: a long-running query holds a lock, your ALTER waits, and trivial reads queue behind the ALTER until the slow query finishes. The table is effectively down for the duration, even though nothing is corrupt.
That inversion is the whole argument for timeouts. A migration that gives up after three seconds leaves traffic untouched and can be retried later during a quiet moment. A migration that waits indefinitely converts a routine schema change into an outage whose length you do not control. As postgres.ai's write-up on lock timeout and retries puts it, lock request ordering means a subsequent SELECT can hang purely because an earlier DDL is waiting for its ACCESS EXCLUSIVE lock. The demonstration uses three sessions and shows a plain read hanging while an ALTER TABLE ... SET NOT NULL waits on an idle-in-transaction session.
The severity depends on what your statement actually needs. ALTER TABLE ADD COLUMN takes ACCESS EXCLUSIVE and, for a nullable column or a constant default on modern versions, holds it for milliseconds regardless of table size (see our deep dive in the add column guide). CREATE INDEX without the concurrent option takes a SHARE lock that permits SELECTs but blocks INSERT, UPDATE and DELETE for the whole build, whereas CREATE INDEX CONCURRENTLY uses SHARE UPDATE EXCLUSIVE and lets writes continue, at the cost of a longer build and an invalid index left behind if it fails, as Bytebase's comparison explains. Different lock modes, different blast radius, one shared risk: the wait.



