Skip to content

Commit a3cb244

Browse files
authored
Automatically apply SQL for inconsistent sequence (#17305)
Rather than forcing the server operator to apply the SQL manually. This should be safe, as there should be only one writer for these sequences.
1 parent e6816ba commit a3cb244

File tree

4 files changed

+25
-37
lines changed

4 files changed

+25
-37
lines changed

changelog.d/17305.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
When rolling back to a previous Synapse version and then forwards again to this release, don't require server operators to manually run SQL.

docs/postgres.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,3 @@ however extreme care must be taken to avoid database corruption.
255255
256256
Note that the above may fail with an error about duplicate rows if corruption
257257
has already occurred, and such duplicate rows will need to be manually removed.
258-
259-
### Fixing inconsistent sequences error
260-
261-
Synapse uses Postgres sequences to generate IDs for various tables. A sequence
262-
and associated table can get out of sync if, for example, Synapse has been
263-
downgraded and then upgraded again.
264-
265-
To fix the issue shut down Synapse (including any and all workers) and run the
266-
SQL command included in the error message. Once done Synapse should start
267-
successfully.

synapse/storage/util/sequence.py

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,6 @@
3636
logger = logging.getLogger(__name__)
3737

3838

39-
_INCONSISTENT_SEQUENCE_ERROR = """
40-
Postgres sequence '%(seq)s' is inconsistent with associated
41-
table '%(table)s'. This can happen if Synapse has been downgraded and
42-
then upgraded again, or due to a bad migration.
43-
44-
To fix this error, shut down Synapse (including any and all workers)
45-
and run the following SQL:
46-
47-
SELECT setval('%(seq)s', (
48-
%(max_id_sql)s
49-
));
50-
51-
See docs/postgres.md for more information.
52-
"""
53-
5439
_INCONSISTENT_STREAM_ERROR = """
5540
Postgres sequence '%(seq)s' is inconsistent with associated stream position
5641
of '%(stream_name)s' in the 'stream_positions' table.
@@ -169,25 +154,33 @@ def check_consistency(
169154
if row:
170155
max_in_stream_positions = row[0]
171156

172-
txn.close()
173-
174157
# If `is_called` is False then `last_value` is actually the value that
175158
# will be generated next, so we decrement to get the true "last value".
176159
if not is_called:
177160
last_value -= 1
178161

179162
if max_stream_id > last_value:
163+
# The sequence is lagging behind the tables. This is probably due to
164+
# rolling back to a version before the sequence was used and then
165+
# forwards again. We resolve this by setting the sequence to the
166+
# right value.
180167
logger.warning(
181-
"Postgres sequence %s is behind table %s: %d < %d",
168+
"Postgres sequence %s is behind table %s: %d < %d. Updating sequence.",
182169
self._sequence_name,
183170
table,
184171
last_value,
185172
max_stream_id,
186173
)
187-
raise IncorrectDatabaseSetup(
188-
_INCONSISTENT_SEQUENCE_ERROR
189-
% {"seq": self._sequence_name, "table": table, "max_id_sql": table_sql}
190-
)
174+
175+
sql = f"""
176+
SELECT setval('{self._sequence_name}', GREATEST(
177+
(SELECT last_value FROM {self._sequence_name}),
178+
({table_sql})
179+
));
180+
"""
181+
txn.execute(sql)
182+
183+
txn.close()
191184

192185
# If we have values in the stream positions table then they have to be
193186
# less than or equal to `last_value`

tests/storage/test_id_generators.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
LoggingDatabaseConnection,
2929
LoggingTransaction,
3030
)
31-
from synapse.storage.engines import IncorrectDatabaseSetup
3231
from synapse.storage.types import Cursor
3332
from synapse.storage.util.id_generators import MultiWriterIdGenerator
3433
from synapse.storage.util.sequence import (
@@ -525,7 +524,7 @@ async def _get_next_async() -> None:
525524
self.assertEqual(id_gen_5.get_current_token_for_writer("third"), 6)
526525

527526
def test_sequence_consistency(self) -> None:
528-
"""Test that we error out if the table and sequence diverges."""
527+
"""Test that we correct the sequence if the table and sequence diverges."""
529528

530529
# Prefill with some rows
531530
self._insert_row_with_id("master", 3)
@@ -536,9 +535,14 @@ def _insert(txn: Cursor) -> None:
536535

537536
self.get_success(self.db_pool.runInteraction("_insert", _insert))
538537

539-
# Creating the ID gen should error
540-
with self.assertRaises(IncorrectDatabaseSetup):
541-
self._create_id_generator("first")
538+
# Creating the ID gen should now fix the inconsistency
539+
id_gen = self._create_id_generator()
540+
541+
async def _get_next_async() -> None:
542+
async with id_gen.get_next() as stream_id:
543+
self.assertEqual(stream_id, 27)
544+
545+
self.get_success(_get_next_async())
542546

543547
def test_minimal_local_token(self) -> None:
544548
self._insert_rows("first", 3)

0 commit comments

Comments
 (0)