Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 9738b1c

Browse files
authored
Avoid executing no-op queries. (#16583)
If simple_{insert,upsert,update}_many_txn is called without any data to modify then return instead of executing the query. This matches the behavior of simple_{select,delete}_many_txn.
1 parent ec9ff38 commit 9738b1c

File tree

7 files changed

+39
-39
lines changed

7 files changed

+39
-39
lines changed

changelog.d/16583.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid executing no-op queries.

synapse/storage/database.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,7 @@ def simple_insert_many_txn(
11171117
txn: LoggingTransaction,
11181118
table: str,
11191119
keys: Collection[str],
1120-
values: Iterable[Iterable[Any]],
1120+
values: Collection[Iterable[Any]],
11211121
) -> None:
11221122
"""Executes an INSERT query on the named table.
11231123
@@ -1130,6 +1130,9 @@ def simple_insert_many_txn(
11301130
keys: list of column names
11311131
values: for each row, a list of values in the same order as `keys`
11321132
"""
1133+
# If there's nothing to insert, then skip executing the query.
1134+
if not values:
1135+
return
11331136

11341137
if isinstance(txn.database_engine, PostgresEngine):
11351138
# We use `execute_values` as it can be a lot faster than `execute_batch`,
@@ -1455,7 +1458,7 @@ def simple_upsert_many_txn(
14551458
key_names: Collection[str],
14561459
key_values: Collection[Iterable[Any]],
14571460
value_names: Collection[str],
1458-
value_values: Iterable[Iterable[Any]],
1461+
value_values: Collection[Iterable[Any]],
14591462
) -> None:
14601463
"""
14611464
Upsert, many times.
@@ -1468,6 +1471,19 @@ def simple_upsert_many_txn(
14681471
value_values: A list of each row's value column values.
14691472
Ignored if value_names is empty.
14701473
"""
1474+
# If there's nothing to upsert, then skip executing the query.
1475+
if not key_values:
1476+
return
1477+
1478+
# No value columns, therefore make a blank list so that the following
1479+
# zip() works correctly.
1480+
if not value_names:
1481+
value_values = [() for x in range(len(key_values))]
1482+
elif len(value_values) != len(key_values):
1483+
raise ValueError(
1484+
f"{len(key_values)} key rows and {len(value_values)} value rows: should be the same number."
1485+
)
1486+
14711487
if table not in self._unsafe_to_upsert_tables:
14721488
return self.simple_upsert_many_txn_native_upsert(
14731489
txn, table, key_names, key_values, value_names, value_values
@@ -1502,10 +1518,6 @@ def simple_upsert_many_txn_emulated(
15021518
value_values: A list of each row's value column values.
15031519
Ignored if value_names is empty.
15041520
"""
1505-
# No value columns, therefore make a blank list so that the following
1506-
# zip() works correctly.
1507-
if not value_names:
1508-
value_values = [() for x in range(len(key_values))]
15091521

15101522
# Lock the table just once, to prevent it being done once per row.
15111523
# Note that, according to Postgres' documentation, once obtained,
@@ -1543,10 +1555,7 @@ def simple_upsert_many_txn_native_upsert(
15431555
allnames.extend(value_names)
15441556

15451557
if not value_names:
1546-
# No value columns, therefore make a blank list so that the
1547-
# following zip() works correctly.
15481558
latter = "NOTHING"
1549-
value_values = [() for x in range(len(key_values))]
15501559
else:
15511560
latter = "UPDATE SET " + ", ".join(
15521561
k + "=EXCLUDED." + k for k in value_names
@@ -1910,6 +1919,7 @@ def simple_select_many_txn(
19101919
Returns:
19111920
The results as a list of tuples.
19121921
"""
1922+
# If there's nothing to select, then skip executing the query.
19131923
if not iterable:
19141924
return []
19151925

@@ -2044,6 +2054,9 @@ def simple_update_many_txn(
20442054
raise ValueError(
20452055
f"{len(key_values)} key rows and {len(value_values)} value rows: should be the same number."
20462056
)
2057+
# If there is nothing to update, then skip executing the query.
2058+
if not key_values:
2059+
return
20472060

20482061
# List of tuples of (value values, then key values)
20492062
# (This matches the order needed for the query)
@@ -2278,6 +2291,7 @@ def simple_delete_many_txn(
22782291
Returns:
22792292
Number rows deleted
22802293
"""
2294+
# If there's nothing to delete, then skip executing the query.
22812295
if not values:
22822296
return 0
22832297

synapse/storage/databases/main/devices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ def _mark_as_sent_devices_by_remote_txn(
703703
key_names=("destination", "user_id"),
704704
key_values=[(destination, user_id) for user_id, _ in rows],
705705
value_names=("stream_id",),
706-
value_values=((stream_id,) for _, stream_id in rows),
706+
value_values=[(stream_id,) for _, stream_id in rows],
707707
)
708708

709709
# Delete all sent outbound pokes

synapse/storage/databases/main/events.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,7 +1476,7 @@ def event_dict(event: EventBase) -> JsonDict:
14761476
txn,
14771477
table="event_json",
14781478
keys=("event_id", "room_id", "internal_metadata", "json", "format_version"),
1479-
values=(
1479+
values=[
14801480
(
14811481
event.event_id,
14821482
event.room_id,
@@ -1485,7 +1485,7 @@ def event_dict(event: EventBase) -> JsonDict:
14851485
event.format_version,
14861486
)
14871487
for event, _ in events_and_contexts
1488-
),
1488+
],
14891489
)
14901490

14911491
self.db_pool.simple_insert_many_txn(
@@ -1508,7 +1508,7 @@ def event_dict(event: EventBase) -> JsonDict:
15081508
"state_key",
15091509
"rejection_reason",
15101510
),
1511-
values=(
1511+
values=[
15121512
(
15131513
self._instance_name,
15141514
event.internal_metadata.stream_ordering,
@@ -1527,7 +1527,7 @@ def event_dict(event: EventBase) -> JsonDict:
15271527
context.rejected,
15281528
)
15291529
for event, context in events_and_contexts
1530-
),
1530+
],
15311531
)
15321532

15331533
# If we're persisting an unredacted event we go and ensure
@@ -1550,11 +1550,11 @@ def event_dict(event: EventBase) -> JsonDict:
15501550
txn,
15511551
table="state_events",
15521552
keys=("event_id", "room_id", "type", "state_key"),
1553-
values=(
1553+
values=[
15541554
(event.event_id, event.room_id, event.type, event.state_key)
15551555
for event, _ in events_and_contexts
15561556
if event.is_state()
1557-
),
1557+
],
15581558
)
15591559

15601560
def _store_rejected_events_txn(

synapse/storage/databases/main/room.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2268,7 +2268,7 @@ def _store_partial_state_room_txn(
22682268
txn,
22692269
table="partial_state_rooms_servers",
22702270
keys=("room_id", "server_name"),
2271-
values=((room_id, s) for s in servers),
2271+
values=[(room_id, s) for s in servers],
22722272
)
22732273
self._invalidate_cache_and_stream(txn, self.is_partial_state_room, (room_id,))
22742274
self._invalidate_cache_and_stream(

synapse/storage/databases/main/search.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ def store_search_entries_txn(
106106
txn,
107107
table="event_search",
108108
keys=("event_id", "room_id", "key", "value"),
109-
values=(
109+
values=[
110110
(
111111
entry.event_id,
112112
entry.room_id,
113113
entry.key,
114114
_clean_value_for_search(entry.value),
115115
)
116116
for entry in entries
117-
),
117+
],
118118
)
119119

120120
else:

tests/storage/test_base.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -189,17 +189,9 @@ def test_insert_many_no_iterable(
189189
)
190190

191191
if USE_POSTGRES_FOR_TESTS:
192-
self.mock_execute_values.assert_called_once_with(
193-
self.mock_txn,
194-
"INSERT INTO tablename (col1, col2) VALUES ?",
195-
[],
196-
template=None,
197-
fetch=False,
198-
)
192+
self.mock_execute_values.assert_not_called()
199193
else:
200-
self.mock_txn.executemany.assert_called_once_with(
201-
"INSERT INTO tablename (col1, col2) VALUES(?, ?)", []
202-
)
194+
self.mock_txn.executemany.assert_not_called()
203195

204196
@defer.inlineCallbacks
205197
def test_select_one_1col(self) -> Generator["defer.Deferred[object]", object, None]:
@@ -393,7 +385,7 @@ def test_update_many(self) -> Generator["defer.Deferred[object]", object, None]:
393385
)
394386

395387
@defer.inlineCallbacks
396-
def test_update_many_no_values(
388+
def test_update_many_no_iterable(
397389
self,
398390
) -> Generator["defer.Deferred[object]", object, None]:
399391
yield defer.ensureDeferred(
@@ -408,16 +400,9 @@ def test_update_many_no_values(
408400
)
409401

410402
if USE_POSTGRES_FOR_TESTS:
411-
self.mock_execute_batch.assert_called_once_with(
412-
self.mock_txn,
413-
"UPDATE tablename SET col3 = ? WHERE col1 = ? AND col2 = ?",
414-
[],
415-
)
403+
self.mock_execute_batch.assert_not_called()
416404
else:
417-
self.mock_txn.executemany.assert_called_once_with(
418-
"UPDATE tablename SET col3 = ? WHERE col1 = ? AND col2 = ?",
419-
[],
420-
)
405+
self.mock_txn.executemany.assert_not_called()
421406

422407
@defer.inlineCallbacks
423408
def test_delete_one(self) -> Generator["defer.Deferred[object]", object, None]:

0 commit comments

Comments
 (0)