def setUp(self):
     ActorRuntime._actor_managers = {}
     ActorRuntime.set_actor_config(ActorRuntimeConfig())
     self._serializer = DefaultJSONSerializer()
     _run(ActorRuntime.register_actor(FakeSimpleActor))
     _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))
     _run(ActorRuntime.register_actor(FakeSimpleTimerActor))
Exemple #2
0
    async def register_actor(
        cls,
        actor: Type[Actor],
        message_serializer: Serializer = DefaultJSONSerializer(),
        state_serializer: Serializer = DefaultJSONSerializer(),
        http_timeout_seconds: int = settings.DAPR_HTTP_TIMEOUT_SECONDS
    ) -> None:
        """Registers an :class:`Actor` object with the runtime.

        Args:
            actor (:class:`Actor`): Actor implementation.
            message_serializer (:class:`Serializer`): A serializer that serializes message
                between actors.
            state_serializer (:class:`Serializer`): Serializer that serializes state values.
            http_timeout_seconds (:int:): a configurable timeout value
        """
        type_info = ActorTypeInformation.create(actor)
        # TODO: We will allow to use gRPC client later.
        actor_client = DaprActorHttpClient(message_serializer,
                                           timeout=http_timeout_seconds)
        ctx = ActorRuntimeContext(type_info, message_serializer,
                                  state_serializer, actor_client)

        # Create an ActorManager, override existing entry if registered again.
        async with cls._actor_managers_lock:
            cls._actor_managers[type_info.type_name] = ActorManager(ctx)
            cls._actor_config.update_entities(
                ActorRuntime.get_registered_actor_types())
 def setUp(self):
     ActorRuntime._actor_managers = {}
     ActorRuntime.set_actor_config(
         ActorRuntimeConfig(reentrancy=ActorReentrancyConfig(enabled=True)))
     self._serializer = DefaultJSONSerializer()
     _run(ActorRuntime.register_actor(FakeReentrantActor))
     _run(ActorRuntime.register_actor(FakeSlowReentrantActor))
     _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))
    def setUp(self):
        self._test_type_info = ActorTypeInformation.create(FakeMultiInterfacesActor)
        self._serializer = DefaultJSONSerializer()

        self._fake_client = FakeDaprActorClient
        self._runtime_ctx = ActorRuntimeContext(
            self._test_type_info, self._serializer,
            self._serializer, self._fake_client)
        self._manager = ActorManager(self._runtime_ctx)
    def setUp(self):
        self._serializer = DefaultJSONSerializer()
        self._fake_client = FakeDaprActorClient

        self._test_reminder_req = self._serializer.serialize({
            'name': 'test_reminder',
            'dueTime': timedelta(seconds=1),
            'period': timedelta(seconds=1),
            'data': 'cmVtaW5kZXJfc3RhdGU=',
        })
class ActorManagerReminderTests(unittest.TestCase):
    def setUp(self):
        self._serializer = DefaultJSONSerializer()
        self._fake_client = FakeDaprActorClient

        self._test_reminder_req = self._serializer.serialize({
            'name': 'test_reminder',
            'dueTime': timedelta(seconds=1),
            'period': timedelta(seconds=1),
            'ttl': timedelta(seconds=1),
            'data': 'cmVtaW5kZXJfc3RhdGU=',
        })

    def test_fire_reminder_for_non_reminderable(self):
        test_type_info = ActorTypeInformation.create(FakeSimpleActor)
        ctx = ActorRuntimeContext(
            test_type_info, self._serializer,
            self._serializer, self._fake_client)
        manager = ActorManager(ctx)
        with self.assertRaises(ValueError):
            _run(manager.fire_reminder(ActorId('testid'), 'test_reminder', self._test_reminder_req))

    def test_fire_reminder_success(self):
        test_actor_id = ActorId('testid')
        test_type_info = ActorTypeInformation.create(FakeSimpleReminderActor)
        ctx = ActorRuntimeContext(
            test_type_info, self._serializer,
            self._serializer, self._fake_client)
        manager = ActorManager(ctx)
        _run(manager.activate_actor(test_actor_id))
        _run(manager.fire_reminder(test_actor_id, 'test_reminder', self._test_reminder_req))
Exemple #7
0
 def __init__(
         self,
         message_serializer=DefaultJSONSerializer(),
         http_timeout_seconds: int = settings.DAPR_HTTP_TIMEOUT_SECONDS):
     # TODO: support serializer for state store later
     self._dapr_client = DaprActorHttpClient(message_serializer, timeout=http_timeout_seconds)
     self._message_serializer = message_serializer
 def setUp(self):
     self._testActorTypeInfo = ActorTypeInformation.create(FakeSimpleActor)
     self._serializer = DefaultJSONSerializer()
     self._fake_client = FakeDaprActorClient
     self._fake_runtime_ctx = ActorRuntimeContext(self._testActorTypeInfo,
                                                  self._serializer,
                                                  self._serializer,
                                                  self._fake_client)
Exemple #9
0
class ActorManagerTests(unittest.TestCase):
    def setUp(self):
        self._test_type_info = ActorTypeInformation.create(
            FakeMultiInterfacesActor)
        self._serializer = DefaultJSONSerializer()

        self._fake_client = FakeDaprActorClient
        self._runtime_ctx = ActorRuntimeContext(self._test_type_info,
                                                self._serializer,
                                                self._serializer,
                                                self._fake_client)
        self._manager = ActorManager(self._runtime_ctx)

    def test_activate_actor(self):
        """Activate ActorId(1)"""
        test_actor_id = ActorId('1')
        _run(self._manager.activate_actor(test_actor_id))

        # assert
        self.assertEqual(test_actor_id,
                         self._manager._active_actors[test_actor_id.id].id)
        self.assertTrue(
            self._manager._active_actors[test_actor_id.id].activated)
        self.assertFalse(
            self._manager._active_actors[test_actor_id.id].deactivated)

    def test_deactivate_actor(self):
        """Activate ActorId('2') and deactivate it"""
        test_actor_id = ActorId('2')
        _run(self._manager.activate_actor(test_actor_id))

        # assert
        self.assertEqual(test_actor_id,
                         self._manager._active_actors[test_actor_id.id].id)
        self.assertTrue(
            self._manager._active_actors[test_actor_id.id].activated)
        self.assertFalse(
            self._manager._active_actors[test_actor_id.id].deactivated)

        _run(self._manager.deactivate_actor(test_actor_id))
        self.assertIsNone(self._manager._active_actors.get(test_actor_id.id))

    def test_dispatch_success(self):
        """dispatch ActionMethod"""
        test_actor_id = ActorId('dispatch')
        _run(self._manager.activate_actor(test_actor_id))

        request_body = {
            "message": "hello dapr",
        }

        test_request_body = self._serializer.serialize(request_body)
        response = _run(
            self._manager.dispatch(test_actor_id, "ActionMethod",
                                   test_request_body))
        self.assertEqual(b'"hello dapr"', response)
    def __init__(
            self,
            timeout: int = 60,
            headers_callback: Optional[Callable[[], Dict[str, str]]] = None):
        """Invokes Dapr's API for method invocation over HTTP.

        Args:
            timeout (int, optional): Timeout in seconds, defaults to 60.
            headers_callback (lambda: Dict[str, str]], optional): Generates header for each request.
        """
        self._client = DaprHttpClient(DefaultJSONSerializer(), timeout, headers_callback)
Exemple #11
0
    def setUp(self):
        # Create mock client
        self._fake_client = FakeDaprActorClient

        self._test_actor_id = ActorId('1')
        self._test_type_info = ActorTypeInformation.create(FakeSimpleActor)
        self._serializer = DefaultJSONSerializer()
        self._runtime_ctx = ActorRuntimeContext(self._test_type_info,
                                                self._serializer,
                                                self._serializer,
                                                self._fake_client)
        self._fake_actor = FakeSimpleActor(self._runtime_ctx,
                                           self._test_actor_id)
Exemple #12
0
    def __init__(self, app=None):
        self._app = app
        self._dapr_serializer = DefaultJSONSerializer()

        if app is not None:
            self.init_routes(app)
Exemple #13
0
class ActorTests(unittest.TestCase):
    def setUp(self):
        ActorRuntime._actor_managers = {}
        ActorRuntime.set_actor_config(ActorRuntimeConfig())
        self._serializer = DefaultJSONSerializer()
        _run(ActorRuntime.register_actor(FakeSimpleActor))
        _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))

    def test_get_registered_actor_types(self):
        actor_types = ActorRuntime.get_registered_actor_types()
        self.assertTrue(actor_types.index('FakeSimpleActor') >= 0)
        self.assertTrue(actor_types.index(FakeMultiInterfacesActor.__name__) >= 0)

    def test_actor_config(self):
        config = ActorRuntime.get_actor_config()

        self.assertTrue(config._drain_rebalanced_actors)
        self.assertEqual(timedelta(hours=1), config._actor_idle_timeout)
        self.assertEqual(timedelta(seconds=30), config._actor_scan_interval)
        self.assertEqual(timedelta(minutes=1), config._drain_ongoing_call_timeout)
        self.assertEqual(2, len(config._entities))

        # apply new config
        new_config = ActorRuntimeConfig(
            timedelta(hours=3), timedelta(seconds=10), timedelta(minutes=1), False)

        ActorRuntime.set_actor_config(new_config)
        config = ActorRuntime.get_actor_config()

        self.assertFalse(config._drain_rebalanced_actors)
        self.assertEqual(timedelta(hours=3), config._actor_idle_timeout)
        self.assertEqual(timedelta(seconds=10), config._actor_scan_interval)
        self.assertEqual(timedelta(minutes=1), config._drain_ongoing_call_timeout)
        self.assertEqual(2, len(config._entities))

    def test_entities_update(self):
        # Clean up managers
        ActorRuntime._actor_managers = {}
        ActorRuntime.set_actor_config(ActorRuntimeConfig())

        config = ActorRuntime.get_actor_config()
        with self.assertRaises(ValueError):
            config._entities.index(FakeSimpleActor.__name__)

        _run(ActorRuntime.register_actor(FakeSimpleActor))
        config = ActorRuntime.get_actor_config()
        self.assertTrue(config._entities.index(FakeSimpleActor.__name__) >= 0)

    def test_dispatch(self):
        _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))

        request_body = {
            "message": "hello dapr",
        }

        test_request_body = self._serializer.serialize(request_body)
        response = _run(ActorRuntime.dispatch(
            FakeMultiInterfacesActor.__name__, 'test-id',
            "ActionMethod", test_request_body))

        self.assertEqual(b'"hello dapr"', response)

        _run(ActorRuntime.deactivate(FakeMultiInterfacesActor.__name__, 'test-id'))

        # Ensure test-id is deactivated
        with self.assertRaises(ValueError):
            _run(ActorRuntime.deactivate(FakeMultiInterfacesActor.__name__, 'test-id'))

    @mock.patch(
        'tests.actor.fake_client.FakeDaprActorClient.register_reminder',
        new=_async_mock(return_value=b'"ok"'))
    @mock.patch(
        'tests.actor.fake_client.FakeDaprActorClient.unregister_reminder',
        new=_async_mock(return_value=b'"ok"'))
    def test_register_reminder(self):

        test_actor_id = ActorId('test_id')
        test_type_info = ActorTypeInformation.create(FakeSimpleReminderActor)
        test_client = FakeDaprActorClient
        ctx = ActorRuntimeContext(
            test_type_info, self._serializer,
            self._serializer, test_client)
        test_actor = FakeSimpleReminderActor(ctx, test_actor_id)

        # register reminder
        _run(test_actor.register_reminder(
            'test_reminder', b'reminder_message',
            timedelta(seconds=1), timedelta(seconds=1)))
        test_client.register_reminder.mock.assert_called_once()
        test_client.register_reminder.mock.assert_called_with(
            'FakeSimpleReminderActor', 'test_id',
            'test_reminder',
            b'{"name":"test_reminder","dueTime":"0h0m1s","period":"0h0m1s","data":"cmVtaW5kZXJfbWVzc2FnZQ=="}')  # noqa E501

        # unregister reminder
        _run(test_actor.unregister_reminder('test_reminder'))
        test_client.unregister_reminder.mock.assert_called_once()
        test_client.unregister_reminder.mock.assert_called_with(
            'FakeSimpleReminderActor', 'test_id', 'test_reminder')

    @mock.patch(
        'tests.actor.fake_client.FakeDaprActorClient.register_timer',
        new=_async_mock(return_value=b'"ok"'))
    @mock.patch(
        'tests.actor.fake_client.FakeDaprActorClient.unregister_timer',
        new=_async_mock(return_value=b'"ok"'))
    def test_register_timer(self):

        test_actor_id = ActorId('test_id')
        test_type_info = ActorTypeInformation.create(FakeSimpleTimerActor)
        test_client = FakeDaprActorClient
        ctx = ActorRuntimeContext(
            test_type_info, self._serializer,
            self._serializer, test_client)
        test_actor = FakeSimpleTimerActor(ctx, test_actor_id)

        # register timer
        _run(test_actor.register_timer(
            'test_timer', test_actor.timer_callback,
            "mydata", timedelta(seconds=1), timedelta(seconds=2)))
        test_client.register_timer.mock.assert_called_once()
        test_client.register_timer.mock.assert_called_with(
            'FakeSimpleTimerActor', 'test_id', 'test_timer',
            b'{"callback":"timer_callback","data":"mydata","dueTime":"0h0m1s","period":"0h0m2s"}')

        # unregister timer
        _run(test_actor.unregister_timer('test_timer'))
        test_client.unregister_timer.mock.assert_called_once()
        test_client.unregister_timer.mock.assert_called_with(
            'FakeSimpleTimerActor', 'test_id', 'test_timer')

        # register timer without timer name
        _run(test_actor.register_timer(
            None, test_actor.timer_callback,
            "timer call", timedelta(seconds=1), timedelta(seconds=1)))
Exemple #14
0
 def __init__(
         self,
         actor_client: DaprActorClientBase,
         state_serializer: Serializer = DefaultJSONSerializer()):
     self._state_client = actor_client
     self._state_serializer = state_serializer
class ActorRuntimeTests(unittest.TestCase):
    def setUp(self):
        ActorRuntime._actor_managers = {}
        ActorRuntime.set_actor_config(ActorRuntimeConfig())
        self._serializer = DefaultJSONSerializer()
        _run(ActorRuntime.register_actor(FakeSimpleActor))
        _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))
        _run(ActorRuntime.register_actor(FakeSimpleTimerActor))

    def test_get_registered_actor_types(self):
        actor_types = ActorRuntime.get_registered_actor_types()
        self.assertTrue(actor_types.index('FakeSimpleActor') >= 0)
        self.assertTrue(
            actor_types.index(FakeMultiInterfacesActor.__name__) >= 0)
        self.assertTrue(actor_types.index(FakeSimpleTimerActor.__name__) >= 0)

    def test_actor_config(self):
        config = ActorRuntime.get_actor_config()

        self.assertTrue(config._drain_rebalanced_actors)
        self.assertEqual(timedelta(hours=1), config._actor_idle_timeout)
        self.assertEqual(timedelta(seconds=30), config._actor_scan_interval)
        self.assertEqual(timedelta(minutes=1),
                         config._drain_ongoing_call_timeout)
        self.assertEqual(3, len(config._entities))

        # apply new config
        new_config = ActorRuntimeConfig(timedelta(hours=3),
                                        timedelta(seconds=10),
                                        timedelta(minutes=1), False)

        ActorRuntime.set_actor_config(new_config)
        config = ActorRuntime.get_actor_config()

        self.assertFalse(config._drain_rebalanced_actors)
        self.assertEqual(timedelta(hours=3), config._actor_idle_timeout)
        self.assertEqual(timedelta(seconds=10), config._actor_scan_interval)
        self.assertEqual(timedelta(minutes=1),
                         config._drain_ongoing_call_timeout)
        self.assertEqual(3, len(config._entities))

    def test_entities_update(self):
        # Clean up managers
        ActorRuntime._actor_managers = {}
        ActorRuntime.set_actor_config(ActorRuntimeConfig())

        config = ActorRuntime.get_actor_config()
        with self.assertRaises(ValueError):
            config._entities.index(FakeSimpleActor.__name__)

        _run(ActorRuntime.register_actor(FakeSimpleActor))
        config = ActorRuntime.get_actor_config()
        self.assertTrue(config._entities.index(FakeSimpleActor.__name__) >= 0)

    def test_dispatch(self):
        _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))

        request_body = {
            "message": "hello dapr",
        }

        test_request_body = self._serializer.serialize(request_body)
        response = _run(
            ActorRuntime.dispatch(FakeMultiInterfacesActor.__name__, 'test-id',
                                  "ActionMethod", test_request_body))

        self.assertEqual(b'"hello dapr"', response)

        _run(
            ActorRuntime.deactivate(FakeMultiInterfacesActor.__name__,
                                    'test-id'))

        # Ensure test-id is deactivated
        with self.assertRaises(ValueError):
            _run(
                ActorRuntime.deactivate(FakeMultiInterfacesActor.__name__,
                                        'test-id'))

    def test_fire_timer_success(self):
        # Fire timer
        _run(
            ActorRuntime.fire_timer(
                FakeSimpleTimerActor.__name__, 'test-id', 'test_timer',
                '{ "callback": "timer_callback", "data": "timer call" }'.
                encode('UTF8')))

        manager = ActorRuntime._actor_managers[FakeSimpleTimerActor.__name__]
        actor = manager._active_actors['test-id']
        self.assertTrue(actor.timer_called)

    def test_fire_timer_unregistered(self):
        with self.assertRaises(ValueError):
            _run(
                ActorRuntime.fire_timer(
                    'UnknownType', 'test-id', 'test_timer',
                    '{ "callback": "timer_callback", "data": "timer call" }'.
                    encode('UTF8')))
Exemple #16
0
    def setUp(self):
        self._serializer = DefaultJSONSerializer()

        self._fake_client = FakeDaprActorClient
Exemple #17
0
class DaprActor(object):
    def __init__(self, app: FastAPI):
        self._dapr_serializer = DefaultJSONSerializer()
        self._router = APIRouter()
        self.init_routes(self._router)
        app.include_router(self._router)

    def init_routes(self, router: APIRouter):
        @router.get("/healthz")
        async def healthz():
            return {'status': 'ok'}

        @router.get('/dapr/config')
        async def dapr_config():
            serialized = self._dapr_serializer.serialize(ActorRuntime.get_actor_config())
            return _wrap_response(status.HTTP_200_OK, serialized)

        @router.delete('/actors/{actor_type_name}/{actor_id}')
        async def actor_deactivation(actor_type_name: str, actor_id: str):
            try:
                await ActorRuntime.deactivate(actor_type_name, actor_id)
            except DaprInternalError as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    ex.as_dict())
            except Exception as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    repr(ex),
                    ERROR_CODE_UNKNOWN)

            msg = f'deactivated actor: {actor_type_name}.{actor_id}'
            logger.debug(msg)
            return _wrap_response(status.HTTP_200_OK, msg)

        @router.put('/actors/{actor_type_name}/{actor_id}/method/{method_name}')
        async def actor_method(
                actor_type_name: str,
                actor_id: str,
                method_name: str,
                request: Request):
            try:
                # Read raw bytes from request stream
                req_body = await request.body()
                result = await ActorRuntime.dispatch(
                    actor_type_name, actor_id, method_name, req_body)
            except DaprInternalError as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_dict())
            except Exception as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    repr(ex),
                    ERROR_CODE_UNKNOWN)

            msg = f'called method. actor: {actor_type_name}.{actor_id}, method: {method_name}'
            logger.debug(msg)
            return _wrap_response(status.HTTP_200_OK, result)

        @router.put('/actors/{actor_type_name}/{actor_id}/method/timer/{timer_name}')
        async def actor_timer(
                actor_type_name: str,
                actor_id: str,
                timer_name: str,
                request: Request):
            try:
                # Read raw bytes from request stream
                req_body = await request.body()
                await ActorRuntime.fire_timer(actor_type_name, actor_id, timer_name, req_body)
            except DaprInternalError as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    ex.as_dict())
            except Exception as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    repr(ex),
                    ERROR_CODE_UNKNOWN)

            msg = f'called timer. actor: {actor_type_name}.{actor_id}, timer: {timer_name}'
            logger.debug(msg)
            return _wrap_response(status.HTTP_200_OK, msg)

        @router.put('/actors/{actor_type_name}/{actor_id}/method/remind/{reminder_name}')
        async def actor_reminder(
                actor_type_name: str,
                actor_id: str,
                reminder_name: str,
                request: Request):
            try:
                # Read raw bytes from request stream
                req_body = await request.body()
                await ActorRuntime.fire_reminder(
                    actor_type_name, actor_id, reminder_name, req_body)
            except DaprInternalError as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    ex.as_dict())
            except Exception as ex:
                return _wrap_response(
                    status.HTTP_500_INTERNAL_SERVER_ERROR,
                    repr(ex),
                    ERROR_CODE_UNKNOWN)

            msg = f'called reminder. actor: {actor_type_name}.{actor_id}, reminder: {reminder_name}'
            logger.debug(msg)
            return _wrap_response(status.HTTP_200_OK, msg)

    async def register_actor(self, actor: Type[Actor]) -> None:
        await ActorRuntime.register_actor(actor)
        logger.debug(f'registered actor: {actor.__class__.__name__}')
Exemple #18
0
 def create(self, actor_interface, actor_type, actor_id) -> ActorProxy:
     return ActorProxy(self._dapr_client, actor_interface, actor_type,
                       actor_id, DefaultJSONSerializer())
Exemple #19
0
class DaprActor(object):
    def __init__(self, app=None):
        self._app = app
        self._dapr_serializer = DefaultJSONSerializer()

        if app is not None:
            self.init_routes(app)

    def init_routes(self, app):
        app.add_url_rule('/healthz',
                         None,
                         self._healthz_handler,
                         methods=['GET'])
        app.add_url_rule('/dapr/config',
                         None,
                         self._config_handler,
                         methods=['GET'])
        app.add_url_rule('/actors/<actor_type_name>/<actor_id>',
                         None,
                         self._deactivation_handler,
                         methods=['DELETE'])
        app.add_url_rule(
            '/actors/<actor_type_name>/<actor_id>/method/<method_name>',
            None,
            self._method_handler,
            methods=['PUT'])
        app.add_url_rule(
            '/actors/<actor_type_name>/<actor_id>/method/timer/<timer_name>',
            None,
            self._timer_handler,
            methods=['PUT'])
        app.add_url_rule(
            '/actors/<actor_type_name>/<actor_id>/method/remind/<reminder_name>',
            None,
            self._reminder_handler,
            methods=['PUT'])

    def teardown(self, exception):
        self._app.logger.debug('actor service is shutting down.')

    def register_actor(self, actor: Type[Actor]) -> None:
        asyncio.run(ActorRuntime.register_actor(actor))
        self._app.logger.debug(f'registered actor: {actor.__class__.__name__}')

    def _healthz_handler(self):
        return wrap_response(200, 'ok')

    def _config_handler(self):
        serialized = self._dapr_serializer.serialize(
            ActorRuntime.get_actor_config())
        return wrap_response(200, serialized)

    def _deactivation_handler(self, actor_type_name, actor_id):
        try:
            asyncio.run(ActorRuntime.deactivate(actor_type_name, actor_id))
        except DaprInternalError as ex:
            return wrap_response(500, ex.as_dict())
        except Exception as ex:
            return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

        msg = f'deactivated actor: {actor_type_name}.{actor_id}'
        self._app.logger.debug(msg)
        return wrap_response(200, msg)

    def _method_handler(self, actor_type_name, actor_id, method_name):
        try:
            # Read raw bytes from request stream
            req_body = request.stream.read()
            result = asyncio.run(
                ActorRuntime.dispatch(actor_type_name, actor_id, method_name,
                                      req_body))
        except DaprInternalError as ex:
            return wrap_response(500, ex.as_dict())
        except Exception as ex:
            return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

        msg = f'called method. actor: {actor_type_name}.{actor_id}, method: {method_name}'
        self._app.logger.debug(msg)
        return wrap_response(200, result)

    def _timer_handler(self, actor_type_name, actor_id, timer_name):
        try:
            asyncio.run(
                ActorRuntime.fire_timer(actor_type_name, actor_id, timer_name))
        except DaprInternalError as ex:
            return wrap_response(500, ex.as_dict())
        except Exception as ex:
            return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

        msg = f'called timer. actor: {actor_type_name}.{actor_id}, timer: {timer_name}'
        self._app.logger.debug(msg)
        return wrap_response(200, msg)

    def _reminder_handler(self, actor_type_name, actor_id, reminder_name):
        try:
            # Read raw bytes from request stream
            req_body = request.stream.read()
            asyncio.run(
                ActorRuntime.fire_reminder(actor_type_name, actor_id,
                                           reminder_name, req_body))
        except DaprInternalError as ex:
            return wrap_response(500, ex.as_dict())
        except Exception as ex:
            return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

        msg = f'called reminder. actor: {actor_type_name}.{actor_id}, reminder: {reminder_name}'
        self._app.logger.debug(msg)
        return wrap_response(200, msg)
Exemple #20
0
 def __init__(self, message_serializer=DefaultJSONSerializer()):
     # TODO: support serializer for state store later
     self._dapr_client = DaprActorHttpClient(message_serializer)
     self._message_serializer = message_serializer
Exemple #21
0
 def __init__(self, app: FastAPI):
     self._dapr_serializer = DefaultJSONSerializer()
     self._router = APIRouter()
     self.init_routes(self._router)
     app.include_router(self._router)
class ActorRuntimeTests(unittest.TestCase):
    def setUp(self):
        ActorRuntime._actor_managers = {}
        ActorRuntime.set_actor_config(
            ActorRuntimeConfig(reentrancy=ActorReentrancyConfig(enabled=True)))
        self._serializer = DefaultJSONSerializer()
        _run(ActorRuntime.register_actor(FakeReentrantActor))
        _run(ActorRuntime.register_actor(FakeSlowReentrantActor))
        _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))

    def test_reentrant_dispatch(self):
        _run(ActorRuntime.register_actor(FakeMultiInterfacesActor))

        request_body = {
            "message": "hello dapr",
        }

        reentrancy_id = "0faa4c8b-f53a-4dff-9a9d-c50205035085"

        test_request_body = self._serializer.serialize(request_body)
        response = _run(
            ActorRuntime.dispatch(FakeMultiInterfacesActor.__name__,
                                  'test-id',
                                  "ReentrantMethod",
                                  test_request_body,
                                  reentrancy_id=reentrancy_id))

        self.assertEqual(b'"hello dapr"', response)

        _run(
            ActorRuntime.deactivate(FakeMultiInterfacesActor.__name__,
                                    'test-id'))

        # Ensure test-id is deactivated
        with self.assertRaises(ValueError):
            _run(
                ActorRuntime.deactivate(FakeMultiInterfacesActor.__name__,
                                        'test-id'))

    def test_interleaved_reentrant_actor_dispatch(self):
        _run(ActorRuntime.register_actor(FakeReentrantActor))
        _run(ActorRuntime.register_actor(FakeSlowReentrantActor))

        request_body = self._serializer.serialize({
            "message": "Normal",
        })

        normal_reentrancy_id = "f6319f23-dc0a-4880-90d9-87b23c19c20a"
        slow_reentrancy_id = "b1653a2f-fe54-4514-8197-98b52d156454"

        async def dispatchReentrantCall(actorName: str, method: str,
                                        reentrancy_id: str):
            return await ActorRuntime.dispatch(actorName,
                                               'test-id',
                                               method,
                                               request_body,
                                               reentrancy_id=reentrancy_id)

        async def run_parallel_actors():
            slow = dispatchReentrantCall(FakeSlowReentrantActor.__name__,
                                         "ReentrantMethod", slow_reentrancy_id)
            normal = dispatchReentrantCall(FakeReentrantActor.__name__,
                                           "ReentrantMethod",
                                           normal_reentrancy_id)

            res = await asyncio.gather(slow, normal)
            self.slow_res = res[0]
            self.normal_res = res[1]

        _run(run_parallel_actors())

        self.assertEqual(self.normal_res,
                         bytes('"' + normal_reentrancy_id + '"', 'utf-8'))
        self.assertEqual(self.slow_res,
                         bytes('"' + slow_reentrancy_id + '"', 'utf-8'))

        _run(
            ActorRuntime.deactivate(FakeSlowReentrantActor.__name__,
                                    'test-id'))
        _run(ActorRuntime.deactivate(FakeReentrantActor.__name__, 'test-id'))

        # Ensure test-id is deactivated
        with self.assertRaises(ValueError):
            _run(
                ActorRuntime.deactivate(FakeSlowReentrantActor.__name__,
                                        'test-id'))
            _run(
                ActorRuntime.deactivate(FakeReentrantActor.__name__,
                                        'test-id'))

    def test_reentrancy_header_passthrough(self):
        _run(ActorRuntime.register_actor(FakeReentrantActor))
        _run(ActorRuntime.register_actor(FakeSlowReentrantActor))

        request_body = self._serializer.serialize({
            "message": "Normal",
        })

        async def expected_return_value(*args, **kwargs):
            return ["expected", "None"]

        reentrancy_id = "f6319f23-dc0a-4880-90d9-87b23c19c20a"
        actor = FakeSlowReentrantActor.__name__
        method = 'ReentrantMethod'

        with mock.patch('dapr.clients.http.client.DaprHttpClient.send_bytes'
                        ) as mocked:

            mocked.side_effect = expected_return_value
            _run(
                ActorRuntime.dispatch(FakeReentrantActor.__name__,
                                      'test-id',
                                      'ReentrantMethodWithPassthrough',
                                      request_body,
                                      reentrancy_id=reentrancy_id))

            mocked.assert_called_with(
                method="POST",
                url=
                f'http://127.0.0.1:3500/v1.0/actors/{actor}/test-id/method/{method}',
                data=None,
                headers={'Dapr-Reentrancy-Id': reentrancy_id})

        _run(ActorRuntime.deactivate(FakeReentrantActor.__name__, 'test-id'))

        # Ensure test-id is deactivated
        with self.assertRaises(ValueError):
            _run(
                ActorRuntime.deactivate(FakeReentrantActor.__name__,
                                        'test-id'))

    def test_header_passthrough_reentrancy_disabled(self):
        config = ActorRuntimeConfig(reentrancy=None)
        ActorRuntime.set_actor_config(config)
        _run(ActorRuntime.register_actor(FakeReentrantActor))
        _run(ActorRuntime.register_actor(FakeSlowReentrantActor))

        request_body = self._serializer.serialize({
            "message": "Normal",
        })

        async def expected_return_value(*args, **kwargs):
            return ["expected", "None"]

        reentrancy_id = "f6319f23-dc0a-4880-90d9-87b23c19c20a"
        actor = FakeSlowReentrantActor.__name__
        method = 'ReentrantMethod'

        with mock.patch('dapr.clients.http.client.DaprHttpClient.send_bytes'
                        ) as mocked:

            mocked.side_effect = expected_return_value
            _run(
                ActorRuntime.dispatch(FakeReentrantActor.__name__,
                                      'test-id',
                                      'ReentrantMethodWithPassthrough',
                                      request_body,
                                      reentrancy_id=reentrancy_id))

            mocked.assert_called_with(
                method="POST",
                url=
                f'http://127.0.0.1:3500/v1.0/actors/{actor}/test-id/method/{method}',
                data=None,
                headers={})

        _run(ActorRuntime.deactivate(FakeReentrantActor.__name__, 'test-id'))

        # Ensure test-id is deactivated
        with self.assertRaises(ValueError):
            _run(
                ActorRuntime.deactivate(FakeReentrantActor.__name__,
                                        'test-id'))

    def test_parse_incoming_reentrancy_header_flask(self):
        from ext.flask_dapr import flask_dapr
        from flask import Flask

        app = Flask(f'{FakeReentrantActor.__name__}Service')
        flask_dapr.DaprActor(app)

        reentrancy_id = "b1653a2f-fe54-4514-8197-98b52d156454"
        actor_type_name = FakeReentrantActor.__name__
        actor_id = 'test-id'
        method_name = 'ReentrantMethod'

        request_body = self._serializer.serialize({
            "message": "Normal",
        })

        relativeUrl = f'/actors/{actor_type_name}/{actor_id}/method/{method_name}'

        with mock.patch(
                'dapr.actor.runtime.runtime.ActorRuntime.dispatch') as mocked:
            client = app.test_client()
            mocked.return_value = None
            client.put(relativeUrl,
                       headers={
                           flask_dapr.actor.DAPR_REENTRANCY_ID_HEADER:
                           reentrancy_id
                       },
                       data=request_body)
            mocked.assert_called_with(actor_type_name, actor_id, method_name,
                                      request_body, reentrancy_id)

    def test_parse_incoming_reentrancy_header_fastapi(self):
        from fastapi import FastAPI
        from fastapi.testclient import TestClient
        from dapr.ext import fastapi

        app = FastAPI(title=f'{FakeReentrantActor.__name__}Service')
        fastapi.DaprActor(app)

        reentrancy_id = "b1653a2f-fe54-4514-8197-98b52d156454"
        actor_type_name = FakeReentrantActor.__name__
        actor_id = 'test-id'
        method_name = 'ReentrantMethod'

        request_body = self._serializer.serialize({
            "message": "Normal",
        })

        relativeUrl = f'/actors/{actor_type_name}/{actor_id}/method/{method_name}'

        with mock.patch(
                'dapr.actor.runtime.runtime.ActorRuntime.dispatch') as mocked:
            client = TestClient(app)
            mocked.return_value = None
            client.put(relativeUrl,
                       headers={
                           fastapi.actor.DAPR_REENTRANCY_ID_HEADER:
                           reentrancy_id
                       },
                       data=request_body)
            mocked.assert_called_with(actor_type_name, actor_id, method_name,
                                      request_body, reentrancy_id)