def setUp(self):
     self.acquire_session = MagicMock()
     self.release_session = MagicMock()
     self.invalidate_session = MagicMock()
     self.session_manager = MagicMock(
         acquire_session=self.acquire_session,
         release_session=self.release_session,
         invalidate_session=self.invalidate_session)
     context = MagicMock(proxy_session_manager=self.session_manager)
     group_id = RaftGroupId("test", 0, 42)
     self.proxy = FencedLock(context, group_id, LOCK_SERVICE,
                             "mylock@mygroup", "mylock").blocking()
예제 #2
0
    def _create_fenced_lock(self, group_id, proxy_name, object_name):
        with self._mux:
            proxy = self._lock_proxies.get(proxy_name, None)
            if proxy:
                if proxy.get_group_id() != group_id:
                    self._lock_proxies.pop(proxy_name, None)
                else:
                    return proxy

            proxy = FencedLock(self._context, group_id, LOCK_SERVICE, proxy_name, object_name)
            self._lock_proxies[proxy_name] = proxy
            return proxy
class FencedLockMockTest(unittest.TestCase):
    def setUp(self):
        self.acquire_session = MagicMock()
        self.release_session = MagicMock()
        self.invalidate_session = MagicMock()
        self.session_manager = MagicMock(
            acquire_session=self.acquire_session,
            release_session=self.release_session,
            invalidate_session=self.invalidate_session,
        )
        context = MagicMock(proxy_session_manager=self.session_manager)
        group_id = RaftGroupId("test", 0, 42)
        self.proxy = FencedLock(context, group_id, LOCK_SERVICE,
                                "mylock@mygroup", "mylock").blocking()

    def test_lock(self):
        # Everything succeeds
        self.prepare_acquire_session(1)
        self.mock_request_lock(2)
        self.assertEqual(2, self.proxy.lock())
        self.assert_call_counts(1, 0, 0)
        self.assert_lock_session_id(1)

    def test_lock_when_acquire_session_fails(self):
        # First call to acquire session fails, should not retry
        self.prepare_acquire_session(-1, HazelcastRuntimeError("server_error"))

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.lock()

        self.assert_call_counts(1, 0, 0)
        self.assert_no_lock_session_id()

    def test_lock_when_server_closes_old_session(self):
        # Same thread issues a new lock call while holding a lock.
        # Server closes session related to the first lock, should not retry
        self.prepare_acquire_session(2)
        self.prepare_lock_session_ids(1)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.lock()

        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_lock_when_lock_acquire_limit_reached(self):
        # Lock acquire limit is reached, server returns invalid fence, should not retry
        self.prepare_acquire_session(1)
        self.mock_request_lock(FencedLock.INVALID_FENCE)

        with self.assertRaises(LockAcquireLimitReachedError):
            self.proxy.lock()

        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_lock_on_session_expired_error(self):
        # Session expired error comes from the server on lock request, retries and gets valid fence
        self.prepare_acquire_session(1)
        self.mock_request_lock(2, SessionExpiredError())
        self.assertEqual(2, self.proxy.lock())
        self.assert_call_counts(2, 0, 1)
        self.assert_lock_session_id(1)

    def test_lock_on_session_expired_error_on_reentrant_lock_request(self):
        # Session expired error comes from the server on second lock request,
        # while holding a lock, should not retry
        self.prepare_acquire_session(1)
        self.prepare_lock_session_ids(1)
        self.mock_request_lock(3, SessionExpiredError())

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.lock()

        self.assert_call_counts(1, 0, 1)
        self.assert_no_lock_session_id()

    def test_lock_on_wait_key_cancelled_error(self):
        # Wait key cancelled error comes from the server, should not retry
        self.prepare_acquire_session(1)
        self.mock_request_lock(2, WaitKeyCancelledError())

        with self.assertRaises(IllegalMonitorStateError):
            self.proxy.lock()

        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_lock_on_unspecified_error(self):
        # Server sends another error, should not retry
        self.prepare_acquire_session(1)
        self.mock_request_lock(-1, HazelcastRuntimeError("expected"))

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.lock()

        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_try_lock(self):
        # Everything succeeds
        self.prepare_acquire_session(1)
        self.mock_request_try_lock(2)
        self.assertEqual(2, self.proxy.try_lock())
        self.assert_call_counts(1, 0, 0)
        self.assert_lock_session_id(1)

    def test_try_lock_when_acquire_session_fails(self):
        # First call to acquire session fails, should not retry
        self.prepare_acquire_session(-1, HazelcastRuntimeError("server_error"))

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.try_lock()

        self.assert_call_counts(1, 0, 0)
        self.assert_no_lock_session_id()

    def test_try_lock_when_server_closes_old_session(self):
        # Same thread issues a new lock call while holding a lock.
        # Server closes session related to the first lock, should not retry
        self.prepare_acquire_session(2)
        self.prepare_lock_session_ids(1)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.try_lock()

        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_try_lock_when_lock_acquire_limit_reached(self):
        # Lock acquire limit is reached, server returns invalid fence
        self.prepare_acquire_session(1)
        self.mock_request_try_lock(FencedLock.INVALID_FENCE)
        self.assertEqual(FencedLock.INVALID_FENCE, self.proxy.try_lock())
        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_try_lock_on_session_expired_error(self):
        # Session expired error comes from the server on lock request,
        # client determines the timeout and returns invalid fence
        self.prepare_acquire_session(1)
        self.mock_request_try_lock(2, SessionExpiredError())
        self.assertEqual(FencedLock.INVALID_FENCE, self.proxy.try_lock())
        self.assert_call_counts(1, 0, 1)
        self.assert_no_lock_session_id()

    def test_try_lock_on_session_expired_error_when_not_timed_out(self):
        # Session expired error comes from the server on lock request,
        # client retries due to not reaching timeout and succeeds
        self.prepare_acquire_session(1)
        self.mock_request_try_lock(2, SessionExpiredError())
        self.assertEqual(2, self.proxy.try_lock(100))
        self.assert_call_counts(2, 0, 1)
        self.assert_lock_session_id(1)

    def test_try_lock_on_session_expired_error_on_reentrant_lock_request(self):
        # Session expired error comes from the server on second lock request,
        # while holding a lock, should not retry
        self.prepare_acquire_session(1)
        self.prepare_lock_session_ids(1)
        self.mock_request_try_lock(3, SessionExpiredError())

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.try_lock()

        self.assert_call_counts(1, 0, 1)
        self.assert_no_lock_session_id()

    def test_try_lock_on_wait_key_cancelled_error(self):
        # Wait key cancelled error comes from the server, invalid fence is returned
        self.prepare_acquire_session(1)
        self.mock_request_try_lock(2, WaitKeyCancelledError())
        self.assertEqual(FencedLock.INVALID_FENCE, self.proxy.try_lock())
        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_try_lock_on_unspecified_error(self):
        # Server sends another error, should not retry
        self.prepare_acquire_session(1)
        self.mock_request_try_lock(-1, HazelcastRuntimeError("expected"))

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.try_lock()

        self.assert_call_counts(1, 1, 0)
        self.assert_no_lock_session_id()

    def test_unlock(self):
        # Everything succeeds
        self.prepare_get_session(2)
        self.mock_request_unlock(True)
        self.proxy.unlock()
        self.assert_call_counts(0, 1, 0)
        self.assert_lock_session_id(
            2)  # Server sent true, client still holds the lock after unlock

    def test_unlock_when_server_closes_old_session(self):
        # Session id is different than what we store in the
        # dict. The old session must be closed while we were
        # holding the lock.
        self.prepare_get_session(2)
        self.prepare_lock_session_ids(1)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.unlock()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_unlock_when_there_is_no_session(self):
        # No active session for the current thread.
        self.prepare_get_session(-1)

        with self.assertRaises(IllegalMonitorStateError):
            self.proxy.unlock()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_unlock_when_client_unlocked_the_locked(self):
        # After the unlock, lock is free
        self.prepare_get_session(1)
        self.mock_request_unlock(False)
        self.proxy.unlock()
        self.assert_call_counts(0, 1, 0)
        self.assert_no_lock_session_id()

    def test_unlock_on_session_expired_error(self):
        # Server sends session expired error
        self.prepare_get_session(1)
        self.mock_request_unlock(None, SessionExpiredError())

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.unlock()

        self.assert_call_counts(0, 0, 1)
        self.assert_no_lock_session_id()

    def test_unlock_on_illegal_monitor_state_error(self):
        # Lock is not held by the current thread, but client
        # thinks that it holds it and sends the request.
        # Server sends illegal monitor state error in response.
        self.prepare_get_session(1)
        self.mock_request_unlock(None, IllegalMonitorStateError())

        with self.assertRaises(IllegalMonitorStateError):
            self.proxy.unlock()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_unlock_on_unspecified_error(self):
        # Server sends an unspecified error
        self.prepare_get_session(1)
        self.mock_request_unlock(None, HazelcastRuntimeError())

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.unlock()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked(self):
        # Everything succeeds, client holds the lock
        self.prepare_get_session(2)
        state = self.prepare_state(3, 1, 2, thread_id())
        self.mock_request_get_lock_ownership_state(state)
        self.assertTrue(self.proxy.is_locked())
        self.assert_call_counts(0, 0, 0)
        self.assert_lock_session_id(2)

    def test_is_locked_when_it_is_locked_by_another_thread(self):
        # Client is not holding the lock, but someone else does.
        self.prepare_get_session(1)
        state = self.prepare_state(3, 1, 2, thread_id() - 1)
        self.mock_request_get_lock_ownership_state(state)
        self.assertTrue(self.proxy.is_locked())
        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_when_free(self):
        # No one holds the lock
        self.prepare_get_session(1)
        state = self.prepare_state(FencedLock.INVALID_FENCE, 0, -1, -1)
        self.mock_request_get_lock_ownership_state(state)
        self.assertFalse(self.proxy.is_locked())
        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_when_server_closes_old_session(self):
        # Session id is different than what we store in the
        # dict. The old session must be closed while we were
        # holding the lock.
        self.prepare_get_session(2)
        self.prepare_lock_session_ids(1)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.is_locked()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_when_server_returns_a_different_thread_id_for_lock_holder(
            self):
        # Client thinks that it holds the lock, but server
        # says it's not.
        self.prepare_get_session(1)
        self.prepare_lock_session_ids(1)
        state = self.prepare_state(3, 1, 2, thread_id() - 1)
        self.mock_request_get_lock_ownership_state(state)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.is_locked()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_on_unspecified_error(self):
        # Server sends an unspecified error
        self.prepare_get_session(1)
        self.mock_request_get_lock_ownership_state(None,
                                                   HazelcastRuntimeError())

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.is_locked()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_by_current_thread(self):
        # Everything succeeds, client holds the lock
        self.prepare_get_session(2)
        state = self.prepare_state(3, 1, 2, thread_id())
        self.mock_request_get_lock_ownership_state(state)
        self.assertTrue(self.proxy.is_locked_by_current_thread())
        self.assert_call_counts(0, 0, 0)
        self.assert_lock_session_id(2)

    def test_is_locked_by_current_thread_when_it_is_locked_by_another_thread(
            self):
        # Client is not holding the lock, but someone else does.
        self.prepare_get_session(1)
        state = self.prepare_state(3, 1, 2, thread_id() - 1)
        self.mock_request_get_lock_ownership_state(state)
        self.assertFalse(self.proxy.is_locked_by_current_thread())
        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_by_current_thread_when_free(self):
        # No one holds the lock
        self.prepare_get_session(1)
        state = self.prepare_state(FencedLock.INVALID_FENCE, 0, -1, -1)
        self.mock_request_get_lock_ownership_state(state)
        self.assertFalse(self.proxy.is_locked_by_current_thread())
        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_by_current_thread_when_server_closes_old_session(self):
        # Session id is different than what we store in the
        # dict. The old session must be closed while we were
        # holding the lock.
        self.prepare_get_session(2)
        self.prepare_lock_session_ids(1)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.is_locked_by_current_thread()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_by_current_thread_when_server_returns_a_different_thread_id_for_lock_holder(
        self, ):
        # Client thinks that it holds the lock, but server
        # says it's not.
        self.prepare_get_session(1)
        self.prepare_lock_session_ids(1)
        state = self.prepare_state(3, 1, 2, thread_id() - 1)
        self.mock_request_get_lock_ownership_state(state)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.is_locked_by_current_thread()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_is_locked_by_current_thread_on_unspecified_error(self):
        # Server sends an unspecified error
        self.prepare_get_session(1)
        self.mock_request_get_lock_ownership_state(None,
                                                   HazelcastRuntimeError())

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.is_locked_by_current_thread()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_get_lock_count(self):
        # Everything succeeds, client holds the lock
        self.prepare_get_session(2)
        state = self.prepare_state(3, 123, 2, thread_id())
        self.mock_request_get_lock_ownership_state(state)
        self.assertEqual(123, self.proxy.get_lock_count())
        self.assert_call_counts(0, 0, 0)
        self.assert_lock_session_id(2)

    def test_get_lock_count_when_it_is_locked_by_another_thread(self):
        # Client is not holding the lock, but someone else does.
        self.prepare_get_session(1)
        state = self.prepare_state(3, 1, 2, thread_id() - 1)
        self.mock_request_get_lock_ownership_state(state)
        self.assertEqual(1, self.proxy.get_lock_count())
        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_get_lock_count_when_free(self):
        # No one holds the lock
        self.prepare_get_session(1)
        state = self.prepare_state(FencedLock.INVALID_FENCE, 0, -1, -1)
        self.mock_request_get_lock_ownership_state(state)
        self.assertEqual(0, self.proxy.get_lock_count())
        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_get_lock_count_when_server_closes_old_session(self):
        # Session id is different than what we store in the
        # dict. The old session must be closed while we were
        # holding the lock.
        self.prepare_get_session(2)
        self.prepare_lock_session_ids(1)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.get_lock_count()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_get_lock_count_when_server_returns_a_different_thread_id_for_lock_holder(
            self):
        # Client thinks that it holds the lock, but server
        # says it's not.
        self.prepare_get_session(1)
        self.prepare_lock_session_ids(1)
        state = self.prepare_state(3, 1, 2, thread_id() - 1)
        self.mock_request_get_lock_ownership_state(state)

        with self.assertRaises(LockOwnershipLostError):
            self.proxy.get_lock_count()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def test_get_lock_count_on_unspecified_error(self):
        # Server sends an unspecified error
        self.prepare_get_session(1)
        self.mock_request_get_lock_ownership_state(None,
                                                   HazelcastRuntimeError())

        with self.assertRaises(HazelcastRuntimeError):
            self.proxy.get_lock_count()

        self.assert_call_counts(0, 0, 0)
        self.assert_no_lock_session_id()

    def prepare_lock_session_ids(self, session_id):
        self.proxy._wrapped._lock_session_ids[thread_id()] = session_id

    def prepare_acquire_session(self, session_id, err=None):
        if err:
            val = ImmediateExceptionFuture(err)
        else:
            val = ImmediateFuture(session_id)

        acquire_mock = MagicMock(return_value=val)
        release_mock = MagicMock()
        invalidate_mock = MagicMock()
        self.session_manager.acquire_session = acquire_mock
        self.session_manager.release_session = release_mock
        self.session_manager.invalidate_session = invalidate_mock
        self.acquire_session = acquire_mock
        self.release_session = release_mock
        self.invalidate_session = invalidate_mock

    def prepare_get_session(self, session_id):
        self.session_manager.get_session_id = MagicMock(
            return_value=session_id)

    def mock_request_lock(self, fence, first_call_err=None):
        self._mock_request("_request_lock", fence, first_call_err)

    def mock_request_unlock(self, result, first_call_err=None):
        self._mock_request("_request_unlock", result, first_call_err)

    def mock_request_try_lock(self, fence, first_call_err=None):
        self._mock_request("_request_try_lock", fence, first_call_err)

    def mock_request_get_lock_ownership_state(self,
                                              state,
                                              first_call_err=None):
        self._mock_request("_request_get_lock_ownership_state", state,
                           first_call_err)

    def _mock_request(self, method_name, result, first_call_err):
        called = AtomicInteger()

        def mock(*_, **__):
            if called.get_and_increment() == 0 and first_call_err:
                return ImmediateExceptionFuture(first_call_err)
            return ImmediateFuture(result)

        setattr(self.proxy._wrapped, method_name, MagicMock(side_effect=mock))

    def assert_call_counts(self, acquire, release, invalidate):
        self.assertEqual(acquire, self.acquire_session.call_count)
        self.assertEqual(release, self.release_session.call_count)
        self.assertEqual(invalidate, self.invalidate_session.call_count)

    def prepare_state(self, fence, lock_count, session_id, t_id):
        return {
            "fence": fence,
            "lock_count": lock_count,
            "session_id": session_id,
            "thread_id": t_id,
        }

    def assert_no_lock_session_id(self):
        self.assertEqual(0, len(self.proxy._wrapped._lock_session_ids))

    def assert_lock_session_id(self, session_id):
        s_id = self.proxy._wrapped._lock_session_ids.get(thread_id(), None)
        self.assertEqual(session_id, s_id)