Repository: ansible/awx
File: awx/main/scheduler/task_manager.py (Python)

Code before:
            latest_src_project_update = self.get_latest_project_update(invsrc.source_project_id)
            if self.should_update_related_project(inventory_task, latest_src_project_update):
                latest_src_project_update = self.create_project_update(inventory_task, project_id=invsrc.source_project_id)
                created_dependencies.append(latest_src_project_update)
            self.add_dependencies(inventory_task, [latest_src_project_update])
            latest_src_project_update.scm_inventory_updates.add(inventory_task)
        return created_dependencies

    @timeit
    def generate_dependencies(self, undeped_tasks):
        created_dependencies = []
        for task in undeped_tasks:
            task.log_lifecycle("acknowledged")
            if type(task) is Job:
                created_dependencies += self.gen_dep_for_job(task)
            elif type(task) is InventoryUpdate:
                created_dependencies += self.gen_dep_for_inventory_update(task)
            else:
                continue
        UnifiedJob.objects.filter(pk__in=[task.pk for task in undeped_tasks]).update(dependencies_processed=True)

        return created_dependencies

    def process_tasks(self):
        deps = self.generate_dependencies(self.all_tasks)
        deps_of_deps = self.generate_dependencies(deps)
        self.subsystem_metrics.inc(f"{self.prefix}_pending_processed", len(self.all_tasks))

    @timeit
    def _schedule(self):
        self.get_tasks(dict(status__in=["pending"], dependencies_processed=False))

        if len(self.all_tasks) > 0:
            self.get_inventory_source_tasks()
            self.process_tasks()
            ScheduleTaskManager().schedule()


class TaskManager(TaskBase):
    def __init__(self):
        """
        Do NOT put database queries or other potentially expensive operations
        in the task manager init. The task manager object is created every time a
        job is created, transitions state, and every 30 seconds on each tower node.
        More often then not, the object is destroyed quickly because the NOOP case is hit.

        The NOOP case is short-circuit logic. If the task manager realizes that another instance
        of the task manager is already running, then it short-circuits and decides not to run.
        """
        # start task limit indicates how many pending jobs can be started on this
        # .schedule() run. Starting jobs is expensive, and there is code in place to reap

Review comment (bug):
so, do you need to process `deps_of_deps` for update-on-launch behavior? Otherwise that's your linter failure

Code after:
                created_dependencies.append(latest_src_project_update)
            self.add_dependencies(inventory_task, [latest_src_project_update])
            latest_src_project_update.scm_inventory_updates.add(inventory_task)
        return created_dependencies

    @timeit
    def generate_dependencies(self, undeped_tasks):
        created_dependencies = []
        for task in undeped_tasks:
            task.log_lifecycle("acknowledged")
            if type(task) is Job:
                created_dependencies += self.gen_dep_for_job(task)
            elif type(task) is InventoryUpdate:
                created_dependencies += self.gen_dep_for_inventory_update(task)
            else:
                continue
        UnifiedJob.objects.filter(pk__in=[task.pk for task in undeped_tasks]).update(dependencies_processed=True)

        return created_dependencies

    def process_tasks(self):
        deps = self.generate_dependencies(self.all_tasks)
        self.generate_dependencies(deps)
        self.subsystem_metrics.inc(f"{self.prefix}_pending_processed", len(self.all_tasks) + len(deps))

    @timeit
    def _schedule(self):
        self.get_tasks(dict(status__in=["pending"], dependencies_processed=False))

        if len(self.all_tasks) > 0:
            self.get_inventory_source_tasks()
            self.process_tasks()
            ScheduleTaskManager().schedule()


class TaskManager(TaskBase):
    def __init__(self):
        """
        Do NOT put database queries or other potentially expensive operations
        in the task manager init. The task manager object is created every time a
        job is created, transitions state, and every 30 seconds on each tower node.
        More often then not, the object is destroyed quickly because the NOOP case is hit.

        The NOOP case is short-circuit logic. If the task manager realizes that another instance
        of the task manager is already running, then it short-circuits and decides not to run.
        """
        # start task limit indicates how many pending jobs can be started on this
        # .schedule() run. Starting jobs is expensive, and there is code in place to reap
        # the task manager after 5 minutes. At scale, the task manager can easily take more than
        # 5 minutes to start pending jobs. If this limit is reached, pending jobs
        # will no longer be started and will be started on the next task manager cycle.