Skip to content

Commit fa8336d

Browse files
committed
Find way dependencies faster by doing a single lookup
For each changed way osm2pgsql has to find all relations referencing that way and make sure they are updated as well. This was done by doing one SQL query per way which is quite inefficient. Instead the new code collects all way ids and does only one query for the parent relations after all ways have been read. This commit also makes sure that ways which have been in the input file are removed from the pending way tracker so that they are not processed again later.
1 parent ea7858a commit fa8336d

File tree

9 files changed

+137
-49
lines changed

9 files changed

+137
-49
lines changed

src/dependency-manager.cpp

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,7 @@ void full_dependency_manager_t::node_changed(osmid_t id)
2020

2121
void full_dependency_manager_t::way_changed(osmid_t id)
2222
{
23-
if (m_ways_pending_tracker.get(id)) {
24-
return;
25-
}
26-
27-
for (auto const rel_id : m_object_store->get_rels_by_way(id)) {
28-
m_rels_pending_tracker.set(rel_id);
29-
}
23+
m_changed_ways.set(id);
3024
}
3125

3226
void full_dependency_manager_t::after_nodes()
@@ -38,12 +32,55 @@ void full_dependency_manager_t::after_nodes()
3832
m_object_store->get_node_parents(m_changed_nodes, &m_ways_pending_tracker,
3933
&m_rels_pending_tracker);
4034
m_changed_nodes.clear();
35+
}
36+
37+
static osmium::index::IdSetSmall<osmid_t>
38+
set_diff(osmium::index::IdSetSmall<osmid_t> const &set,
39+
osmium::index::IdSetSmall<osmid_t> const &to_be_removed)
40+
{
41+
osmium::index::IdSetSmall<osmid_t> new_set;
42+
43+
for (auto const id : set) {
44+
if (!to_be_removed.get_binary_search(id)) {
45+
new_set.set(id);
46+
}
47+
}
48+
49+
return new_set;
50+
}
51+
52+
void full_dependency_manager_t::after_ways()
53+
{
54+
if (!m_changed_ways.empty()) {
55+
if (!m_ways_pending_tracker.empty()) {
56+
// Remove ids from changed ways in the input data from
57+
// m_ways_pending_tracker, because they have already been processed.
58+
m_ways_pending_tracker =
59+
set_diff(m_ways_pending_tracker, m_changed_ways);
4160

42-
for (auto const way_id : m_ways_pending_tracker) {
43-
for (auto const rel_id : m_object_store->get_rels_by_way(way_id)) {
44-
m_rels_pending_tracker.set(rel_id);
61+
// Add the list of pending way ids to the list of changed ways,
62+
// because we need the parents for them, too.
63+
m_changed_ways.merge_sorted(m_ways_pending_tracker);
4564
}
65+
66+
m_object_store->get_way_parents(m_changed_ways,
67+
&m_rels_pending_tracker);
68+
69+
m_changed_ways.clear();
70+
return;
4671
}
72+
73+
if (!m_ways_pending_tracker.empty()) {
74+
m_object_store->get_way_parents(m_ways_pending_tracker,
75+
&m_rels_pending_tracker);
76+
}
77+
}
78+
79+
void full_dependency_manager_t::mark_parent_relations_as_pending(
80+
osmium::index::IdSetSmall<osmid_t> const &way_ids)
81+
{
82+
assert(m_rels_pending_tracker.empty());
83+
m_object_store->get_way_parents(way_ids, &m_rels_pending_tracker);
4784
}
4885

4986
bool full_dependency_manager_t::has_pending() const noexcept

src/dependency-manager.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ class dependency_manager_t
5353
virtual void way_changed(osmid_t) {}
5454

5555
virtual void after_nodes() {}
56+
virtual void after_ways() {}
57+
58+
virtual void mark_parent_relations_as_pending(
59+
osmium::index::IdSetSmall<osmid_t> const & /*way_ids*/)
60+
{
61+
}
5662

5763
/// Are there pending objects that need to be processed?
5864
virtual bool has_pending() const noexcept { return false; }
@@ -98,6 +104,10 @@ class full_dependency_manager_t : public dependency_manager_t
98104
void way_changed(osmid_t id) override;
99105

100106
void after_nodes() override;
107+
void after_ways() override;
108+
109+
void mark_parent_relations_as_pending(
110+
osmium::index::IdSetSmall<osmid_t> const &ids) override;
101111

102112
bool has_pending() const noexcept override;
103113

@@ -126,6 +136,15 @@ class full_dependency_manager_t : public dependency_manager_t
126136
*/
127137
osmium::index::IdSetSmall<osmid_t> m_changed_nodes;
128138

139+
/**
140+
* In append mode all new and changed ways will be added to this. After
141+
* all ways are read this is used to figure out which parent relations
142+
* reference these ways. Deleted ways are not stored in here, because all
143+
* relations that referenced deleted ways must be in the change file, too,
144+
* and so we don't have to find out which ones they are.
145+
*/
146+
osmium::index::IdSetSmall<osmid_t> m_changed_ways;
147+
129148
osmium::index::IdSetSmall<osmid_t> m_ways_pending_tracker;
130149
osmium::index::IdSetSmall<osmid_t> m_rels_pending_tracker;
131150
};

src/middle-pgsql.cpp

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,6 @@ middle_pgsql_t::table_desc::table_desc(options_t const &options,
127127
m_copy_target->id = "id";
128128

129129
if (options.with_forward_dependencies) {
130-
m_prepare_fw_dep_lookups =
131-
build_sql(options, ts.prepare_fw_dep_lookups);
132130
m_create_fw_dep_indexes = build_sql(options, ts.create_fw_dep_indexes);
133131
}
134132
}
@@ -859,9 +857,57 @@ INSERT INTO osm2pgsql_changed_relations
859857
parent_ways->size(), parent_relations->size());
860858
}
861859

862-
idlist_t middle_pgsql_t::get_rels_by_way(osmid_t osm_id)
860+
void middle_pgsql_t::get_way_parents(
861+
osmium::index::IdSetSmall<osmid_t> const &changed_ways,
862+
osmium::index::IdSetSmall<osmid_t> *parent_relations) const
863863
{
864-
return get_ids_from_db(&m_db_connection, "mark_rels_by_way", osm_id);
864+
util::timer_t timer;
865+
866+
auto const num_relations_referenced_by_nodes = parent_relations->size();
867+
868+
m_db_connection.exec("BEGIN");
869+
m_db_connection.exec("CREATE TEMP TABLE osm2pgsql_changed_ways"
870+
" (id int8 NOT NULL) ON COMMIT DROP");
871+
m_db_connection.exec("CREATE TEMP TABLE osm2pgsql_changed_relations"
872+
" (id int8 NOT NULL) ON COMMIT DROP");
873+
874+
send_id_list(m_db_connection, "osm2pgsql_changed_ways", changed_ways);
875+
876+
m_db_connection.exec("ANALYZE osm2pgsql_changed_ways");
877+
878+
if (m_options->middle_database_format == 1) {
879+
m_db_connection.exec(build_sql(*m_options, R"(
880+
INSERT INTO osm2pgsql_changed_relations
881+
SELECT DISTINCT r.id
882+
FROM {schema}"{prefix}_rels" r, osm2pgsql_changed_ways w
883+
WHERE r.parts && ARRAY[w.id]
884+
AND r.parts[way_off+1:rel_off] && ARRAY[w.id]
885+
)"));
886+
} else {
887+
m_db_connection.exec(build_sql(*m_options, R"(
888+
INSERT INTO osm2pgsql_changed_relations
889+
SELECT DISTINCT r.id
890+
FROM {schema}"{prefix}_rels" r, osm2pgsql_changed_ways c
891+
WHERE {schema}"{prefix}_member_ids"(r.members, 'W'::char) && ARRAY[c.id];
892+
)"));
893+
}
894+
895+
load_id_list(m_db_connection, "osm2pgsql_changed_relations",
896+
parent_relations);
897+
898+
m_db_connection.exec("COMMIT");
899+
900+
timer.stop();
901+
log_debug("Found {} ways that are new/changed in input or parent of"
902+
" changed node.",
903+
changed_ways.size());
904+
log_debug(" Found in {} their {} parent relations.",
905+
std::chrono::duration_cast<std::chrono::seconds>(timer.elapsed()),
906+
parent_relations->size() - num_relations_referenced_by_nodes);
907+
908+
// (Potentially) contains parent relations from nodes and from ways. Make
909+
// sure they are merged.
910+
parent_relations->sort_unique();
865911
}
866912

867913
void middle_pgsql_t::way_set(osmium::Way const &way)
@@ -1223,13 +1269,6 @@ void middle_pgsql_t::start()
12231269
// problems when accessing the intarrays.
12241270
m_db_connection.set_config("jit_above_cost", "-1");
12251271
m_db_connection.set_config("max_parallel_workers_per_gather", "0");
1226-
1227-
// Prepare queries for updating dependent objects
1228-
for (auto &table : m_tables) {
1229-
if (!table.m_prepare_fw_dep_lookups.empty()) {
1230-
m_db_connection.exec(table.m_prepare_fw_dep_lookups);
1231-
}
1232-
}
12331272
} else {
12341273
if (m_store_options.db_format == 2) {
12351274
table_setup(m_db_connection, m_users_table);
@@ -1488,12 +1527,6 @@ static table_sql sql_for_relations_format1()
14881527
" SELECT members, tags"
14891528
" FROM {schema}\"{prefix}_rels\" WHERE id = $1;\n";
14901529

1491-
sql.prepare_fw_dep_lookups =
1492-
"PREPARE mark_rels_by_way(int8) AS"
1493-
" SELECT id FROM {schema}\"{prefix}_rels\""
1494-
" WHERE parts && ARRAY[$1]"
1495-
" AND parts[way_off+1:rel_off] && ARRAY[$1];\n";
1496-
14971530
sql.create_fw_dep_indexes =
14981531
"CREATE INDEX ON {schema}\"{prefix}_rels\" USING GIN (parts)"
14991532
" WITH (fastupdate = off) {index_tablespace};\n";
@@ -1528,12 +1561,6 @@ static table_sql sql_for_relations_format2()
15281561
" {users_table_access}"
15291562
" WHERE o.id = $1;\n";
15301563

1531-
sql.prepare_fw_dep_lookups =
1532-
"PREPARE mark_rels_by_way(int8) AS"
1533-
" SELECT DISTINCT id FROM {schema}\"{prefix}_rels\""
1534-
" WHERE {schema}\"{prefix}_member_ids\"(members, 'W'::char)"
1535-
" @> ARRAY[$1::bigint];\n";
1536-
15371564
sql.create_fw_dep_indexes =
15381565
"CREATE INDEX \"{prefix}_rels_node_members\""
15391566
" ON {schema}\"{prefix}_rels\" USING GIN"

src/middle-pgsql.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ struct table_sql
9595
std::string name;
9696
std::string create_table;
9797
std::string prepare_query;
98-
std::string prepare_fw_dep_lookups;
9998
std::string create_fw_dep_indexes;
10099
};
101100

@@ -122,7 +121,9 @@ struct middle_pgsql_t : public middle_t
122121
osmium::index::IdSetSmall<osmid_t> *parent_ways,
123122
osmium::index::IdSetSmall<osmid_t> *parent_relations) const override;
124123

125-
idlist_t get_rels_by_way(osmid_t osm_id) override;
124+
void get_way_parents(
125+
osmium::index::IdSetSmall<osmid_t> const &changed_ways,
126+
osmium::index::IdSetSmall<osmid_t> *parent_relations) const override;
126127

127128
class table_desc
128129
{
@@ -150,7 +151,6 @@ struct middle_pgsql_t : public middle_t
150151

151152
std::string m_create_table;
152153
std::string m_prepare_query;
153-
std::string m_prepare_fw_dep_lookups;
154154
std::string m_create_fw_dep_indexes;
155155

156156
void task_set(std::future<std::chrono::microseconds> &&future)

src/middle.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ class middle_t
156156
{
157157
}
158158

159-
virtual idlist_t get_rels_by_way(osmid_t) { return {}; }
159+
virtual void get_way_parents(
160+
osmium::index::IdSetSmall<osmid_t> const & /*changed_ways*/,
161+
osmium::index::IdSetSmall<osmid_t> * /*parent_relations*/) const
162+
{
163+
}
160164

161165
virtual std::shared_ptr<middle_query_t> get_query_instance() = 0;
162166

src/osmdata.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ void osmdata_t::after_ways()
109109
{
110110
m_mid->after_ways();
111111
m_output->after_ways();
112+
if (m_append) {
113+
m_dependency_manager->after_ways();
114+
}
112115
}
113116

114117
void osmdata_t::relation(osmium::Relation const &rel)
@@ -351,10 +354,13 @@ void osmdata_t::process_dependents() const
351354
}
352355

353356
// stage 1c processing: mark parent relations of marked objects as changed
354-
for (auto const id : m_output->get_marked_way_ids()) {
355-
m_dependency_manager->way_changed(id);
357+
auto marked_ways = m_output->get_marked_way_ids();
358+
if (marked_ways.empty()) {
359+
return;
356360
}
357361

362+
m_dependency_manager->mark_parent_relations_as_pending(marked_ways);
363+
358364
// process parent relations of marked ways
359365
if (m_dependency_manager->has_pending()) {
360366
proc.process_relations_stage1c(

src/pgsql-helper.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ idlist_t get_ids_from_result(pg_result_t const &result) {
2525
return ids;
2626
}
2727

28-
idlist_t get_ids_from_db(pg_conn_t const *db_connection, char const *stmt,
29-
osmid_t id)
30-
{
31-
auto const res = db_connection->exec_prepared(stmt, id);
32-
return get_ids_from_result(res);
33-
}
34-
3528
void create_geom_check_trigger(pg_conn_t *db_connection,
3629
std::string const &schema,
3730
std::string const &table,

src/pgsql-helper.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ class pg_result_t;
2727
*/
2828
idlist_t get_ids_from_result(pg_result_t const &result);
2929

30-
idlist_t get_ids_from_db(pg_conn_t const *db_connection, char const *stmt,
31-
osmid_t id);
32-
3330
void create_geom_check_trigger(pg_conn_t *db_connection,
3431
std::string const &schema,
3532
std::string const &table,

tests/test-middle.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,7 @@ TEMPLATE_TEST_CASE("middle: change nodes in way", "", options_slim_default,
11071107
mid->after_nodes();
11081108
dependency_manager.after_nodes();
11091109
mid->after_ways();
1110+
dependency_manager.after_ways();
11101111
mid->after_relations();
11111112

11121113
REQUIRE(dependency_manager.has_pending());
@@ -1141,6 +1142,7 @@ TEMPLATE_TEST_CASE("middle: change nodes in way", "", options_slim_default,
11411142
mid->after_nodes();
11421143
dependency_manager.after_nodes();
11431144
mid->after_ways();
1145+
dependency_manager.after_ways();
11441146
mid->after_relations();
11451147

11461148
REQUIRE(dependency_manager.has_pending());
@@ -1181,6 +1183,7 @@ TEMPLATE_TEST_CASE("middle: change nodes in way", "", options_slim_default,
11811183
mid->after_nodes();
11821184
dependency_manager.after_nodes();
11831185
mid->after_ways();
1186+
dependency_manager.after_ways();
11841187
mid->after_relations();
11851188

11861189
REQUIRE_FALSE(dependency_manager.has_pending());
@@ -1247,6 +1250,7 @@ TEMPLATE_TEST_CASE("middle: change nodes in relation", "", options_slim_default,
12471250
mid->after_nodes();
12481251
dependency_manager.after_nodes();
12491252
mid->after_ways();
1253+
dependency_manager.after_ways();
12501254
mid->after_relations();
12511255

12521256
REQUIRE(dependency_manager.has_pending());
@@ -1268,6 +1272,7 @@ TEMPLATE_TEST_CASE("middle: change nodes in relation", "", options_slim_default,
12681272
mid->after_nodes();
12691273
dependency_manager.after_nodes();
12701274
mid->after_ways();
1275+
dependency_manager.after_ways();
12711276
mid->after_relations();
12721277

12731278
REQUIRE(dependency_manager.has_pending());

0 commit comments

Comments
 (0)