This is an automated email from the ASF dual-hosted git repository.
bchapuis pushed a commit to branch optimize-postgres-tilestore
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git
The following commit(s) were added to refs/heads/optimize-postgres-tilestore by
this push:
new 00262643 Improve documentation and naming
00262643 is described below
commit 0026264385c0fe4381518e85bc05e798e9433e64
Author: Bertil Chapuis <[email protected]>
AuthorDate: Thu Oct 26 10:45:06 2023 +0200
Improve documentation and naming
---
.../tilestore/postgres/PostgresTileStore.java | 184 +++++++++++----------
.../tilestore/postgres/PostgresTileStoreTest.java | 2 +-
2 files changed, 101 insertions(+), 85 deletions(-)
diff --git
a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
index 2160a889..a1125033 100644
---
a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
+++
b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
@@ -19,7 +19,6 @@ package org.apache.baremaps.tilestore.postgres;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.sql.*;
@@ -37,7 +36,7 @@ import org.slf4j.LoggerFactory;
/**
* A read-only {@code TileStore} implementation that uses the PostgreSQL to
generate vector tiles.
* This {@code TileStore} combines the input queries, identifies common table
expressions (CTE), and
- * generates a single optimized query that hits the database.
+ * generates a single optimized sql that hits the database.
*/
public class PostgresTileStore implements TileStore {
@@ -47,155 +46,172 @@ public class PostgresTileStore implements TileStore {
private final Tileset tileset;
+ /**
+ * Constructs a {@code PostgresTileStore}.
+ *
+ * @param datasource the datasource
+ * @param tileset the tileset
+ */
public PostgresTileStore(DataSource datasource, Tileset tileset) {
this.datasource = datasource;
this.tileset = tileset;
}
- private Map<Integer, TileQuery> cache = new ConcurrentHashMap<>();
+ /**
+ * A cache of queries.
+ */
+ private Map<Integer, Query> cache = new ConcurrentHashMap<>();
+
+ /**
+ * A record that holds the sql of a prepared statement and the number of
parameters.
+ *
+ * @param sql
+ * @param parameters
+ */
+ protected record Query(String sql, int parameters) {
+ }
@Override
public ByteBuffer read(TileCoord tileCoord) throws TileStoreException {
+ var start = System.currentTimeMillis();
+
+ // Prepare and cache the query
var query = cache.computeIfAbsent(tileCoord.z(), z ->
prepareQuery(tileset, z));
- try (var connection = datasource.getConnection()) {
- return query.execute(connection, tileCoord);
+
+ // Fetch and compress the tile data
+ try (var connection = datasource.getConnection();
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ var statement = connection.prepareStatement(query.sql())) {
+
+ // Set the parameters for the tile
+ for (int i = 0; i < query.parameters(); i += 3) {
+ statement.setInt(i + 1, tileCoord.z());
+ statement.setInt(i + 2, tileCoord.x());
+ statement.setInt(i + 3, tileCoord.y());
+ }
+
+ try (ResultSet resultSet = statement.executeQuery();
+ OutputStream gzip = new GZIPOutputStream(data)) {
+ while (resultSet.next()) {
+ byte[] bytes = resultSet.getBytes(1);
+ gzip.write(bytes);
+ }
+ }
+
+ // Log slow queries (> 10s)
+ long stop = System.currentTimeMillis();
+ long duration = stop - start;
+ if (duration > 10_000) {
+ logger.warn("Executed sql for tile {} in {} ms", tileCoord, duration);
+ }
+
+ return ByteBuffer.wrap(data.toByteArray());
+
} catch (Exception e) {
throw new TileStoreException(e);
}
}
/**
- * This operation is not supported.
- */
- @Override
- public void write(TileCoord tileCoord, ByteBuffer blob) {
- throw new UnsupportedOperationException("The postgis tile store is read
only");
- }
-
- /**
- * This operation is not supported.
+ * Prepare the sql query for a given tileset and zoom level.
+ *
+ * @param tileset the tileset
+ * @param zoom the zoom level
+ * @return
*/
- @Override
- public void delete(TileCoord tileCoord) {
- throw new UnsupportedOperationException("The postgis tile store is read
only");
- }
-
-
- protected static TileQuery prepareQuery(Tileset tileset, int zoom) {
- // Initialize a builder for the tile query
- var tileQuery = new StringBuilder();
- tileQuery.append("SELECT (");
+ protected static Query prepareQuery(Tileset tileset, int zoom) {
+ // Initialize a builder for the tile sql
+ var tileSql = new StringBuilder();
+ tileSql.append("SELECT (");
// Iterate over the layers and keep track of the number of layers and
parameters included in the
- // final query
+ // final sql
var layers = tileset.getVectorLayers();
var layerCount = 0;
var paramCount = 0;
for (var layer : layers) {
- // Initialize a builder for the layer query
- var layerQuery = new StringBuilder();
+ // Initialize a builder for the layer sql
+ var layerSql = new StringBuilder();
var layerHead = "(WITH mvtGeom AS (";
- layerQuery.append(layerHead);
+ layerSql.append(layerHead);
// Iterate over the queries and keep track of the number of queries
included in the final
- // query
+ // sql
var queries = layer.getQueries();
var queryCount = 0;
for (var query : queries) {
- // Only include the query if the zoom level is in the range
+ // Only include the sql if the zoom level is in the range
if (query.getMinzoom() <= zoom && zoom < query.getMaxzoom()) {
// Add a union between queries
if (queryCount > 0) {
- layerQuery.append("UNION ");
+ layerSql.append("UNION ");
}
- // Add the query to the layer query
- var sql = query.getSql()
+ // Add the sql to the layer sql
+ var querySql = query.getSql()
.replace(";", "")
.replace("?", "??")
.replace("$zoom", String.valueOf(zoom));
- var queryWithParams = String.format(
+ var querySqlWithParams = String.format(
"SELECT ST_AsMVTGeom(t.geom, ST_TileEnvelope(?, ?, ?)) AS geom,
t.tags, t.id " +
"FROM (%s) AS t WHERE t.geom && ST_TileEnvelope(?, ?, ?,
margin => (64.0/4096))",
- sql);
- layerQuery.append(queryWithParams);
+ querySql);
+ layerSql.append(querySqlWithParams);
- // Increase the parameter count (e.g. ?) and query count
+ // Increase the parameter count (e.g. ?) and sql count
paramCount += 6;
queryCount++;
}
}
- // Add the tail of the layer query
+ // Add the tail of the layer sql
var layerQueryTail =
String.format(") SELECT ST_AsMVT(mvtGeom.*, '%s') FROM mvtGeom)",
layer.getId());
- layerQuery.append(layerQueryTail);
+ layerSql.append(layerQueryTail);
- // Only include the layer query if queries were included for this layer
+ // Only include the layer sql if queries were included for this layer
if (queryCount > 0) {
// Add the concatenation between layer queries
if (layerCount > 0) {
- tileQuery.append(" || ");
+ tileSql.append(" || ");
}
- // Add the layer query to the mvt query
- tileQuery.append(layerQuery);
+ // Add the layer sql to the mvt sql
+ tileSql.append(layerSql);
// Increase the layer count
layerCount++;
}
}
- // Add the tail of the tile query
+ // Add the tail of the tile sql
var tileQueryTail = ") mvtTile";
- tileQuery.append(tileQueryTail);
+ tileSql.append(tileQueryTail);
- // Log the resulting query
- var query = tileQuery.toString().replace("\n", " ");
- logger.debug("query: {}", query);
+ // Log the resulting sql
+ var sql = tileSql.toString().replace("\n", " ");
+ logger.debug("sql: {}", sql);
- return new TileQuery(query, paramCount);
+ return new Query(sql, paramCount);
}
- public record TileQuery(String query, int paramCount) {
-
- public ByteBuffer execute(Connection connection, TileCoord tileCoord)
- throws SQLException, IOException {
- long start = System.currentTimeMillis();
- try (var statement = connection.prepareStatement(query)) {
-
- // Set the parameters for the tile
- for (int i = 0; i < paramCount; i += 3) {
- statement.setInt(i + 1, tileCoord.z());
- statement.setInt(i + 2, tileCoord.x());
- statement.setInt(i + 3, tileCoord.y());
- }
-
- // Fetch and compress the tile data
- try (ByteArrayOutputStream data = new ByteArrayOutputStream();) {
- try (ResultSet resultSet = statement.executeQuery();
- OutputStream gzip = new GZIPOutputStream(data)) {
- while (resultSet.next()) {
- byte[] bytes = resultSet.getBytes(1);
- gzip.write(bytes);
- }
- }
- return ByteBuffer.wrap(data.toByteArray());
-
- } finally {
- // Log slow queries (> 10s)
- long stop = System.currentTimeMillis();
- long duration = stop - start;
- if (duration > 10_000) {
- logger.warn("Executed query for tile {} in {} ms", tileCoord,
duration);
- }
- }
- }
- }
+ /**
+ * This operation is not supported.
+ */
+ @Override
+ public void write(TileCoord tileCoord, ByteBuffer blob) {
+ throw new UnsupportedOperationException("The postgis tile store is read
only");
}
+ /**
+ * This operation is not supported.
+ */
+ @Override
+ public void delete(TileCoord tileCoord) {
+ throw new UnsupportedOperationException("The postgis tile store is read
only");
+ }
}
diff --git
a/baremaps-core/src/test/java/org/apache/baremaps/tilestore/postgres/PostgresTileStoreTest.java
b/baremaps-core/src/test/java/org/apache/baremaps/tilestore/postgres/PostgresTileStoreTest.java
index 2539d17a..f146a2cf 100644
---
a/baremaps-core/src/test/java/org/apache/baremaps/tilestore/postgres/PostgresTileStoreTest.java
+++
b/baremaps-core/src/test/java/org/apache/baremaps/tilestore/postgres/PostgresTileStoreTest.java
@@ -41,6 +41,6 @@ class PostgresTileStoreTest {
var query = PostgresTileStore.prepareQuery(tileset, 10);
assertEquals(
"SELECT ((WITH mvtGeom AS (SELECT ST_AsMVTGeom(t.geom,
ST_TileEnvelope(?, ?, ?)) AS geom, t.tags, t.id FROM (SELECT id, tags, geom
FROM table) AS t WHERE t.geom && ST_TileEnvelope(?, ?, ?, margin =>
(64.0/4096))) SELECT ST_AsMVT(mvtGeom.*, 'a') FROM mvtGeom) || (WITH mvtGeom AS
(SELECT ST_AsMVTGeom(t.geom, ST_TileEnvelope(?, ?, ?)) AS geom, t.tags, t.id
FROM (SELECT id, tags, geom FROM table) AS t WHERE t.geom && ST_TileEnvelope(?,
?, ?, margin => (64.0/4096))) SELECT ST_AsMVT(mv [...]
- query.query());
+ query.sql());
}
}