Example #1
0
 def load_triggers(self):
     """
     Queries the database to get the triggers we're supposed to be running,
     adds them to our runner, and then removes ones from it we no longer
     need.
     """
     Trigger.assign_unassigned(self.id, self.capacity)
     ids = Trigger.ids_for_triggerer(self.id)
     self.runner.update_triggers(set(ids))
Example #2
0
 def handle_failed_triggers(self):
     """
     Handles "failed" triggers - ones that errored or exited before they
     sent an event. Task Instances that depend on them need failing.
     """
     while self.runner.failed_triggers:
         # Tell the model to fail this trigger's deps
         trigger_id = self.runner.failed_triggers.popleft()
         Trigger.submit_failure(trigger_id=trigger_id)
Example #3
0
 def handle_events(self):
     """
     Handles outbound events from triggers - dispatching them into the Trigger
     model where they are then pushed into the relevant task instances.
     """
     while self.runner.events:
         # Get the event and its trigger ID
         trigger_id, event = self.runner.events.popleft()
         # Tell the model to wake up its tasks
         Trigger.submit_event(trigger_id=trigger_id, event=event)
Example #4
0
    def _run_trigger_loop(self) -> None:
        """
        The main-thread trigger loop.

        This runs synchronously and handles all database reads/writes.
        """
        while not self.runner.stop:
            # Clean out unused triggers
            Trigger.clean_unused()
            # Load/delete triggers
            self.load_triggers()
            # Handle events
            self.handle_events()
            # Handle failed triggers
            self.handle_failed_triggers()
            # Idle sleep
            time.sleep(1)
Example #5
0
    def _run_trigger_loop(self) -> None:
        """
        The main-thread trigger loop.

        This runs synchronously and handles all database reads/writes.
        """
        while not self.runner.stop:
            # Clean out unused triggers
            Trigger.clean_unused()
            # Load/delete triggers
            self.load_triggers()
            # Handle events
            self.handle_events()
            # Handle failed triggers
            self.handle_failed_triggers()
            # Handle heartbeat
            self.heartbeat(only_if_necessary=True)
            # Collect stats
            self.emit_metrics()
            # Idle sleep
            time.sleep(1)
Example #6
0
    def update_triggers(self, requested_trigger_ids: Set[int]):
        """
        Called from the main thread to request that we update what
        triggers we're running.

        Works out the differences - ones to add, and ones to remove - then
        adds them to the deques so the subthread can actually mutate the running
        trigger set.
        """
        # Note that `triggers` could be mutated by the other thread during this
        # line's execution, but we consider that safe, since there's a strict
        # add -> remove -> never again lifecycle this function is already
        # handling.
        running_trigger_ids = set(self.triggers.keys())
        known_trigger_ids = (running_trigger_ids.union(
            x[0] for x in self.events).union(self.to_cancel).union(
                x[0] for x in self.to_create).union(
                    trigger[0] for trigger in self.failed_triggers))
        # Work out the two difference sets
        new_trigger_ids = requested_trigger_ids - known_trigger_ids
        cancel_trigger_ids = running_trigger_ids - requested_trigger_ids
        # Bulk-fetch new trigger records
        new_triggers = Trigger.bulk_fetch(new_trigger_ids)
        # Add in new triggers
        for new_id in new_trigger_ids:
            # Check it didn't vanish in the meantime
            if new_id not in new_triggers:
                self.log.warning(
                    "Trigger ID %s disappeared before we could start it",
                    new_id)
                continue
            # Resolve trigger record into an actual class instance
            try:
                trigger_class = self.get_trigger_by_classpath(
                    new_triggers[new_id].classpath)
            except BaseException as e:
                # Either the trigger code or the path to it is bad. Fail the trigger.
                self.failed_triggers.append((new_id, e))
                continue
            self.to_create.append(
                (new_id, trigger_class(**new_triggers[new_id].kwargs)))
        # Enqueue orphaned triggers for cancellation
        for old_id in cancel_trigger_ids:
            self.to_cancel.append(old_id)