diff --git a/changelog.d/4881.misc b/changelog.d/4881.misc
new file mode 100644
index 0000000000000000000000000000000000000000..308c21c83930d98febdc0f913fbe57726e9c4456
--- /dev/null
+++ b/changelog.d/4881.misc
@@ -0,0 +1 @@
+Update link to federation docs.
diff --git a/changelog.d/4886.bugfix b/changelog.d/4886.bugfix
new file mode 100644
index 0000000000000000000000000000000000000000..b17aa92485d4addfefcf5492e66b6b138d91f40e
--- /dev/null
+++ b/changelog.d/4886.bugfix
@@ -0,0 +1 @@
+fix test_auto_create_auto_join_where_no_consent.
diff --git a/changelog.d/4886.misc b/changelog.d/4886.misc
new file mode 100644
index 0000000000000000000000000000000000000000..b17aa92485d4addfefcf5492e66b6b138d91f40e
--- /dev/null
+++ b/changelog.d/4886.misc
@@ -0,0 +1 @@
+fix test_auto_create_auto_join_where_no_consent.
diff --git a/changelog.d/4887.feature b/changelog.d/4887.feature
new file mode 100644
index 0000000000000000000000000000000000000000..e7ff0b9297aea5ac028733af00fc78da15c576b1
--- /dev/null
+++ b/changelog.d/4887.feature
@@ -0,0 +1 @@
+The user directory has been rewritten to make it faster, with less chance of falling behind on a large server. 
diff --git a/docs/federate.md b/docs/federate.md
index 186245a94b33f824d1926617d3d50c02a34b528d..b7fc09661ce3a87f6eafa76a5ee3c41b543fe8a5 100644
--- a/docs/federate.md
+++ b/docs/federate.md
@@ -15,8 +15,8 @@ machine's public DNS hostname, and provide Synapse with a TLS certificate
 which is valid for your ``server_name``.
 
 Once you have completed the steps necessary to federate, you should be able to 
-join a room via federation. (A good place to start is ``#synapse:matrix.org``
-- a room for Synapse admins.)
+join a room via federation. (A good place to start is ``#synapse:matrix.org`` - a 
+room for Synapse admins.)
 
 
 ## Delegation
@@ -89,7 +89,6 @@ In our example, we would need to add this SRV record in the
 
      _matrix._tcp.example.com. 3600 IN SRV 10 5 443 synapse.example.com.
 
-
 Once done and set up, you can check the DNS record with ``dig -t srv
 _matrix._tcp.<server_name>``. In our example, we would expect this:
 
@@ -117,7 +116,6 @@ you invite them to. This can be caused by an incorrectly-configured reverse
 proxy: see [reverse_proxy.rst](<reverse_proxy.rst>) for instructions on how to correctly
 configure a reverse proxy.
 
-
 ## Running a Demo Federation of Synapses
 
 If you want to get up and running quickly with a trio of homeservers in a
diff --git a/docs/reverse_proxy.rst b/docs/reverse_proxy.rst
index 6cd129abf4041d42a67509427c03a5fef1c5fc80..8e26c50f1ba9f271b13e63bc5277207c18bf310c 100644
--- a/docs/reverse_proxy.rst
+++ b/docs/reverse_proxy.rst
@@ -18,7 +18,7 @@ servers do not necessarily need to connect to your server via the same server
 name or port. Indeed, clients will use port 443 by default, whereas servers
 default to port 8448. Where these are different, we refer to the 'client port'
 and the 'federation port'. See `Setting up federation
-<../README.rst#setting-up-federation>`_ for more details of the algorithm used for
+<federate.md>`_ for more details of the algorithm used for
 federation connections.
 
 Let's assume that we expect clients to connect to our server at
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index c762b58902c86b2f3ec2beffc84393b239b8a243..55787563c001e04f6d199377fa0198658adedbc7 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -243,7 +243,14 @@ class EventCreationHandler(object):
 
         self.spam_checker = hs.get_spam_checker()
 
-        if self.config.block_events_without_consent_error is not None:
+        self._block_events_without_consent_error = (
+            self.config.block_events_without_consent_error
+        )
+
+        # we need to construct a ConsentURIBuilder here, as it checks that the necessary
+        # config options, but *only* if we have a configuration for which we are
+        # going to need it.
+        if self._block_events_without_consent_error:
             self._consent_uri_builder = ConsentURIBuilder(self.config)
 
     @defer.inlineCallbacks
@@ -378,7 +385,7 @@ class EventCreationHandler(object):
         Raises:
             ConsentNotGivenError: if the user has not given consent yet
         """
-        if self.config.block_events_without_consent_error is None:
+        if self._block_events_without_consent_error is None:
             return
 
         # exempt AS users from needing consent
@@ -405,7 +412,7 @@ class EventCreationHandler(object):
         consent_uri = self._consent_uri_builder.build_user_consent_uri(
             requester.user.localpart,
         )
-        msg = self.config.block_events_without_consent_error % {
+        msg = self._block_events_without_consent_error % {
             'consent_uri': consent_uri,
         }
         raise ConsentNotGivenError(
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index 0ec16b1d2e3d3a64ebd5254a3cbb98167e323000..68f73d37935f0dd68647bd425eacd576ba1c6b30 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -23,6 +23,7 @@ from synapse.api.constants import LoginType
 from synapse.api.errors import (
     AuthError,
     Codes,
+    ConsentNotGivenError,
     InvalidCaptchaError,
     LimitExceededError,
     RegistrationError,
@@ -311,6 +312,10 @@ class RegistrationHandler(BaseHandler):
                         )
                 else:
                     yield self._join_user_to_room(fake_requester, r)
+            except ConsentNotGivenError as e:
+                # Technically not necessary to pull out this error though
+                # moving away from bare excepts is a good thing to do.
+                logger.error("Failed to join new user to %r: %r", r, e)
             except Exception as e:
                 logger.error("Failed to join new user to %r: %r", r, e)
 
diff --git a/synapse/storage/schema/delta/53/user_dir_populate.sql b/synapse/storage/schema/delta/53/user_dir_populate.sql
index 955b8fdbd6983661dd4c070e4d6c5d19d9de0db7..ffcc896b588732a49109264fae03dd0cd5385cd3 100644
--- a/synapse/storage/schema/delta/53/user_dir_populate.sql
+++ b/synapse/storage/schema/delta/53/user_dir_populate.sql
@@ -23,7 +23,7 @@ INSERT INTO background_updates (update_name, progress_json, depends_on) VALUES
 
 -- Insert all users, if search_all_users is on
 INSERT INTO background_updates (update_name, progress_json, depends_on) VALUES
-    ('populate_user_directory_process_users', '{}', 'populate_user_directory_rooms');
+    ('populate_user_directory_process_users', '{}', 'populate_user_directory_process_rooms');
 
 -- Clean up staging tables
 INSERT INTO background_updates (update_name, progress_json, depends_on) VALUES
diff --git a/tests/handlers/test_register.py b/tests/handlers/test_register.py
index c9c1506273055a30d0aba449c3b85ab06ac355be..010e65829ed10dd492064c8d641fc7f5723e785c 100644
--- a/tests/handlers/test_register.py
+++ b/tests/handlers/test_register.py
@@ -187,12 +187,32 @@ class RegistrationTestCase(unittest.TestCase):
 
     @defer.inlineCallbacks
     def test_auto_create_auto_join_where_no_consent(self):
-        self.hs.config.user_consent_at_registration = True
-        self.hs.config.block_events_without_consent_error = "Error"
+        """Test to ensure that the first user is not auto-joined to a room if
+        they have not given general consent.
+        """
+
+        # Given:-
+        #    * a user must give consent,
+        #    * they have not given that consent
+        #    * The server is configured to auto-join to a room
+        # (and autocreate if necessary)
+
+        event_creation_handler = self.hs.get_event_creation_handler()
+        # (Messing with the internals of event_creation_handler is fragile
+        # but can't see a better way to do this. One option could be to subclass
+        # the test with custom config.)
+        event_creation_handler._block_events_without_consent_error = ("Error")
+        event_creation_handler._consent_uri_builder = Mock()
         room_alias_str = "#room:test"
         self.hs.config.auto_join_rooms = [room_alias_str]
+
+        # When:-
+        #   * the user is registered and post consent actions are called
         res = yield self.handler.register(localpart='jeff')
         yield self.handler.post_consent_actions(res[0])
+
+        # Then:-
+        #   * Ensure that they have not been joined to the room
         rooms = yield self.store.get_rooms_for_user(res[0])
         self.assertEqual(len(rooms), 0)