22
22
Awaitable ,
23
23
Callable ,
24
24
Collection ,
25
+ Container ,
25
26
Dict ,
26
27
Iterable ,
27
28
List ,
@@ -513,6 +514,7 @@ async def _try_destination_list(
513
514
description : str ,
514
515
destinations : Iterable [str ],
515
516
callback : Callable [[str ], Awaitable [T ]],
517
+ failover_errcodes : Optional [Container [str ]] = None ,
516
518
failover_on_unknown_endpoint : bool = False ,
517
519
) -> T :
518
520
"""Try an operation on a series of servers, until it succeeds
@@ -533,6 +535,9 @@ async def _try_destination_list(
533
535
next server tried. Normally the stacktrace is logged but this is
534
536
suppressed if the exception is an InvalidResponseError.
535
537
538
+ failover_errcodes: Error codes (specific to this endpoint) which should
539
+ cause a failover.
540
+
536
541
failover_on_unknown_endpoint: if True, we will try other servers if it looks
537
542
like a server doesn't support the endpoint. This is typically useful
538
543
if the endpoint in question is new or experimental.
@@ -544,6 +549,9 @@ async def _try_destination_list(
544
549
SynapseError if the chosen remote server returns a 300/400 code, or
545
550
no servers were reachable.
546
551
"""
552
+ if failover_errcodes is None :
553
+ failover_errcodes = ()
554
+
547
555
for destination in destinations :
548
556
if destination == self .server_name :
549
557
continue
@@ -558,11 +566,17 @@ async def _try_destination_list(
558
566
synapse_error = e .to_synapse_error ()
559
567
failover = False
560
568
561
- # Failover on an internal server error, or if the destination
562
- # doesn't implemented the endpoint for some reason.
569
+ # Failover should occur:
570
+ #
571
+ # * On internal server errors.
572
+ # * If the destination responds that it cannot complete the request.
573
+ # * If the destination doesn't implemented the endpoint for some reason.
563
574
if 500 <= e .code < 600 :
564
575
failover = True
565
576
577
+ elif e .code == 400 and synapse_error .errcode in failover_errcodes :
578
+ failover = True
579
+
566
580
elif failover_on_unknown_endpoint and self ._is_unknown_endpoint (
567
581
e , synapse_error
568
582
):
@@ -678,8 +692,20 @@ async def send_request(destination: str) -> Tuple[str, EventBase, RoomVersion]:
678
692
679
693
return destination , ev , room_version
680
694
695
+ # MSC3083 defines additional error codes for room joins. Unfortunately
696
+ # we do not yet know the room version, assume these will only be returned
697
+ # by valid room versions.
698
+ failover_errcodes = (
699
+ (Codes .UNABLE_AUTHORISE_JOIN , Codes .UNABLE_TO_GRANT_JOIN )
700
+ if membership == Membership .JOIN
701
+ else None
702
+ )
703
+
681
704
return await self ._try_destination_list (
682
- "make_" + membership , destinations , send_request
705
+ "make_" + membership ,
706
+ destinations ,
707
+ send_request ,
708
+ failover_errcodes = failover_errcodes ,
683
709
)
684
710
685
711
async def send_join (
@@ -818,7 +844,14 @@ async def _execute(pdu: EventBase) -> None:
818
844
origin = destination ,
819
845
)
820
846
847
+ # MSC3083 defines additional error codes for room joins.
848
+ failover_errcodes = None
821
849
if room_version .msc3083_join_rules :
850
+ failover_errcodes = (
851
+ Codes .UNABLE_AUTHORISE_JOIN ,
852
+ Codes .UNABLE_TO_GRANT_JOIN ,
853
+ )
854
+
822
855
# If the join is being authorised via allow rules, we need to send
823
856
# the /send_join back to the same server that was originally used
824
857
# with /make_join.
@@ -827,7 +860,9 @@ async def _execute(pdu: EventBase) -> None:
827
860
get_domain_from_id (pdu .content ["join_authorised_via_users_server" ])
828
861
]
829
862
830
- return await self ._try_destination_list ("send_join" , destinations , send_request )
863
+ return await self ._try_destination_list (
864
+ "send_join" , destinations , send_request , failover_errcodes = failover_errcodes
865
+ )
831
866
832
867
async def _do_send_join (
833
868
self , room_version : RoomVersion , destination : str , pdu : EventBase
0 commit comments