From: | Marco Boeringa <marco(at)boeringa(dot)demon(dot)nl> |
---|---|
To: | Andres Freund <andres(at)anarazel(dot)de> |
Cc: | pgsql-bugs(at)lists(dot)postgresql(dot)org, Thom Brown <thom(at)linux(dot)com> |
Subject: | Re: Potential "AIO / io workers" inter-worker locking issue in PG18? |
Date: | 2025-10-08 20:09:04 |
Message-ID: | 6f4d0f00-27e8-44ea-bb2f-70aa636c6d09@boeringa.demon.nl |
Views: | Whole Thread | Raw Message | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-bugs |
Hi Andres, > Even just knowing whether the "normal query plan" is the
same one as we see in > profiles of "stuck" backends is valuable. Even
if the query plan is perfectly > normal, it *still* is very important to
know in which order the joins are > evaluated etc. But there also might
be changes in the query plan between 17 > and 18 that trigger the
issue... > > Without more details about what is expected to be run and
what is actually > happening, it's just about impossible for us to debug
this without a > reproducer that we can run and debug ourselves. I now
encountered the auto_explain option in the PostgreSQL help. May sound
stupid, but I hadn't been aware of this option. This might help in
getting an explain during the actual execution of my tool, if I
understand the option properly. This would be far more valuable - as
being the "real" thing - than some contrived reproduction case. I will
need to investigate this a bit more:
https://www.postgresql.org/docs/current/auto-explain.html >>> Making
vacuum more aggressive won't really help much if you have >> longrunning
queries/sessions, since vacuum can't clean up row versions that >> are
still visibile to some of the transactions. >> >> My code batches the
updates in sets of 2000 records at a time and then >> COMMITs, so the
transactions themselves are limited in time and size, which >> should
allow vacuum to do its job. > > Are the "stuck" backends stuck within
one 2000 record batch, or are they > "just" slower processing each
batch? I can't tell. But to explain: each thread has its own set of jobs
assigned, and each job will be batched in sets of 2000 records until
COMMIT. So if one job has 100k records to process, 50 commits should
occur for that job by one Python thread. I take care to avoid to process
records totally randomly, which could cause conflicts and locking issues
between threads attempting to access the same locked database page,
significantly slowing down the processing. Records are assigned by
database page (and depending on some other parameters), which has worked
really well so far. Note that this is just a simplified version of the
different processing modes I developed for different challenges and
geoprocessing steps. >> 26.48% postgres postgres [.] LWLockAttemptLock
>> | >> ---LWLockAttemptLock >> | >>
|--23.15%--heapam_index_fetch_tuple.lto_priv.0 >> | index_fetch_heap >>
| IndexNext >> | ExecScan >> | ExecNestLoop >> | ExecNestLoop >> |
ExecModifyTable >> | standard_ExecutorRun >> | ProcessQuery > > So the
query plan we have is a nested loop between at least three tables >
(there are two joins, c.f. the two ExecNestLoop calls), where there
presumably > are a lot of row [versions] on the inner side of the
innermost join. > > In [1] you showed a query. Reformated that looks
like this: > > UPDATE osm.landcover_scrubs_small_scale_2_ply AS t1 > SET
area_geo = t2.area_geo, > perim_geo = t2.perim_geo, > compact_geo = CASE
WHEN t2.area_geo > 0 THEN ((power(t2.perim_geo,2) / t2.area_geo) / (4 *
pi())) ELSE 0 END, > npoints_geo = t2.npoints_geo, > comp_npoints_geo =
CASE WHEN t2.npoints_geo > 0 THEN (CASE WHEN t2.area_geo > 0 THEN
((power(t2.perim_geo,2) / t2.area_geo) / (4 * pi())) ELSE 0 END /
t2.npoints_geo) ELSE 0 END, > convex_ratio_geo = CASE WHEN
ST_Area(ST_ConvexHull(way)::geography,true) > 0 THEN (t2.area_geo /
ST_Area(ST_ConvexHull(way)::geography,true)) ELSE 1 END > FROM ( >
SELECT > objectid, > ST_Area(way::geography,true) AS area_geo, >
ST_Perimeter(way::geography,true) AS perim_geo, > ST_NPoints(way) AS
npoints_geo > FROM osm.landcover_scrubs_small_scale_2_ply) AS t2 > WHERE
(t2.objectid = t1.objectid) > AND t1.objectid IN (SELECT t3.objectid
FROM mini_test.osm.osm_tmp_28128_ch5 AS t3) > > > Which certainly fits
with two nested loops, although I don't think I can infer > which order
it the joins are in. > > > Is
osm.landcover_scrubs_small_scale_2_ply.object_id unique? Yes. > Can
there be multiple rows for one object_id in >
mini_test.osm.osm_tmp_28128_ch5? No. This table contains the records to
process, which are unique. It is the job.
It is a one-to-one join.
> Are there indexes on mini_test.osm.osm_tmp_28128_ch5.unique_id and > osm.landcover_scrubs_small_scale_2_ply? Yes, the unique ids /
objectid fields are indexed to allow an efficient join.
From | Date | Subject | |
---|---|---|---|
Next Message | Marco Boeringa | 2025-10-08 20:22:48 | Re: Potential "AIO / io workers" inter-worker locking issue in PG18? |
Previous Message | Andres Freund | 2025-10-08 19:08:28 | Re: Potential "AIO / io workers" inter-worker locking issue in PG18? |