Exemple #1
0
import time
from sys import stderr

from rx import create, interval
from rx.core import Observer
from rx.disposable import Disposable
from rx.operators import *
from rx.scheduler.scheduler import Scheduler
from rx.scheduler import ThreadPoolScheduler
from rx.subject import ReplaySubject

flow = ReplaySubject(None)

pool = ThreadPoolScheduler(1)


def on_well(e):
    if e == "Well":
        flow.on_next("E!")


flow.pipe(subscribe_on(pool), retry(3), do_action(on_next=on_well)).subscribe(
    on_next=lambda s: print(s), on_error=lambda e: print(e, file=stderr))

while True:
    flow.on_next("Hi")
    time.sleep(1)
    flow.on_next("Well")
    time.sleep(1)
class DefaultInstanceManager(InstanceManager):
    def __init__(self, namespace: str, use_repeaters=True, networks=[]):
        super().__init__(namespace)

        self.__networks = networks
        self.__reachable_peers: Set[InstanceReference] = set()
        self.__resource_subjects: Dict[bytes, ReplaySubject] = {}
        self.__peer_subjects: Dict[InstanceReference, ReplaySubject] = {}
        self.__has_aip_group_peers = ReplaySubject()

        if (len(networks) == 0):
            port = 5156
            while True:
                try:
                    network = IPv4("0.0.0.0", port)
                    self.__networks.append(network)
                    break
                except Exception as e:
                    if (port >= 9000):
                        raise e

                    port += 1

        self.__muxer = MX2()

        for network in self.__networks:
            network.bring_up()
            self.__muxer.register_network(network)

        self.__discoverer = AIP(self.__muxer)
        self.__instance = self.__muxer.create_instance(self.namespace)
        self.__transport = STP(self.__muxer, self.__instance)
        self.__path_finder = RPP(self.__muxer, self.__discoverer)

        self.__path_finder.add_instance(self.__instance.reference)

        self.__instance.incoming_greeting.subscribe(self.__received_greeting)
        self.__transport.incoming_stream.subscribe(self.__new_stream)

        for network in self.__networks:
            self.__discoverer.add_network(network)

        self.__info = ApplicationInformation.from_instance(self.__instance)

        # Add the application to the discoverer
        self.__discoverer.add_application(self.__info).subscribe(
            self.__new_aip_app_peer)

    def establish_stream(self,
                         peer: InstanceReference,
                         *,
                         in_reply_to=None) -> Subject:
        # Settle on a reply
        reply = in_reply_to or b"\x00" * 16

        # Ask the transport to establish a stream
        return self.__transport.initialise_stream(peer, in_reply_to=reply)

    def find_resource_peers(self, resource: bytes) -> Subject:
        # Do we already have a subject for this query?
        if (resource not in self.__resource_subjects):
            # Create one
            self.__resource_subjects[resource] = ReplaySubject()

        # Prepeare function to make the resource request
        def find_peers(has_group_peers):
            # Create a query for the resource
            query = self.__discoverer.find_application_resource(
                self.__info, resource)

            # Subscribe to the queries answer
            query.answer.subscribe(
                lambda x: self.__found_resource_instance(x, resource))

        # When we are in a position to ask group peers, do it
        self.__has_aip_group_peers.pipe(take(1)).subscribe(find_peers)

        # Return the resource subject
        return self.__resource_subjects[resource]

    @property
    def resources(self) -> Set[bytes]:
        return self.__info.resources

    def __new_aip_app_peer(self, instance):
        # Query for application instances
        self.__discoverer.find_application_instance(self.__info).subscribe(
            self.__found_instance)

        # We now have an AIP group peer
        self.__has_aip_group_peers.on_next(True)

    def __found_instance(self, instance_info: InstanceInformation):
        # Is this peer already reachable?
        if (instance_info.instance_reference in self.__reachable_peers):
            # Don't harras it
            return

        # Inquire about the peer
        subject = self.__muxer.inquire(self.__instance,
                                       instance_info.instance_reference,
                                       instance_info.connection_methods)

        # Handle timeouts
        subject.subscribe(on_error=lambda x: self.__greeting_timeout(
            instance_info.instance_reference))

    def __found_resource_instance(self, instance_info: InstanceInformation,
                                  resource: bytes):
        # Get the resource subject
        resource_subject = self.__resource_subjects[resource]

        # Get the instance subject
        instance_subject = self.__get_instance_subject(
            instance_info.instance_reference)

        # Notify resource subject when instance subject is reachable
        instance_subject.subscribe(resource_subject.on_next)

        # Handle new instance
        self.__found_instance(instance_info)

    def __received_greeting(self, instance: InstanceReference):
        # Have we already marked this instance as reachable
        if (instance in self.__reachable_peers):
            # Don't notify app again
            return

        # Add to reachable peers
        self.__reachable_peers.add(instance)

        # Notify instance subject
        self.__get_instance_subject(instance).on_next(instance)

        # Notify the app
        self.new_peer.on_next(instance)

    def __new_stream(self, stream):
        # Notify app of new stream
        self.new_stream.on_next(stream)

    def __get_instance_subject(self, ref: InstanceReference) -> Subject:
        # Do we have it?
        if (ref in self.__peer_subjects):
            # Yes
            return self.__peer_subjects[ref]

        # No, create it
        subject = ReplaySubject()
        self.__peer_subjects[ref] = subject
        return subject

    def __greeting_timeout(self, target: InstanceReference):
        # Have we already found this peer?
        if (target in self.__instance.reachable_peers
                or not self.use_repeaters):
            return

        # Did not receive greeting from instance, ask for paths via repeaters
        query = self.__path_finder.find_path(target)

        def handle_route(paths):
            # Have we already found this peer?
            if (target in self.__instance.reachable_peers):
                return

            # We have a path, inquire
            self.__muxer.inquire_via_paths(self.__instance, target, paths)

        # Subscribe to answers
        query.subscribe(handle_route)
Exemple #3
0
class WorkQueue(object):
    def __init__(self, concurrency_per_group, description=None):
        self.scheduler = ThreadPoolScheduler(concurrency_per_group)
        self._requests = Subject()
        self._output = ReplaySubject()
        self._description = description
        self._subscription = self._requests.pipe(
            group_by(lambda r: r['concurrency_group']),
            flat_map(lambda concurrency_group: concurrency_group.pipe(
                map(lambda r: r['request']),
                merge(max_concurrent=concurrency_per_group)))).subscribe(
                    on_next=lambda request: self._output.on_next(request),
                    on_error=lambda error: logging.exception(
                        'Error in {} request stream'.format(self)),
                    on_completed=lambda: logging.error(
                        '{} request stream unexpectedly completed'.format(self
                                                                          )),
                    scheduler=self.scheduler)

    def enqueue(self,
                observable: Observable,
                group: str = None,
                retries: int = 0,
                description: str = None):
        # Provide a function returning a callable?

        description = description or str(Observable)
        key = '{}({})'.format(description, random.random())

        def log_status(status):
            logging.debug(
                str({
                    'WorkQueue': str(self),
                    'group': group,
                    'key': key,
                    status: description
                }))

        log_status('ENQUEUED')
        error: Optional[Exception] = None

        def handle_error(e):
            log_status('FAILED')
            nonlocal error
            error = e
            return of({'key': key, 'error': e})

        def throw_if_error(request):
            if error:
                return throw(error)
            else:
                return of(request)

        def extract_value(value):
            if type(value) == Observable:
                return value
            else:
                return of(value)

        request = of(True).pipe(
            do_action(lambda _: log_status('STARTED')),
            flat_map(lambda _: observable.pipe(
                flat_map(extract_value),
                map(lambda value: {
                    'key': key,
                    'value': value
                }),
                retry_with_backoff(
                    retries=retries,
                    description='{}.enqueue(group={}, description={})'.format(
                        self, group, description)),
                catch(handler=lambda e, o: handle_error(e)),
            )),
            concat(
                of({
                    'key': key,
                    'complete': True
                }).pipe(do_action(lambda _: log_status('COMPLETED')))))
        result_stream = self._output.pipe(
            filter(lambda request: request['key'] == key),
            flat_map(lambda request: throw_if_error(request)),
            take_while(lambda request: not request.get('complete')),
            flat_map(lambda request: of(request.get('value'))))
        self._requests.on_next({
            'request': request,
            'concurrency_group': group
        })
        return result_stream

    def dispose(self):
        if self._subscription:
            self._subscription.dispose()

    def __str__(self):
        return 'WorkQueue({})'.format(
            self._description) if self._description else super().__str__()