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

Commit

Permalink
Merge commit 'f76194a02' into anoa/dinsic_release_1_21_x
Browse files Browse the repository at this point in the history
* commit 'f76194a02':
  1.21.0
  Update change log
  1.21.0rc3
  Reduce serialization errors in MultiWriterIdGen (#8456)
  Add Ubuntu 20.10 (Groovy Gorilla) to build scripts. (#8475)
  move #8444 to 'feature'
  linkify changelog
  • Loading branch information
anoadragon453 committed Oct 21, 2020
2 parents 56e2477 + f76194a commit e15cca7
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 8 deletions.
29 changes: 27 additions & 2 deletions CHANGES.md
@@ -1,14 +1,39 @@
Synapse 1.21.0 (2020-10-12)
===========================

No significant changes since v1.21.0rc3.


Synapse 1.21.0rc3 (2020-10-08)
==============================

Bugfixes
--------

- Fix duplication of events on high traffic servers, caused by PostgreSQL `could not serialize access due to concurrent update` errors. ([\#8456](https://github.com/matrix-org/synapse/issues/8456))


Internal Changes
----------------

- Add Groovy Gorilla to the list of distributions we build `.deb`s for. ([\#8475](https://github.com/matrix-org/synapse/issues/8475))


Synapse 1.21.0rc2 (2020-10-02)
==============================

Features
--------

- Convert additional templates from inline HTML to Jinja2 templates. ([\#8444](https://github.com/matrix-org/synapse/issues/8444))

Bugfixes
--------

- Fix a regression in v1.21.0rc1 which broke thumbnails of remote media. ([\#8438](https://github.com/matrix-org/synapse/issues/8438))
- Do not expose the experimental `uk.half-shot.msc2778.login.application_service` flow in the login API, which caused a compatibility problem with Element iOS. ([\#8440](https://github.com/matrix-org/synapse/issues/8440))
- Fix malformed log line in new federation "catch up" logic. ([\#8442](https://github.com/matrix-org/synapse/issues/8442))
- Convert additional templates from inline HTML to Jinja2 templates. ([\#8444](https://github.com/matrix-org/synapse/issues/8444))
- Fix DB query on startup for negative streams which caused long start up times. Introduced in #8374. ([\#8447](https://github.com/matrix-org/synapse/issues/8447))
- Fix DB query on startup for negative streams which caused long start up times. Introduced in [\#8374](https://github.com/matrix-org/synapse/issues/8374). ([\#8447](https://github.com/matrix-org/synapse/issues/8447))


Synapse 1.21.0rc1 (2020-10-01)
Expand Down
6 changes: 6 additions & 0 deletions debian/changelog
@@ -1,3 +1,9 @@
matrix-synapse-py3 (1.21.0) stable; urgency=medium

* New synapse release 1.21.0.

-- Synapse Packaging team <packages@matrix.org> Mon, 12 Oct 2020 15:47:44 +0100

matrix-synapse-py3 (1.20.1) stable; urgency=medium

* New synapse release 1.20.1.
Expand Down
1 change: 1 addition & 0 deletions scripts-dev/build_debian_packages
Expand Up @@ -25,6 +25,7 @@ DISTS = (
"ubuntu:xenial",
"ubuntu:bionic",
"ubuntu:focal",
"ubuntu:groovy",
)

DESC = '''\
Expand Down
2 changes: 1 addition & 1 deletion synapse/__init__.py
Expand Up @@ -48,7 +48,7 @@
except ImportError:
pass

__version__ = "1.21.0rc2"
__version__ = "1.21.0"

if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)):
# We import here so that we don't have to install a bunch of deps when
Expand Down
63 changes: 60 additions & 3 deletions synapse/storage/database.py
Expand Up @@ -403,6 +403,24 @@ def new_transaction(
*args: Any,
**kwargs: Any
) -> R:
"""Start a new database transaction with the given connection.
Note: The given func may be called multiple times under certain
failure modes. This is normally fine when in a standard transaction,
but care must be taken if the connection is in `autocommit` mode that
the function will correctly handle being aborted and retried half way
through its execution.
Args:
conn
desc
after_callbacks
exception_callbacks
func
*args
**kwargs
"""

start = monotonic_time()
txn_id = self._TXN_ID

Expand Down Expand Up @@ -508,7 +526,12 @@ def new_transaction(
sql_txn_timer.labels(desc).observe(duration)

async def runInteraction(
self, desc: str, func: "Callable[..., R]", *args: Any, **kwargs: Any
self,
desc: str,
func: "Callable[..., R]",
*args: Any,
db_autocommit: bool = False,
**kwargs: Any
) -> R:
"""Starts a transaction on the database and runs a given function
Expand All @@ -518,6 +541,18 @@ async def runInteraction(
database transaction (twisted.enterprise.adbapi.Transaction) as
its first argument, followed by `args` and `kwargs`.
db_autocommit: Whether to run the function in "autocommit" mode,
i.e. outside of a transaction. This is useful for transactions
that are only a single query.
Currently, this is only implemented for Postgres. SQLite will still
run the function inside a transaction.
WARNING: This means that if func fails half way through then
the changes will *not* be rolled back. `func` may also get
called multiple times if the transaction is retried, so must
correctly handle that case.
args: positional args to pass to `func`
kwargs: named args to pass to `func`
Expand All @@ -538,6 +573,7 @@ async def runInteraction(
exception_callbacks,
func,
*args,
db_autocommit=db_autocommit,
**kwargs
)

Expand All @@ -551,7 +587,11 @@ async def runInteraction(
return cast(R, result)

async def runWithConnection(
self, func: "Callable[..., R]", *args: Any, **kwargs: Any
self,
func: "Callable[..., R]",
*args: Any,
db_autocommit: bool = False,
**kwargs: Any
) -> R:
"""Wraps the .runWithConnection() method on the underlying db_pool.
Expand All @@ -560,6 +600,9 @@ async def runWithConnection(
database connection (twisted.enterprise.adbapi.Connection) as
its first argument, followed by `args` and `kwargs`.
args: positional args to pass to `func`
db_autocommit: Whether to run the function in "autocommit" mode,
i.e. outside of a transaction. This is useful for transaction
that are only a single query. Currently only affects postgres.
kwargs: named args to pass to `func`
Returns:
Expand All @@ -575,6 +618,13 @@ async def runWithConnection(
start_time = monotonic_time()

def inner_func(conn, *args, **kwargs):
# We shouldn't be in a transaction. If we are then something
# somewhere hasn't committed after doing work. (This is likely only
# possible during startup, as `run*` will ensure changes are
# committed/rolled back before putting the connection back in the
# pool).
assert not self.engine.in_transaction(conn)

with LoggingContext("runWithConnection", parent_context) as context:
sched_duration_sec = monotonic_time() - start_time
sql_scheduling_timer.observe(sched_duration_sec)
Expand All @@ -584,7 +634,14 @@ def inner_func(conn, *args, **kwargs):
logger.debug("Reconnecting closed database connection")
conn.reconnect()

return func(conn, *args, **kwargs)
try:
if db_autocommit:
self.engine.attempt_to_set_autocommit(conn, True)

return func(conn, *args, **kwargs)
finally:
if db_autocommit:
self.engine.attempt_to_set_autocommit(conn, False)

return await make_deferred_yieldable(
self._db_pool.runWithConnection(inner_func, *args, **kwargs)
Expand Down
17 changes: 17 additions & 0 deletions synapse/storage/engines/_base.py
Expand Up @@ -97,3 +97,20 @@ def server_version(self) -> str:
"""Gets a string giving the server version. For example: '3.22.0'
"""
...

@abc.abstractmethod
def in_transaction(self, conn: Connection) -> bool:
"""Whether the connection is currently in a transaction.
"""
...

@abc.abstractmethod
def attempt_to_set_autocommit(self, conn: Connection, autocommit: bool):
"""Attempt to set the connections autocommit mode.
When True queries are run outside of transactions.
Note: This has no effect on SQLite3, so callers still need to
commit/rollback the connections.
"""
...
10 changes: 9 additions & 1 deletion synapse/storage/engines/postgres.py
Expand Up @@ -15,7 +15,8 @@

import logging

from ._base import BaseDatabaseEngine, IncorrectDatabaseSetup
from synapse.storage.engines._base import BaseDatabaseEngine, IncorrectDatabaseSetup
from synapse.storage.types import Connection

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -119,6 +120,7 @@ def on_new_connection(self, db_conn):
cursor.execute("SET synchronous_commit TO OFF")

cursor.close()
db_conn.commit()

@property
def can_native_upsert(self):
Expand Down Expand Up @@ -171,3 +173,9 @@ def server_version(self):
return "%i.%i" % (numver / 10000, numver % 10000)
else:
return "%i.%i.%i" % (numver / 10000, (numver % 10000) / 100, numver % 100)

def in_transaction(self, conn: Connection) -> bool:
return conn.status != self.module.extensions.STATUS_READY # type: ignore

def attempt_to_set_autocommit(self, conn: Connection, autocommit: bool):
return conn.set_session(autocommit=autocommit) # type: ignore
10 changes: 10 additions & 0 deletions synapse/storage/engines/sqlite.py
Expand Up @@ -17,6 +17,7 @@
import typing

from synapse.storage.engines import BaseDatabaseEngine
from synapse.storage.types import Connection

if typing.TYPE_CHECKING:
import sqlite3 # noqa: F401
Expand Down Expand Up @@ -86,6 +87,7 @@ def on_new_connection(self, db_conn):

db_conn.create_function("rank", 1, _rank)
db_conn.execute("PRAGMA foreign_keys = ON;")
db_conn.commit()

def is_deadlock(self, error):
return False
Expand All @@ -105,6 +107,14 @@ def server_version(self):
"""
return "%i.%i.%i" % self.module.sqlite_version_info

def in_transaction(self, conn: Connection) -> bool:
return conn.in_transaction # type: ignore

def attempt_to_set_autocommit(self, conn: Connection, autocommit: bool):
# Twisted doesn't let us set attributes on the connections, so we can't
# set the connection to autocommit mode.
pass


# Following functions taken from: https://github.com/coleifer/peewee

Expand Down
12 changes: 11 additions & 1 deletion synapse/storage/util/id_generators.py
Expand Up @@ -24,6 +24,7 @@

from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.storage.database import DatabasePool, LoggingTransaction
from synapse.storage.types import Cursor
from synapse.storage.util.sequence import PostgresSequenceGenerator

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -552,7 +553,7 @@ def _add_persisted_position(self, new_id: int):
# do.
break

def _update_stream_positions_table_txn(self, txn):
def _update_stream_positions_table_txn(self, txn: Cursor):
"""Update the `stream_positions` table with newly persisted position.
"""

Expand Down Expand Up @@ -602,10 +603,13 @@ class _MultiWriterCtxManager:
stream_ids = attr.ib(type=List[int], factory=list)

async def __aenter__(self) -> Union[int, List[int]]:
# It's safe to run this in autocommit mode as fetching values from a
# sequence ignores transaction semantics anyway.
self.stream_ids = await self.id_gen._db.runInteraction(
"_load_next_mult_id",
self.id_gen._load_next_mult_id_txn,
self.multiple_ids or 1,
db_autocommit=True,
)

# Assert the fetched ID is actually greater than any ID we've already
Expand Down Expand Up @@ -636,10 +640,16 @@ async def __aexit__(self, exc_type, exc, tb):
#
# We only do this on the success path so that the persisted current
# position points to a persisted row with the correct instance name.
#
# We do this in autocommit mode as a) the upsert works correctly outside
# transactions and b) reduces the amount of time the rows are locked
# for. If we don't do this then we'll often hit serialization errors due
# to the fact we default to REPEATABLE READ isolation levels.
if self.id_gen._writers:
await self.id_gen._db.runInteraction(
"MultiWriterIdGenerator._update_table",
self.id_gen._update_stream_positions_table_txn,
db_autocommit=True,
)

return False
1 change: 1 addition & 0 deletions tests/storage/test_base.py
Expand Up @@ -56,6 +56,7 @@ def runWithConnection(func, *args, **kwargs):
engine = create_engine(sqlite_config)
fake_engine = Mock(wraps=engine)
fake_engine.can_native_upsert = False
fake_engine.in_transaction.return_value = False

db = DatabasePool(Mock(), Mock(config=sqlite_config), fake_engine)
db._db_pool = self.db_pool
Expand Down

0 comments on commit e15cca7

Please sign in to comment.