v0.18.6-rc3

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJYbk9MAAoJEEKm+zTV5pD3okwQAKVW95pKwzDr4IzOu4fOR6QB
 eUjVgmBmmTuAfgxWJbmNaYbFERnsSs+iebNjcYwFKYr/alCbttplz/msoOjEbMHC
 Ge3Z7jjMPMzs4v+35YnfKDJllUeeF439bpBhuJx3A4nJZTLO/zNIKtIc3myl0J4b
 LchNyhd4O0waoF9H80r6Gkbu/HC4/dKHXuD700hl8y01JfI0KWZsuIibOyZ6ciiR
 p1UaDE3x+qMe2p86iKHje/OxcZ3wGNI7ppGF/BWtpRv6yxMwwEYJ3ivWcgyVmqya
 M342SS/k1W8Vk9gEwTKAqFW70nvaaULdtxmvTHNc7QF+F3Dxk13ER8To0q1bI5eX
 K2q+p3gWdrecorRgkYz1oeh+myMkIQWEtqsHZ6eAKS86yixzluQMYnw9b5vToJ9c
 Pia4hnl616PKlQFiDGSjw+bpJwvWFCLDl6Xg0OH1ERBeNMwuidolij4Z24BJiQOo
 +UOLEgpRs7HdDb8jlwlPojMeqMjAOF3TEseJdAQxY0f243/hZHsOhvM1hQ9lm609
 Q5BO1Ra5d5eoMKiQ4hKxu7qgMN1s/pAkzgUGl+cNFiMVtQO3d+oLZh96xAWpzoB0
 h5K1+kpgYTUh9Xps0zimsEi63ZKXS11dLMJR1oDk5KH+bupt9c795VnR0aUVpNQ5
 hIpADjnch8MXofVqv1UV
 =Qice
 -----END PGP SIGNATURE-----

Merge tag 'v0.18.6-rc3' into matrix-org-hotfixes

v0.18.6-rc3
This commit is contained in:
Mark Haines 2017-01-05 14:19:36 +00:00
commit 345afd9fdc
14 changed files with 109 additions and 15 deletions

View file

@ -1,3 +1,28 @@
Changes in synapse v0.18.6-rc3 (2017-01-05)
===========================================
Bug fixes:
* Fix bug where we failed to send ban events to the banned server (PR #1758)
* Fix bug where we sent event that didn't originate on this server to
other servers (PR #1764)
* Fix bug where processing an event from a remote server took a long time
because we were making long HTTP requests (PR #1765, PR #1744)
Changes:
* Improve logging for debugging deadlocks (PR #1766, PR #1767)
Changes in synapse v0.18.6-rc2 (2016-12-30)
===========================================
Bug fixes:
* Fix memory leak in twisted by initialising logging correctly (PR #1731)
* Fix bug where fetching missing events took an unacceptable amount of time in
large rooms (PR #1734)
Changes in synapse v0.18.6-rc1 (2016-12-29) Changes in synapse v0.18.6-rc1 (2016-12-29)
=========================================== ===========================================

View file

@ -16,4 +16,4 @@
""" This is a reference implementation of a Matrix home server. """ This is a reference implementation of a Matrix home server.
""" """
__version__ = "0.18.6-rc1" __version__ = "0.18.6-rc3"

View file

@ -290,6 +290,7 @@ class Auth(object):
with Measure(self.clock, "check_host_in_room"): with Measure(self.clock, "check_host_in_room"):
latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id) latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id)
logger.info("calling resolve_state_groups from check_host_in_room")
entry = yield self.state.resolve_state_groups( entry = yield self.state.resolve_state_groups(
room_id, latest_event_ids room_id, latest_event_ids
) )

View file

@ -36,6 +36,15 @@ class _EventInternalMetadata(object):
def is_invite_from_remote(self): def is_invite_from_remote(self):
return getattr(self, "invite_from_remote", False) return getattr(self, "invite_from_remote", False)
def get_send_on_behalf_of(self):
"""Whether this server should send the event on behalf of another server.
This is used by the federation "send_join" API to forward the initial join
event for a server in the room.
returns a str with the name of the server this event is sent on behalf of.
"""
return getattr(self, "get_send_on_behalf_of", None)
def _event_dict_property(key): def _event_dict_property(key):
def getter(self): def getter(self):

View file

@ -707,7 +707,7 @@ class FederationClient(FederationBase):
@defer.inlineCallbacks @defer.inlineCallbacks
def get_missing_events(self, destination, room_id, earliest_events_ids, def get_missing_events(self, destination, room_id, earliest_events_ids,
latest_events, limit, min_depth): latest_events, limit, min_depth, timeout):
"""Tries to fetch events we are missing. This is called when we receive """Tries to fetch events we are missing. This is called when we receive
an event without having received all of its ancestors. an event without having received all of its ancestors.
@ -721,6 +721,7 @@ class FederationClient(FederationBase):
have all previous events for. have all previous events for.
limit (int): Maximum number of events to return. limit (int): Maximum number of events to return.
min_depth (int): Minimum depth of events tor return. min_depth (int): Minimum depth of events tor return.
timeout (int): Max time to wait in ms
""" """
try: try:
content = yield self.transport_layer.get_missing_events( content = yield self.transport_layer.get_missing_events(
@ -730,6 +731,7 @@ class FederationClient(FederationBase):
latest_events=[e.event_id for e in latest_events], latest_events=[e.event_id for e in latest_events],
limit=limit, limit=limit,
min_depth=min_depth, min_depth=min_depth,
timeout=timeout,
) )
events = [ events = [

View file

@ -425,6 +425,7 @@ class FederationServer(FederationBase):
" limit: %d, min_depth: %d", " limit: %d, min_depth: %d",
earliest_events, latest_events, limit, min_depth earliest_events, latest_events, limit, min_depth
) )
missing_events = yield self.handler.on_get_missing_events( missing_events = yield self.handler.on_get_missing_events(
origin, room_id, earliest_events, latest_events, limit, min_depth origin, room_id, earliest_events, latest_events, limit, min_depth
) )
@ -567,6 +568,25 @@ class FederationServer(FederationBase):
len(prevs - seen), pdu.room_id, list(prevs - seen)[:5] len(prevs - seen), pdu.room_id, list(prevs - seen)[:5]
) )
# XXX: we set timeout to 10s to help workaround
# https://github.com/matrix-org/synapse/issues/1733.
# The reason is to avoid holding the linearizer lock
# whilst processing inbound /send transactions, causing
# FDs to stack up and block other inbound transactions
# which empirically can currently take up to 30 minutes.
#
# N.B. this explicitly disables retry attempts.
#
# N.B. this also increases our chances of falling back to
# fetching fresh state for the room if the missing event
# can't be found, which slightly reduces our security.
# it may also increase our DAG extremity count for the room,
# causing additional state resolution? See #1760.
# However, fetching state doesn't hold the linearizer lock
# apparently.
#
# see https://github.com/matrix-org/synapse/pull/1744
missing_events = yield self.get_missing_events( missing_events = yield self.get_missing_events(
origin, origin,
pdu.room_id, pdu.room_id,
@ -574,6 +594,7 @@ class FederationServer(FederationBase):
latest_events=[pdu], latest_events=[pdu],
limit=10, limit=10,
min_depth=min_depth, min_depth=min_depth,
timeout=10000,
) )
# We want to sort these by depth so we process them and # We want to sort these by depth so we process them and

View file

@ -19,7 +19,6 @@ from twisted.internet import defer
from .persistence import TransactionActions from .persistence import TransactionActions
from .units import Transaction, Edu from .units import Transaction, Edu
from synapse.api.constants import EventTypes, Membership
from synapse.api.errors import HttpResponseException from synapse.api.errors import HttpResponseException
from synapse.util.async import run_on_reactor from synapse.util.async import run_on_reactor
from synapse.util.logcontext import preserve_context_over_fn from synapse.util.logcontext import preserve_context_over_fn
@ -62,6 +61,7 @@ class TransactionQueue(object):
self.transport_layer = hs.get_federation_transport_client() self.transport_layer = hs.get_federation_transport_client()
self.clock = hs.get_clock() self.clock = hs.get_clock()
self.is_mine_id = hs.is_mine_id
# Is a mapping from destinations -> deferreds. Used to keep track # Is a mapping from destinations -> deferreds. Used to keep track
# of which destinations have transactions in flight and when they are # of which destinations have transactions in flight and when they are
@ -153,17 +153,32 @@ class TransactionQueue(object):
break break
for event in events: for event in events:
# Only send events for this server.
send_on_behalf_of = event.internal_metadata.get_send_on_behalf_of()
is_mine = self.is_mine_id(event.event_id)
if not is_mine and send_on_behalf_of is None:
continue
# Get the state from before the event.
# We need to make sure that this is the state from before
# the event and not from after it.
# Otherwise if the last member on a server in a room is
# banned then it won't receive the event because it won't
# be in the room after the ban.
users_in_room = yield self.state.get_current_user_in_room( users_in_room = yield self.state.get_current_user_in_room(
event.room_id, latest_event_ids=[event.event_id], event.room_id, latest_event_ids=[
prev_id for prev_id, _ in event.prev_events
],
) )
destinations = set( destinations = set(
get_domain_from_id(user_id) for user_id in users_in_room get_domain_from_id(user_id) for user_id in users_in_room
) )
if send_on_behalf_of is not None:
if event.type == EventTypes.Member: # If we are sending the event on behalf of another server
if event.content["membership"] == Membership.JOIN: # then it already has the event and there is no reason to
destinations.add(get_domain_from_id(event.state_key)) # send the event to it.
destinations.discard(send_on_behalf_of)
logger.debug("Sending %s to %r", event, destinations) logger.debug("Sending %s to %r", event, destinations)

View file

@ -386,7 +386,7 @@ class TransportLayerClient(object):
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function
def get_missing_events(self, destination, room_id, earliest_events, def get_missing_events(self, destination, room_id, earliest_events,
latest_events, limit, min_depth): latest_events, limit, min_depth, timeout):
path = PREFIX + "/get_missing_events/%s" % (room_id,) path = PREFIX + "/get_missing_events/%s" % (room_id,)
content = yield self.client.post_json( content = yield self.client.post_json(
@ -397,7 +397,8 @@ class TransportLayerClient(object):
"min_depth": int(min_depth), "min_depth": int(min_depth),
"earliest_events": earliest_events, "earliest_events": earliest_events,
"latest_events": latest_events, "latest_events": latest_events,
} },
timeout=timeout,
) )
defer.returnValue(content) defer.returnValue(content)

View file

@ -591,6 +591,7 @@ class FederationHandler(BaseHandler):
event_ids = list(extremities.keys()) event_ids = list(extremities.keys())
logger.info("calling resolve_state_groups in _maybe_backfill")
states = yield preserve_context_over_deferred(defer.gatherResults([ states = yield preserve_context_over_deferred(defer.gatherResults([
preserve_fn(self.state_handler.resolve_state_groups)(room_id, [e]) preserve_fn(self.state_handler.resolve_state_groups)(room_id, [e])
for e in event_ids for e in event_ids
@ -790,6 +791,10 @@ class FederationHandler(BaseHandler):
) )
event.internal_metadata.outlier = False event.internal_metadata.outlier = False
# Send this event on behalf of the origin server since they may not
# have an up to data view of the state of the room at this event so
# will not know which servers to send the event to.
event.internal_metadata.send_on_behalf_of = origin
context, event_stream_id, max_stream_id = yield self._handle_new_event( context, event_stream_id, max_stream_id = yield self._handle_new_event(
origin, event origin, event

View file

@ -123,6 +123,7 @@ class StateHandler(object):
if not latest_event_ids: if not latest_event_ids:
latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id) latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id)
logger.info("calling resolve_state_groups from get_current_state")
ret = yield self.resolve_state_groups(room_id, latest_event_ids) ret = yield self.resolve_state_groups(room_id, latest_event_ids)
state = ret.state state = ret.state
@ -147,6 +148,7 @@ class StateHandler(object):
if not latest_event_ids: if not latest_event_ids:
latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id) latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id)
logger.info("calling resolve_state_groups from get_current_state_ids")
ret = yield self.resolve_state_groups(room_id, latest_event_ids) ret = yield self.resolve_state_groups(room_id, latest_event_ids)
state = ret.state state = ret.state
@ -158,6 +160,7 @@ class StateHandler(object):
@defer.inlineCallbacks @defer.inlineCallbacks
def get_current_user_in_room(self, room_id, latest_event_ids=None): def get_current_user_in_room(self, room_id, latest_event_ids=None):
logger.info("calling resolve_state_groups from get_current_user_in_room")
if not latest_event_ids: if not latest_event_ids:
latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id) latest_event_ids = yield self.store.get_latest_event_ids_in_room(room_id)
entry = yield self.resolve_state_groups(room_id, latest_event_ids) entry = yield self.resolve_state_groups(room_id, latest_event_ids)
@ -223,6 +226,7 @@ class StateHandler(object):
context.prev_state_events = [] context.prev_state_events = []
defer.returnValue(context) defer.returnValue(context)
logger.info("calling resolve_state_groups from compute_event_context")
if event.is_state(): if event.is_state():
entry = yield self.resolve_state_groups( entry = yield self.resolve_state_groups(
event.room_id, [e for e, _ in event.prev_events], event.room_id, [e for e, _ in event.prev_events],

View file

@ -1091,8 +1091,10 @@ class EventsStore(SQLBaseStore):
self._do_fetch self._do_fetch
) )
logger.info("Loading %d events", len(events))
with PreserveLoggingContext(): with PreserveLoggingContext():
rows = yield events_d rows = yield events_d
logger.info("Loaded %d events (%d rows)", len(events), len(rows))
if not allow_rejected: if not allow_rejected:
rows[:] = [r for r in rows if not r["rejects"]] rows[:] = [r for r in rows if not r["rejects"]]

View file

@ -393,8 +393,8 @@ class RoomMemberStore(SQLBaseStore):
@cachedInlineCallbacks(num_args=2, cache_context=True) @cachedInlineCallbacks(num_args=2, cache_context=True)
def _get_joined_users_from_context(self, room_id, state_group, current_state_ids, def _get_joined_users_from_context(self, room_id, state_group, current_state_ids,
cache_context, event=None): cache_context, event=None):
# We don't use `state_group`, its there so that we can cache based # We don't use `state_group`, it's there so that we can cache based
# on it. However, its important that its never None, since two current_state's # on it. However, it's important that it's never None, since two current_states
# with a state_group of None are likely to be different. # with a state_group of None are likely to be different.
# See bulk_get_push_rules_for_room for how we work around this. # See bulk_get_push_rules_for_room for how we work around this.
assert state_group is not None assert state_group is not None

View file

@ -384,7 +384,7 @@ class StateStore(SQLBaseStore):
# We did this before by getting the list of group ids, and # We did this before by getting the list of group ids, and
# then passing that list to sqlite to get latest event for # then passing that list to sqlite to get latest event for
# each (type, state_key). However, that was terribly slow # each (type, state_key). However, that was terribly slow
# without the right indicies (which we can't add until # without the right indices (which we can't add until
# after we finish deduping state, which requires this func) # after we finish deduping state, which requires this func)
args = [next_group] args = [next_group]
if types: if types:

View file

@ -166,7 +166,11 @@ class Linearizer(object):
# do some work. # do some work.
""" """
def __init__(self): def __init__(self, name=None):
if name is None:
self.name = id(self)
else:
self.name = name
self.key_to_defer = {} self.key_to_defer = {}
@defer.inlineCallbacks @defer.inlineCallbacks
@ -185,15 +189,20 @@ class Linearizer(object):
self.key_to_defer[key] = new_defer self.key_to_defer[key] = new_defer
if current_defer: if current_defer:
logger.info("Waiting to acquire linearizer lock for key %r", key) logger.info(
"Waiting to acquire linearizer lock %r for key %r", self.name, key
)
with PreserveLoggingContext(): with PreserveLoggingContext():
yield current_defer yield current_defer
logger.info("Acquired linearizer lock %r for key %r", self.name, key)
@contextmanager @contextmanager
def _ctx_manager(): def _ctx_manager():
try: try:
yield yield
finally: finally:
logger.info("Releasing linearizer lock %r for key %r", self.name, key)
new_defer.callback(None) new_defer.callback(None)
current_d = self.key_to_defer.get(key) current_d = self.key_to_defer.get(key)
if current_d is new_defer: if current_d is new_defer: