Report #6285
[bug\_fix] PostgreSQL out of shared memory or max\_locks\_per\_transaction exceeded when accessing many partitions
PostgreSQL pre-allocates shared memory for lock tracking. Each table, index, or partition touched by a transaction consumes a lock object. When accessing a partitioned table with thousands of partitions \(e.g., time-series data with daily partitions over years\), a single query can exceed max\_locks\_per\_transaction \(default 64\), causing "out of shared memory" or "too many relations" errors. The fix is to increase max\_locks\_per\_transaction in postgresql.conf \(e.g., to 128 or 256\) and restart PostgreSQL. If using predicate locks for SERIALIZABLE isolation, also increase max\_pred\_locks\_per\_transaction. For long-term solutions with very high partition counts \(10k\+\), ensure partition pruning works at planning time to avoid locking all partitions, or consider sub-partitioning or archiving old partitions.
Journey Context:
You manage a time-series database with declarative partitioning by day, creating 365 partitions per year. After 5 years, you have over 1800 partitions. A simple reporting query SELECT count\(\*\) FROM events WHERE event\_time > '2023-01-01' suddenly fails with "ERROR: out of shared memory". You check pg\_logs and see "HINT: You might need to increase max\_locks\_per\_transaction". You query pg\_locks and see locks on thousands of relation OIDs. You realize that because the planner needs to open each partition to check constraints for partition pruning, it's exhausting the pre-allocated lock table. The default max\_locks\_per\_transaction is 64, multiplied by max\_connections, but each transaction can only hold 64 locks before hitting the per-transaction limit. You edit postgresql.conf, setting max\_locks\_per\_transaction = 256. You restart PostgreSQL \(required for this parameter\). The query now executes successfully, acquiring locks on all necessary partitions without error. You then refactor the query to use a more specific time range and ensure constraint\_exclusion is properly configured so future queries don't unnecessarily touch all 1800 partitions. You also set max\_pred\_locks\_per\_transaction = 128 to support SERIALIZABLE transactions on partitioned tables.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T23:42:35.764888+00:00— report_created — created