I have PostgreSql 15.3 running as a docker container.
My docker run configuration is
-m 512g --memory-swap 512g --shm-size=16g
Using this configuration, I loaded 36B rows, taking up about 30T between tables and indexes.
I loaded with about 24 parallel operations, and the pg driver was using connection pooling, so there were about 150 concurrent connections but not all active.
My postgresql.conf was set to
max_connections = 512
max_locks_per_transaction = 1024
All of this ran without any problem.
The 36B row table is sub-partitioned. The layer with actual data has about 80,000 tables.
Parent, partitioned by category, each partitioned by year, each partitioned by month, each partitioned by sensor id, which is the 80K tables of data.
My problem is that for the life of me, I cannot perform a simple COUNT(*)
on the top level table without the
out of shared memory, you might need to increase max_locks_per_connection
. In fact, I can count from a single table and from the month partition, but not from the year partition, which is only about 10K tables.
Reading that I need the lock table to hold max_connection * max_locks in shared memory, I’ve tried lowering the max_connections, and increasing both max_locks and shared memory but without success.
Currently I am at
-m 512g --memory-swap 512g --shm-size=64g
for the container and
max_connections = 8
max_locks_per_transaction = 163840
in the config, having gone by increments of course.
I do have a second table partitioned the same way, much smaller volume of data, but also 80K tables with data.
From what I’ve read, something like
max_connections = 100
max_locks_per_transaction = 1600
should cover 160K tables, and if each lock takes 168 bytes, the shared memory needs only at least 26M.
Any suggestions on what I should be modifying and how I should be calculating the target values?
2
Answers
The final answer is indeed "you have too many partitions".
HOWEVER, the actual solution to my specific question lies in the behavior of the PostgreSql docker image and specifically it seems to ignore
max_locks_per_transaction
if modifed in a config file attached to the container. It needs to be passed into thedocker run
command line as a-c
option instead.Thanks @jjanes for suggesting verifying the values were read.
When you run a postgres container, you can attach your own config file:
Of course, you usually need to stop and restart the container to modify the configuration setting. If you modify the config file with
max_connections = 333
(some non-default number),start the container, and issue
Now do the same with
max_locks_per_transaction
and for thepostgres:15.3
image no matter what you set it to you getThis is hinted at in Cant change max_locks in docker and at Change max_locks for github actions
The configuration seems to be respected only if passed in on the
docker run
command lineOnce I did this, and set
max_locks_per_transaction
to a ludicrous number (500,000), I was able to issue a count against the next higher partition level.That said, the second solution to my question lies in the detail that most discussions around this very issue seem to not mention.
max_locks_per_transaction * max_connections
needs to cover not just the number of all partitioned tables, but also each and every index on them. Thanks @jjanes again.In my case, each table has about 10 indexes. A query against one table needed 11 locks. Against the month partition needed 21,000 locks, and against the year partition needed 218,000 locks. Clearly unsustainable.
The sobering fact is that PostgreSql partitioning does not scale horizontally as comfortably as many other features and may not lend itself well to matching the logical organization of your business domain.
Having a ridiculous number of partitions is going to require a ridiculous number of locks. The number of
max_connections * max_locks_per_connection
you show should be able to handle 80000 tables in a simple case of unindexed tables with flat partitions, but indexes also need to be locked during planning even if they don’t end up being used in the execution. The deep partitioning structure will also require some more locks, but without a test script to reproduce your exact structure I haven’t tested to see how many it would need.Handling partitions gets more and more unwieldy the more partitions you have. That is going to put a practical limit on the number of partitions you can have long before you reach a hard limit. I would say you are already over the practical limit.
These problems are just internal to PostgreSQL. If you were hitting memory problems at the OS/VM level, I would expect those to fail with different error messages than the one you see.
The locking issues should be the same whether the tables are populated or just exist but empty, so if you need to test things it should be easy to do just be exporting the structure without the data (like
pg_dump -s
).