mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-24 02:25:45 +03:00
Automatically apply SQL for inconsistent sequence (#17305)
Some checks failed
Build release artifacts / Calculate list of debian distros (push) Has been cancelled
Deploy the documentation / Calculate variables for GitHub Pages deployment (push) Has been cancelled
Build release artifacts / Build wheels on ${{ matrix.os }} for ${{ matrix.arch }} (aarch64, ${{ startsWith(github.ref, 'refs/pull/') }}, ubuntu-20.04) (push) Has been cancelled
Build release artifacts / Build wheels on ${{ matrix.os }} for ${{ matrix.arch }} (x86_64, ${{ startsWith(github.ref, 'refs/pull/') }}, macos-11) (push) Has been cancelled
Build release artifacts / Build wheels on ${{ matrix.os }} for ${{ matrix.arch }} (x86_64, ${{ startsWith(github.ref, 'refs/pull/') }}, ubuntu-20.04) (push) Has been cancelled
Build release artifacts / Build sdist (push) Has been cancelled
Tests / lint-newsfile (push) Has been cancelled
Tests / changes (push) Has been cancelled
Tests / check-lockfile (push) Has been cancelled
Tests / lint-crlf (push) Has been cancelled
Tests / lint-pydantic (push) Has been cancelled
Tests / lint-clippy (push) Has been cancelled
Tests / lint-clippy-nightly (push) Has been cancelled
Tests / lint-rustfmt (push) Has been cancelled
Deploy the documentation / GitHub Pages (push) Has been cancelled
Build release artifacts / Build .deb packages (push) Has been cancelled
Build release artifacts / Attach assets to release (push) Has been cancelled
Tests / check-sampleconfig (push) Has been cancelled
Tests / check-schema-delta (push) Has been cancelled
Tests / lint (push) Has been cancelled
Tests / Typechecking (push) Has been cancelled
Tests / linting-done (push) Has been cancelled
Tests / calculate-test-jobs (push) Has been cancelled
Tests / trial (push) Has been cancelled
Tests / trial-olddeps (push) Has been cancelled
Tests / trial-pypy (all, pypy-3.8) (push) Has been cancelled
Tests / sytest (push) Has been cancelled
Tests / export-data (push) Has been cancelled
Tests / portdb (11, 3.8) (push) Has been cancelled
Tests / portdb (15, 3.11) (push) Has been cancelled
Tests / complement (monolith, Postgres) (push) Has been cancelled
Tests / complement (monolith, SQLite) (push) Has been cancelled
Tests / complement (workers, Postgres) (push) Has been cancelled
Tests / cargo-test (push) Has been cancelled
Tests / cargo-bench (push) Has been cancelled
Tests / tests-done (push) Has been cancelled
Some checks failed
Build release artifacts / Calculate list of debian distros (push) Has been cancelled
Deploy the documentation / Calculate variables for GitHub Pages deployment (push) Has been cancelled
Build release artifacts / Build wheels on ${{ matrix.os }} for ${{ matrix.arch }} (aarch64, ${{ startsWith(github.ref, 'refs/pull/') }}, ubuntu-20.04) (push) Has been cancelled
Build release artifacts / Build wheels on ${{ matrix.os }} for ${{ matrix.arch }} (x86_64, ${{ startsWith(github.ref, 'refs/pull/') }}, macos-11) (push) Has been cancelled
Build release artifacts / Build wheels on ${{ matrix.os }} for ${{ matrix.arch }} (x86_64, ${{ startsWith(github.ref, 'refs/pull/') }}, ubuntu-20.04) (push) Has been cancelled
Build release artifacts / Build sdist (push) Has been cancelled
Tests / lint-newsfile (push) Has been cancelled
Tests / changes (push) Has been cancelled
Tests / check-lockfile (push) Has been cancelled
Tests / lint-crlf (push) Has been cancelled
Tests / lint-pydantic (push) Has been cancelled
Tests / lint-clippy (push) Has been cancelled
Tests / lint-clippy-nightly (push) Has been cancelled
Tests / lint-rustfmt (push) Has been cancelled
Deploy the documentation / GitHub Pages (push) Has been cancelled
Build release artifacts / Build .deb packages (push) Has been cancelled
Build release artifacts / Attach assets to release (push) Has been cancelled
Tests / check-sampleconfig (push) Has been cancelled
Tests / check-schema-delta (push) Has been cancelled
Tests / lint (push) Has been cancelled
Tests / Typechecking (push) Has been cancelled
Tests / linting-done (push) Has been cancelled
Tests / calculate-test-jobs (push) Has been cancelled
Tests / trial (push) Has been cancelled
Tests / trial-olddeps (push) Has been cancelled
Tests / trial-pypy (all, pypy-3.8) (push) Has been cancelled
Tests / sytest (push) Has been cancelled
Tests / export-data (push) Has been cancelled
Tests / portdb (11, 3.8) (push) Has been cancelled
Tests / portdb (15, 3.11) (push) Has been cancelled
Tests / complement (monolith, Postgres) (push) Has been cancelled
Tests / complement (monolith, SQLite) (push) Has been cancelled
Tests / complement (workers, Postgres) (push) Has been cancelled
Tests / cargo-test (push) Has been cancelled
Tests / cargo-bench (push) Has been cancelled
Tests / tests-done (push) Has been cancelled
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.
This commit is contained in:
parent
e6816babf6
commit
a3cb244755
4 changed files with 25 additions and 37 deletions
1
changelog.d/17305.misc
Normal file
1
changelog.d/17305.misc
Normal file
|
@ -0,0 +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.
|
|
@ -255,13 +255,3 @@ however extreme care must be taken to avoid database corruption.
|
||||||
|
|
||||||
Note that the above may fail with an error about duplicate rows if corruption
|
Note that the above may fail with an error about duplicate rows if corruption
|
||||||
has already occurred, and such duplicate rows will need to be manually removed.
|
has already occurred, and such duplicate rows will need to be manually removed.
|
||||||
|
|
||||||
### Fixing inconsistent sequences error
|
|
||||||
|
|
||||||
Synapse uses Postgres sequences to generate IDs for various tables. A sequence
|
|
||||||
and associated table can get out of sync if, for example, Synapse has been
|
|
||||||
downgraded and then upgraded again.
|
|
||||||
|
|
||||||
To fix the issue shut down Synapse (including any and all workers) and run the
|
|
||||||
SQL command included in the error message. Once done Synapse should start
|
|
||||||
successfully.
|
|
||||||
|
|
|
@ -36,21 +36,6 @@ if TYPE_CHECKING:
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
_INCONSISTENT_SEQUENCE_ERROR = """
|
|
||||||
Postgres sequence '%(seq)s' is inconsistent with associated
|
|
||||||
table '%(table)s'. This can happen if Synapse has been downgraded and
|
|
||||||
then upgraded again, or due to a bad migration.
|
|
||||||
|
|
||||||
To fix this error, shut down Synapse (including any and all workers)
|
|
||||||
and run the following SQL:
|
|
||||||
|
|
||||||
SELECT setval('%(seq)s', (
|
|
||||||
%(max_id_sql)s
|
|
||||||
));
|
|
||||||
|
|
||||||
See docs/postgres.md for more information.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_INCONSISTENT_STREAM_ERROR = """
|
_INCONSISTENT_STREAM_ERROR = """
|
||||||
Postgres sequence '%(seq)s' is inconsistent with associated stream position
|
Postgres sequence '%(seq)s' is inconsistent with associated stream position
|
||||||
of '%(stream_name)s' in the 'stream_positions' table.
|
of '%(stream_name)s' in the 'stream_positions' table.
|
||||||
|
@ -169,25 +154,33 @@ class PostgresSequenceGenerator(SequenceGenerator):
|
||||||
if row:
|
if row:
|
||||||
max_in_stream_positions = row[0]
|
max_in_stream_positions = row[0]
|
||||||
|
|
||||||
txn.close()
|
|
||||||
|
|
||||||
# If `is_called` is False then `last_value` is actually the value that
|
# If `is_called` is False then `last_value` is actually the value that
|
||||||
# will be generated next, so we decrement to get the true "last value".
|
# will be generated next, so we decrement to get the true "last value".
|
||||||
if not is_called:
|
if not is_called:
|
||||||
last_value -= 1
|
last_value -= 1
|
||||||
|
|
||||||
if max_stream_id > last_value:
|
if max_stream_id > last_value:
|
||||||
|
# The sequence is lagging behind the tables. This is probably due to
|
||||||
|
# rolling back to a version before the sequence was used and then
|
||||||
|
# forwards again. We resolve this by setting the sequence to the
|
||||||
|
# right value.
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Postgres sequence %s is behind table %s: %d < %d",
|
"Postgres sequence %s is behind table %s: %d < %d. Updating sequence.",
|
||||||
self._sequence_name,
|
self._sequence_name,
|
||||||
table,
|
table,
|
||||||
last_value,
|
last_value,
|
||||||
max_stream_id,
|
max_stream_id,
|
||||||
)
|
)
|
||||||
raise IncorrectDatabaseSetup(
|
|
||||||
_INCONSISTENT_SEQUENCE_ERROR
|
sql = f"""
|
||||||
% {"seq": self._sequence_name, "table": table, "max_id_sql": table_sql}
|
SELECT setval('{self._sequence_name}', GREATEST(
|
||||||
)
|
(SELECT last_value FROM {self._sequence_name}),
|
||||||
|
({table_sql})
|
||||||
|
));
|
||||||
|
"""
|
||||||
|
txn.execute(sql)
|
||||||
|
|
||||||
|
txn.close()
|
||||||
|
|
||||||
# If we have values in the stream positions table then they have to be
|
# If we have values in the stream positions table then they have to be
|
||||||
# less than or equal to `last_value`
|
# less than or equal to `last_value`
|
||||||
|
|
|
@ -28,7 +28,6 @@ from synapse.storage.database import (
|
||||||
LoggingDatabaseConnection,
|
LoggingDatabaseConnection,
|
||||||
LoggingTransaction,
|
LoggingTransaction,
|
||||||
)
|
)
|
||||||
from synapse.storage.engines import IncorrectDatabaseSetup
|
|
||||||
from synapse.storage.types import Cursor
|
from synapse.storage.types import Cursor
|
||||||
from synapse.storage.util.id_generators import MultiWriterIdGenerator
|
from synapse.storage.util.id_generators import MultiWriterIdGenerator
|
||||||
from synapse.storage.util.sequence import (
|
from synapse.storage.util.sequence import (
|
||||||
|
@ -525,7 +524,7 @@ class WorkerMultiWriterIdGeneratorTestCase(MultiWriterIdGeneratorBase):
|
||||||
self.assertEqual(id_gen_5.get_current_token_for_writer("third"), 6)
|
self.assertEqual(id_gen_5.get_current_token_for_writer("third"), 6)
|
||||||
|
|
||||||
def test_sequence_consistency(self) -> None:
|
def test_sequence_consistency(self) -> None:
|
||||||
"""Test that we error out if the table and sequence diverges."""
|
"""Test that we correct the sequence if the table and sequence diverges."""
|
||||||
|
|
||||||
# Prefill with some rows
|
# Prefill with some rows
|
||||||
self._insert_row_with_id("master", 3)
|
self._insert_row_with_id("master", 3)
|
||||||
|
@ -536,9 +535,14 @@ class WorkerMultiWriterIdGeneratorTestCase(MultiWriterIdGeneratorBase):
|
||||||
|
|
||||||
self.get_success(self.db_pool.runInteraction("_insert", _insert))
|
self.get_success(self.db_pool.runInteraction("_insert", _insert))
|
||||||
|
|
||||||
# Creating the ID gen should error
|
# Creating the ID gen should now fix the inconsistency
|
||||||
with self.assertRaises(IncorrectDatabaseSetup):
|
id_gen = self._create_id_generator()
|
||||||
self._create_id_generator("first")
|
|
||||||
|
async def _get_next_async() -> None:
|
||||||
|
async with id_gen.get_next() as stream_id:
|
||||||
|
self.assertEqual(stream_id, 27)
|
||||||
|
|
||||||
|
self.get_success(_get_next_async())
|
||||||
|
|
||||||
def test_minimal_local_token(self) -> None:
|
def test_minimal_local_token(self) -> None:
|
||||||
self._insert_rows("first", 3)
|
self._insert_rows("first", 3)
|
||||||
|
|
Loading…
Reference in a new issue