Closed ghelle-fhh closed 9 months ago
@ghelle-fhh
A similar public API from NRW with a larger number of features (12.6 million buildings) does not have the same behavior (link). It takes a while, but the response is returned.
Or the OS Open Zoomstack API (2.7 million buildings).
The log message ("canceling statement due to user request") seems to indicate that the client canceled the request. If the result is a 502, it could be a timeout in some intermediate component, e.g. the reverse proxy?
Interesting, I also add
When I test the request in docker on my local machine with no reverse proxy I receive a 500 response and the same message as before "canceling statement due to user request".
Maybe this is messaged is triggered by the httpClient setting in the cfg.yml file? I increased the value to 300000ms and also configured simplified geometry but I still experience the same behavior and receive the same log message in a local docker environment (albeit now I receive a 500 error, not 502)
DEBUG [2024-01-26 09:56:15,966] solarpotenzialanalyse - Filter: Or{args=[SIntersects{args=[Property{name=geom, nestedFilters={}, path=[geom]}, SpatialLiteral{value=Envelope{crs=EpsgCrs{code=4326, forceAxisOrder=LON_LAT}, coordinates=[9.0, 53.0, 11.0, 54.0]}, type=interface de.ii.xtraplatform.cql.domain.Geometry}], spatialOperator=S_INTERSECTS, op=s_intersects}, IsNull{args=[Property{name=geom, nestedFilters={}, path=[geom]}], op=isNull}], op=or} [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 09:56:15,967] solarpotenzialanalyse - FeatureQuery: FeatureQuery{type=gebaeude, filters=[Or{args=[SIntersects{args=[Property{name=geom, nestedFilters={}, path=[geom]}, SpatialLiteral{value=Envelope{crs=EpsgCrs{code=4326, forceAxisOrder=LON_LAT}, coordinates=[9.0, 53.0, 11.0, 54.0]}, type=interface de.ii.xtraplatform.cql.domain.Geometry}], spatialOperator=S_INTERSECTS, op=s_intersects}, IsNull{args=[Property{name=geom, nestedFilters={}, path=[geom]}], op=isNull}], op=or}], sortKeys=[], fields=[], skipGeometry=false, crs=EpsgCrs{code=4326, forceAxisOrder=LON_LAT}, maxAllowableOffset=0.0, geometryPrecision=[0, 0, 0], limit=10, offset=0, hitsOnly=false, returnsSingleFeature=false, extensions=[], schemaScope=RETURNABLE} [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 09:56:16,027] solarpotenzialanalyse - acceptable: [text/html] [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 09:56:16,027] solarpotenzialanalyse - provided: [text/html, application/geo+json, application/vnd.policy.attributes] [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 09:56:16,027] solarpotenzialanalyse - selected: text/html [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 09:56:17,501] - Chosen operation for EPSG:4326 (LON_LAT) -> EPSG:25832: axis order change (2D) + Inverse of ETRS89 to WGS 84 (1) + UTM zone 32N ERROR [2024-01-26 10:06:22,605] solarpotenzialanalyse - Unexpected SQL query error: ERROR: canceling statement due to user request [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 10:06:22,611] solarpotenzialanalyse - accept text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/*;q=0.8 [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 10:06:22,613] solarpotenzialanalyse - content-type Optional[ApiMediaType{type=text/html, label=HTML, parameter=html, fileExtension=html, qs=1000}] [b2362d4f-5861-4620-a9ed-3e31fd8d8588] ERROR [2024-01-26 10:06:22,615] solarpotenzialanalyse - Server Error with ID 58ffc64b7a84b7ae: Unexpected SQL query error: ERROR: canceling statement due to user request [b2362d4f-5861-4620-a9ed-3e31fd8d8588] DEBUG [2024-01-26 10:06:22,616] solarpotenzialanalyse - Sending response: 500 Internal Server Error [b2362d4f-5861-4620-a9ed-3e31fd8d8588]
Would it be possible to get a DB dump and the API configuration to see, if we can reproduce the response?
Yes that is not a problem, what would be the best way to provide you with the files? The ZIP file is approx. 150mb large
I will send you an email with an upload link...
@ghelle-fhh
I was able to reproduce the problem. The reason for the bad performance - and eventually the error - is the following requirement from Features Part 1:
The bbox parameter SHALL match all features in the collection that are not associated with a spatial geometry, too.
This means that the DB query selects all rows where ST_Intersects(geom, ST_GeomFromText('POLYGON((...))')) OR geom is NULL
. The IS NULL
condition results in a full table scan. To avoid this, the indexes would need to be optimized for such queries. Maybe also adding NOT NULL
on the geom
column helps, but that did not seem to be the case in a quick test. It did not change the EXPLAIN
results.
So, this is not a bug, but nevertheless it would be good to be able to avoid the IS NULL
for cases where just the standard geometry indexes are present and the geometry is never NULL
, that is, where the IS NULL
simply adds a very large overhead without any effect.
I will create a PR that drops the IS NULL
, if the primary geometry property is marked as required in the schema, because it is not needed.
geom:
sourcePath: geom
type: GEOMETRY
...
constraints:
required: true
I tested it and then the response is returned immediately.
The same applies for the datetime
parameter and the primary instant property, so I will apply the same changes for that parameter in the PR, too.
Thank you very much! That should solve the issue, for curiosity's sake could you perhaps explain this part further?
To avoid this, the indexes would need to be optimized for such queries
Do you have any concrete optimization suggestions? I assume these optimizations are implemented on the working examples you referenced.
I do not have concrete optimization recommendations for indexes in this case. I assume that the query planning can be improved for such cases with indexes for this type of expressions; e.g., using partial indexes for null values. But I am not an expert and do not have concrete recommendations.
One general optimization suggestion for large datasets is, however, to skip computing the number of matched features in a query. This can become quite costly. For this reason, it is optional to return this information in the standard. To disable returning numberMatched
, add the following to the provider:
queryGeneration:
computeNumberMatched: false
This option is also used in the APIs that I had linked to and this is likely the reason why they did not time out.
To give an idea, I analyzed query plans for a query where the bbox
covers all dachseiten features - using the existing, standard GIST index on the geometry.
numberMatched
with the IS NULL
test: cost=4030543044776.87numberReturned
with the IS NULL
test: cost=61594695.66numberMatched
without the IS NULL
test: cost=9297798.17numberReturned
without the IS NULL
test: cost=252.27So, it can make sense to disable numberMatched
also with the geometry property set to required (that is, without the IS NULL
test). This depends on how important that information is for users of the API and what type of queries are typically used with the API.
ldproxy Version
3.5.0
Current Behavior
When requesting a relatively small bounding box and limit set to 10 the requested features are returned with relatively good performance.
https://api.hamburg.de/datasets/v1/alkis_vereinfacht/collections/GebaeudeBauwerk/items?bbox=10.0130%2C53.3716%2C10.5315%2C53.6096&limit=10
However when the bbox is set to, for example, the whole of Hamburg, the request fails with a 502 error, even with the limit parameter set: https://api.hamburg.de/datasets/v1/alkis_vereinfacht/collections/GebaeudeBauwerk/items?limit=10&bbox=9.2191%2C53.4365%2C10.4335%2C53.7864
This behavior is also consistent with another dataset "Solarpotenzialanalyse" (see relevant log output)
Expected Behavior
I would expect to at least receive the amount of features specified in the limit parameter within the bounding box and not a 502 error
Steps to Reproduce
Relevant log output