def __init__(self, hs):
        self.hs = hs
        self.auth = hs.get_auth()
        self.store = hs.get_datastore()
        self.clock = hs.get_clock()

        self.pagination_lock = ReadWriteLock()
        self._purges_in_progress_by_room = set()
        # map from purge id to PurgeStatus
        self._purges_by_id = {}
Exemple #2
0
    def test_rwlock(self):
        rwlock = ReadWriteLock()

        key = object()

        ds = [
            rwlock.read(key),  # 0
            rwlock.read(key),  # 1
            rwlock.write(key),  # 2
            rwlock.write(key),  # 3
            rwlock.read(key),  # 4
            rwlock.read(key),  # 5
            rwlock.write(key),  # 6
        ]
        ds = [defer.ensureDeferred(d) for d in ds]

        self._assert_called_before_not_after(ds, 2)

        with ds[0].result:
            self._assert_called_before_not_after(ds, 2)
        self._assert_called_before_not_after(ds, 2)

        with ds[1].result:
            self._assert_called_before_not_after(ds, 2)
        self._assert_called_before_not_after(ds, 3)

        with ds[2].result:
            self._assert_called_before_not_after(ds, 3)
        self._assert_called_before_not_after(ds, 4)

        with ds[3].result:
            self._assert_called_before_not_after(ds, 4)
        self._assert_called_before_not_after(ds, 6)

        with ds[5].result:
            self._assert_called_before_not_after(ds, 6)
        self._assert_called_before_not_after(ds, 6)

        with ds[4].result:
            self._assert_called_before_not_after(ds, 6)
        self._assert_called_before_not_after(ds, 7)

        with ds[6].result:
            pass

        d = defer.ensureDeferred(rwlock.write(key))
        self.assertTrue(d.called)
        with d.result:
            pass

        d = defer.ensureDeferred(rwlock.read(key))
        self.assertTrue(d.called)
        with d.result:
            pass
Exemple #3
0
    def test_cancellation_while_waiting_for_read_lock(self):
        """Test cancellation while waiting for a read lock.

        Tests that cancelling a waiting reader:
         * does not cancel the writer it is waiting on
         * does not cancel the next writer waiting on it
         * does not allow the next writer to acquire the lock before an earlier writer
           has finished
         * does not keep the next writer waiting indefinitely

        These correspond to the asserts with explicit messages.
        """
        rwlock = ReadWriteLock()
        key = "key"

        # 1. A writer takes the lock and blocks.
        writer1_d, _, unblock_writer1 = self._start_blocking_writer(
            rwlock, key, "write 1 completed")

        # 2. A reader waits for the first writer to complete.
        #    This reader will be cancelled later.
        reader_d, _ = self._start_nonblocking_reader(rwlock, key,
                                                     "read completed")
        self.assertFalse(reader_d.called)

        # 3. A second writer waits for both the first writer and the reader to complete.
        writer2_d, _ = self._start_nonblocking_writer(rwlock, key,
                                                      "write 2 completed")
        self.assertFalse(writer2_d.called)

        # 4. The waiting reader is cancelled.
        #    Neither of the writers should be cancelled.
        #    The second writer should still be waiting, but only on the first writer.
        reader_d.cancel()
        self.failureResultOf(reader_d, CancelledError)
        self.assertFalse(writer1_d.called,
                         "First writer was unexpectedly cancelled")
        self.assertFalse(
            writer2_d.called,
            "Second writer was unexpectedly cancelled or given the lock before the "
            "first writer finished",
        )

        # 5. Unblock the first writer, which should complete.
        unblock_writer1.callback(None)
        self.assertEqual("write 1 completed", self.successResultOf(writer1_d))

        # 6. The second writer should take the lock and complete.
        self.assertTrue(
            writer2_d.called,
            "Second writer is stuck waiting for a cancelled reader")
        self.assertEqual("write 2 completed", self.successResultOf(writer2_d))
Exemple #4
0
    def __init__(self, hs):
        self.hs = hs
        self.auth = hs.get_auth()
        self.store = hs.get_datastore()
        self.storage = hs.get_storage()
        self.state_store = self.storage.state
        self.clock = hs.get_clock()
        self._server_name = hs.hostname

        self.pagination_lock = ReadWriteLock()
        self._purges_in_progress_by_room = set()
        # map from purge id to PurgeStatus
        self._purges_by_id = {}
        self._event_serializer = hs.get_event_client_serializer()
Exemple #5
0
    def __init__(self, hs: "HomeServer"):
        self.hs = hs
        self.auth = hs.get_auth()
        self.store = hs.get_datastores().main
        self._storage_controllers = hs.get_storage_controllers()
        self._state_storage_controller = self._storage_controllers.state
        self.clock = hs.get_clock()
        self._server_name = hs.hostname
        self._room_shutdown_handler = hs.get_room_shutdown_handler()
        self._relations_handler = hs.get_relations_handler()

        self.pagination_lock = ReadWriteLock()
        # IDs of rooms in which there currently an active purge *or delete* operation.
        self._purges_in_progress_by_room: Set[str] = set()
        # map from purge id to PurgeStatus
        self._purges_by_id: Dict[str, PurgeStatus] = {}
        # map from purge id to DeleteStatus
        self._delete_by_id: Dict[str, DeleteStatus] = {}
        # map from room id to delete ids
        # Dict[`room_id`, List[`delete_id`]]
        self._delete_by_room: Dict[str, List[str]] = {}
        self._event_serializer = hs.get_event_client_serializer()

        self._retention_default_max_lifetime = (
            hs.config.retention.retention_default_max_lifetime)

        self._retention_allowed_lifetime_min = (
            hs.config.retention.retention_allowed_lifetime_min)
        self._retention_allowed_lifetime_max = (
            hs.config.retention.retention_allowed_lifetime_max)

        if (hs.config.worker.run_background_tasks
                and hs.config.retention.retention_enabled):
            # Run the purge jobs described in the configuration file.
            for job in hs.config.retention.retention_purge_jobs:
                logger.info("Setting up purge job with config: %s", job)

                self.clock.looping_call(
                    run_as_background_process,
                    job.interval,
                    "purge_history_for_rooms_in_range",
                    self.purge_history_for_rooms_in_range,
                    job.shortest_max_lifetime,
                    job.longest_max_lifetime,
                )
Exemple #6
0
    def test_lock_handoff_to_nonblocking_writer(self):
        """Test a writer handing the lock to another writer that completes instantly."""
        rwlock = ReadWriteLock()
        key = "key"

        d1, _, unblock = self._start_blocking_writer(rwlock, key,
                                                     "write 1 completed")
        d2, _ = self._start_nonblocking_writer(rwlock, key,
                                               "write 2 completed")
        self.assertFalse(d1.called)
        self.assertFalse(d2.called)

        # Unblock the first writer. The second writer will complete without blocking.
        unblock.callback(None)
        self.assertTrue(d1.called)
        self.assertTrue(d2.called)

        # The `ReadWriteLock` should operate as normal.
        d3, _ = self._start_nonblocking_writer(rwlock, key,
                                               "write 3 completed")
        self.assertTrue(d3.called)
Exemple #7
0
    def __init__(self, hs: "HomeServer"):
        self.hs = hs
        self.auth = hs.get_auth()
        self.store = hs.get_datastore()
        self.storage = hs.get_storage()
        self.state_store = self.storage.state
        self.clock = hs.get_clock()
        self._server_name = hs.hostname

        self.pagination_lock = ReadWriteLock()
        self._purges_in_progress_by_room: Set[str] = set()
        # map from purge id to PurgeStatus
        self._purges_by_id: Dict[str, PurgeStatus] = {}
        self._event_serializer = hs.get_event_client_serializer()

        self._retention_default_max_lifetime = (
            hs.config.server.retention_default_max_lifetime)

        self._retention_allowed_lifetime_min = (
            hs.config.server.retention_allowed_lifetime_min)
        self._retention_allowed_lifetime_max = (
            hs.config.server.retention_allowed_lifetime_max)

        if hs.config.worker.run_background_tasks and hs.config.server.retention_enabled:
            # Run the purge jobs described in the configuration file.
            for job in hs.config.server.retention_purge_jobs:
                logger.info("Setting up purge job with config: %s", job)

                self.clock.looping_call(
                    run_as_background_process,
                    job.interval,
                    "purge_history_for_rooms_in_range",
                    self.purge_history_for_rooms_in_range,
                    job.shortest_max_lifetime,
                    job.longest_max_lifetime,
                )
Exemple #8
0
    def test_cancellation_while_waiting_for_write_lock(self):
        """Test cancellation while waiting for a write lock.

        Tests that cancelling a waiting writer:
         * does not cancel the reader or writer it is waiting on
         * does not cancel the next writer waiting on it
         * does not allow the next writer to acquire the lock before an earlier reader
           and writer have finished
         * does not keep the next writer waiting indefinitely

        These correspond to the asserts with explicit messages.
        """
        rwlock = ReadWriteLock()
        key = "key"

        # 1. A reader takes the lock and blocks.
        reader_d, _, unblock_reader = self._start_blocking_reader(
            rwlock, key, "read completed")

        # 2. A writer waits for the reader to complete.
        writer1_d, _, unblock_writer1 = self._start_blocking_writer(
            rwlock, key, "write 1 completed")

        # 3. A second writer waits for both the reader and first writer to complete.
        #    This writer will be cancelled later.
        writer2_d, _ = self._start_nonblocking_writer(rwlock, key,
                                                      "write 2 completed")
        self.assertFalse(writer2_d.called)

        # 4. A third writer waits for the second writer to complete.
        writer3_d, _ = self._start_nonblocking_writer(rwlock, key,
                                                      "write 3 completed")
        self.assertFalse(writer3_d.called)

        # 5. The second writer is cancelled, but continues waiting for the lock.
        #    The reader, first writer and third writer should not be cancelled.
        #    The first writer should still be waiting on the reader.
        #    The third writer should still be waiting on the second writer.
        writer2_d.cancel()
        self.assertNoResult(writer2_d)
        self.assertFalse(reader_d.called, "Reader was unexpectedly cancelled")
        self.assertFalse(writer1_d.called,
                         "First writer was unexpectedly cancelled")
        self.assertFalse(
            writer3_d.called,
            "Third writer was unexpectedly cancelled or given the lock before the first "
            "writer finished",
        )

        # 6. Unblock the reader, which should complete.
        #    The first writer should be given the lock and block.
        #    The third writer should still be waiting on the second writer.
        unblock_reader.callback(None)
        self.assertEqual("read completed", self.successResultOf(reader_d))
        self.assertNoResult(writer2_d)
        self.assertFalse(
            writer3_d.called,
            "Third writer was unexpectedly given the lock before the first writer "
            "finished",
        )

        # 7. Unblock the first writer, which should complete.
        unblock_writer1.callback(None)
        self.assertEqual("write 1 completed", self.successResultOf(writer1_d))

        # 8. The second writer should take the lock and release it immediately, since it
        #    has been cancelled.
        self.failureResultOf(writer2_d, CancelledError)

        # 9. The third writer should take the lock and complete.
        self.assertTrue(
            writer3_d.called,
            "Third writer is stuck waiting for a cancelled writer")
        self.assertEqual("write 3 completed", self.successResultOf(writer3_d))
Exemple #9
0
    def test_rwlock(self):
        rwlock = ReadWriteLock()
        key = "key"

        ds = [
            self._start_blocking_reader(rwlock, key, "0"),
            self._start_blocking_reader(rwlock, key, "1"),
            self._start_blocking_writer(rwlock, key, "2"),
            self._start_blocking_writer(rwlock, key, "3"),
            self._start_blocking_reader(rwlock, key, "4"),
            self._start_blocking_reader(rwlock, key, "5"),
            self._start_blocking_writer(rwlock, key, "6"),
        ]
        # `Deferred`s that resolve when each reader or writer acquires the lock.
        acquired_ds = [acquired_d for _, acquired_d, _ in ds]
        # `Deferred`s that will trigger the release of locks when resolved.
        release_ds = [release_d for _, _, release_d in ds]

        # The first two readers should acquire their locks.
        self._assert_first_n_resolved(acquired_ds, 2)

        # Release one of the read locks. The next writer should not acquire the lock,
        # because there is another reader holding the lock.
        self._assert_first_n_resolved(acquired_ds, 2)
        release_ds[0].callback(None)
        self._assert_first_n_resolved(acquired_ds, 2)

        # Release the other read lock. The next writer should acquire the lock.
        self._assert_first_n_resolved(acquired_ds, 2)
        release_ds[1].callback(None)
        self._assert_first_n_resolved(acquired_ds, 3)

        # Release the write lock. The next writer should acquire the lock.
        self._assert_first_n_resolved(acquired_ds, 3)
        release_ds[2].callback(None)
        self._assert_first_n_resolved(acquired_ds, 4)

        # Release the write lock. The next two readers should acquire locks.
        self._assert_first_n_resolved(acquired_ds, 4)
        release_ds[3].callback(None)
        self._assert_first_n_resolved(acquired_ds, 6)

        # Release one of the read locks. The next writer should not acquire the lock,
        # because there is another reader holding the lock.
        self._assert_first_n_resolved(acquired_ds, 6)
        release_ds[5].callback(None)
        self._assert_first_n_resolved(acquired_ds, 6)

        # Release the other read lock. The next writer should acquire the lock.
        self._assert_first_n_resolved(acquired_ds, 6)
        release_ds[4].callback(None)
        self._assert_first_n_resolved(acquired_ds, 7)

        # Release the write lock.
        release_ds[6].callback(None)

        # Acquire and release the write and read locks one last time for good measure.
        _, acquired_d = self._start_nonblocking_writer(rwlock, key,
                                                       "last writer")
        self.assertTrue(acquired_d.called)

        _, acquired_d = self._start_nonblocking_reader(rwlock, key,
                                                       "last reader")
        self.assertTrue(acquired_d.called)