Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add caching of address information for Nominatim export #850

Merged
merged 17 commits into from
Nov 15, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add transaction manager and go back to manual commit
With autocommit, server-side cursors don't work, slowing down the
large queries.

Without autocommit without a transaction manager, all queries will be
rolled back. So protect at least the writing queries.
lonvia committed Nov 12, 2024
commit 6b4dd50a8be3128ec6b68f89b2678456606ce670
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import java.sql.ResultSet;
import java.sql.SQLException;
@@ -17,6 +19,7 @@
public class NominatimConnector {
protected final DBDataAdapter dbutils;
protected final JdbcTemplate template;
protected final TransactionTemplate txTemplate;
protected Map<String, Map<String, String>> countryNames;
protected final boolean hasNewStyleInterpolation;

@@ -28,7 +31,11 @@ protected NominatimConnector(String host, int port, String database, String user
if (password != null) {
dataSource.setPassword(password);
}
dataSource.setDefaultAutoCommit(true);

// Keep disabled or server-side cursors won't work.
dataSource.setDefaultAutoCommit(false);

txTemplate = new TransactionTemplate(new DataSourceTransactionManager(dataSource));

template = new JdbcTemplate(dataSource);
template.setFetchSize(100000);
26 changes: 16 additions & 10 deletions src/main/java/de/komoot/photon/nominatim/NominatimImporter.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package de.komoot.photon.nominatim;

import de.komoot.photon.PhotonDoc;
import de.komoot.photon.nominatim.model.*;
import de.komoot.photon.nominatim.model.AddressRow;
import de.komoot.photon.nominatim.model.NominatimAddressCache;
import de.komoot.photon.nominatim.model.OsmlineRowMapper;
import de.komoot.photon.nominatim.model.PlaceRowMapper;
import org.locationtech.jts.geom.Geometry;
import org.slf4j.Logger;

import java.sql.Types;
import java.util.List;
import java.util.Map;

/**
@@ -182,14 +184,18 @@ public void readCountry(String countryCode, ImportThread importThread) {
* not will create them. This may take a while.
*/
public void prepareDatabase() {
Integer indexRowNum = template.queryForObject(
"SELECT count(*) FROM pg_indexes WHERE tablename = 'placex' AND indexdef LIKE '%(country_code)'",
Integer.class);

if (indexRowNum == null || indexRowNum == 0) {
LOGGER.info("Creating index over countries.");
template.execute("CREATE INDEX ON placex (country_code)");
}
txTemplate.execute(status -> {
Integer indexRowNum = template.queryForObject(
"SELECT count(*) FROM pg_indexes WHERE tablename = 'placex' AND indexdef LIKE '%(country_code)'",
Integer.class);

if (indexRowNum == null || indexRowNum == 0) {
LOGGER.info("Creating index over countries.");
template.execute("CREATE INDEX ON placex (country_code)");
}

return 0;
});
}

public String[] getCountriesFromDatabase() {
53 changes: 31 additions & 22 deletions src/main/java/de/komoot/photon/nominatim/NominatimUpdater.java
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
import de.komoot.photon.nominatim.model.*;
import org.locationtech.jts.geom.Geometry;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
@@ -138,8 +140,13 @@ public void setUpdater(Updater updater) {

public void initUpdates(String updateUser) {
LOGGER.info("Creating tracking tables");
template.execute(TRIGGER_SQL);
template.execute("GRANT SELECT, DELETE ON photon_updates TO \"" + updateUser + '"');
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
template.execute(TRIGGER_SQL);
template.execute("GRANT SELECT, DELETE ON photon_updates TO \"" + updateUser + '"');
}
});
}

public void update() {
@@ -230,28 +237,30 @@ private void updateFromInterpolations() {
}

private List<UpdateRow> getPlaces(String table) {
List<UpdateRow> results = template.query(dbutils.deleteReturning(
"DELETE FROM photon_updates WHERE rel = ?", "place_id, operation, indexed_date"),
(rs, rowNum) -> {
boolean isDelete = "DELETE".equals(rs.getString("operation"));
return new UpdateRow(rs.getLong("place_id"), isDelete, rs.getTimestamp("indexed_date"));
}, table);

// For each place only keep the newest item.
// Order doesn't really matter because updates of each place are independent now.
results.sort(Comparator.comparing(UpdateRow::getPlaceId).thenComparing(
Comparator.comparing(UpdateRow::getUpdateDate).reversed()));

ArrayList<UpdateRow> todo = new ArrayList<>();
long prevId = -1;
for (UpdateRow row: results) {
if (row.getPlaceId() != prevId) {
prevId = row.getPlaceId();
todo.add(row);
return txTemplate.execute(status -> {
List<UpdateRow> results = template.query(dbutils.deleteReturning(
"DELETE FROM photon_updates WHERE rel = ?", "place_id, operation, indexed_date"),
(rs, rowNum) -> {
boolean isDelete = "DELETE".equals(rs.getString("operation"));
return new UpdateRow(rs.getLong("place_id"), isDelete, rs.getTimestamp("indexed_date"));
}, table);

// For each place only keep the newest item.
// Order doesn't really matter because updates of each place are independent now.
results.sort(Comparator.comparing(UpdateRow::getPlaceId).thenComparing(
Comparator.comparing(UpdateRow::getUpdateDate).reversed()));

ArrayList<UpdateRow> todo = new ArrayList<>();
long prevId = -1;
for (UpdateRow row : results) {
if (row.getPlaceId() != prevId) {
prevId = row.getPlaceId();
todo.add(row);
}
}
}

return todo;
return todo;
});
}


Original file line number Diff line number Diff line change
@@ -16,15 +16,18 @@
import java.util.Date;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.transaction.support.TransactionTemplate;

class NominatimConnectorDBTest {
private EmbeddedDatabase db;
private NominatimImporter connector;
private CollectingImporter importer;
private JdbcTemplate jdbc;
private TransactionTemplate txTemplate;

@BeforeEach
void setup() {
@@ -39,7 +42,9 @@ void setup() {
importer = new CollectingImporter();

jdbc = new JdbcTemplate(db);
txTemplate = new TransactionTemplate(new DataSourceTransactionManager(db));
ReflectionTestUtil.setFieldValue(connector, NominatimConnector.class, "template", jdbc);
ReflectionTestUtil.setFieldValue(connector, NominatimConnector.class, "txTemplate", txTemplate);
}

private void readEntireDatabase() {
Original file line number Diff line number Diff line change
@@ -5,9 +5,11 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.transaction.support.TransactionTemplate;

import static org.junit.jupiter.api.Assertions.*;

@@ -16,6 +18,7 @@ class NominatimUpdaterDBTest {
private NominatimUpdater connector;
private CollectingUpdater updater;
private JdbcTemplate jdbc;
private TransactionTemplate txTemplate;

@BeforeEach
void setup() {
@@ -31,7 +34,9 @@ void setup() {
connector.setUpdater(updater);

jdbc = new JdbcTemplate(db);
txTemplate = new TransactionTemplate(new DataSourceTransactionManager(db));
ReflectionTestUtil.setFieldValue(connector, NominatimConnector.class, "template", jdbc);
ReflectionTestUtil.setFieldValue(connector, NominatimConnector.class, "txTemplate", txTemplate);
}

@Test