Skip to content

[NEEDS CODE REVIEWER] Allow job configuration overrides on instance level - #311

Open
heapoutofspace wants to merge 1 commit into
ManiMatter:devfrom
heapoutofspace:instance-job-config
Open

[NEEDS CODE REVIEWER] Allow job configuration overrides on instance level#311
heapoutofspace wants to merge 1 commit into
ManiMatter:devfrom
heapoutofspace:instance-job-config

Conversation

@heapoutofspace

Copy link
Copy Markdown

Adds support for instance-level job configuration overrides, allowing different Sonarr/Radarr instances to have different job settings.

The change has been implemented with the following goals in mind:

  • Avoid adding unnecessary complexity or new concepts to the config file.
  • Hide the implementation from the jobs themselves, by handling the override mechanism in the settings module.

Example

job_defaults:
  max_strikes: 3

jobs:
  remove_slow:
    min_speed: 100
  search_unmet_cutoff:
    min_days_between_searches: 7
    max_concurrent_searches: 1

instances:
  sonarr:
    - base_url: "http://sonarr-hd:8989"
      api_key: "key1"
      # Uses global settings

    - base_url: "http://sonarr-4k:8989"
      api_key: "key2"
      jobs:
        remove_slow:
          min_speed: 500  # Override for this instance
        search_unmet_cutoff: false  # Disable for this instance

AI Assistance

While the code itself has been written by hand, some of the documentation and unit tests were generated by Claude Code and then manually verified.

@ManiMatter

Copy link
Copy Markdown
Owner

hi, I am truly sorry I haven't looked into your PR in such a long time. I do appreciate very much that you took the time to contribute.

Unfortunately, I don't have the time to look into it still.
To overcome me being the bottleneck, I am looking to open this repo up to other people who help maintain it, and contributors can review each others code / merge.

Would you be willing to act as a formal contributor? If yes, I will add you, and if I find others (from open PRs), hopefully you can review each others PR and they can be merged.

Thanks for letting me know, and apologies again for my radio silence.

@ManiMatter ManiMatter changed the title [Feature] Allow job configuration overrides on instance level [NEEDS CODE REVIEWER] Allow job configuration overrides on instance level Apr 21, 2026
@lolimmlost

lolimmlost commented Apr 30, 2026

Copy link
Copy Markdown
Collaborator

Hey @heapoutofspace, reviewing as part of clearing the open PR backlog with @ManiMatter. Design's good — lazy @property, deep copies of JobParams so instances don't share state, three input shapes (None/bool/dict) handled cleanly, solid test coverage. Found one real bug + a couple of things to flag.

Real bug — typo on a dict override silently enables a globally-disabled job with garbage attrs:

_apply_override skips two things the global path does: parameter-name validation, and validate_data_types. Combined with the "dict override means enabled=True" rule, a single typo can flip a job on and start removing torrents.

Smoke-tested locally:

# Global: remove_stalled is NOT configured (.enabled=False, max_strikes=3 from defaults)
arr = ArrInstance(settings, 'sonarr', 'http://t', 'k',
                  jobs_config={'remove_stalled': {'max_stikes': 99}})  # typo
job = arr.jobs.remove_stalled

job.enabled       # True  ← was False globally, now ON
job.max_strikes   # 3     ← real attr untouched, fell back to default
job.max_stikes    # 99    ← garbage attr, no warning logged

So a user has remove_stalled disabled globally, writes remove_stalled: {max_stikes: 99} on one instance hoping to tune it, ends up enabling the job with max_strikes=3 from defaults and the typo gets silently dropped. Confirmed end-to-end: RemoveStalled.__init__ reads max_strikes=3, builds a strikes_handler, run() proceeds. Real torrents would get struck.

The validator already exists btw — global path with {max_strikes: 'five'} logs and recovers:

ERROR | ❗️ Invalid type for 'max_strikes': Expected int, but got str.
       Error: invalid literal... Using default value: 3

Override path silently stores 'five' as a string. Fix is calling validate_data_types(job, self.job_defaults) at the end of _apply_override, plus a warning for unknown attr names like you already do for unknown job names. ~10 lines.

Flag — detect_deletions per-instance doesn't actually work:

WatcherManager is built once from global settings.jobs.detect_deletions in main.py. Per-instance overrides of this one are no-ops — the watcher is global to the process. Either reject the override with a warning or document as global-only. I'd just doc it.

Flag — interaction with web UI runtime config (#337):

Not your bug, just flagging. _create_merged_jobs returns self.settings.jobs (shared) when no override, deep copy when there is one. Once #337 lands, the web UI mutates settings.jobs.X.attr at runtime — those mutations hit no-override instances but get masked on instances with overrides. I'll handle this in the follow-up UI work, probably with a CONFIG_CHANGED event subscriber on each ArrInstance.

Nits:

  • dir(global_jobs) works because of the isinstance filter, but vars(global_jobs).items() is more direct
  • The valid-job-names list rebuilt inline in the warning is a long line, could be a helper

Suggested path:

  1. Fix the validation gap in this PR (small change, plus a regression test for the typo)
  2. Doc note that detect_deletions is global-only
  3. Merge — runtime config interaction is on me

@heapoutofspace

Copy link
Copy Markdown
Author

Hi folks, as you might have guessed from my delayed response, I'm in a similar situation as @ManiMatter and have limited capacity to spare on OSS these days. That said, thank you very much @lolimmlost for the review, I will take the time to fix the flagged deficiencies.

@ManiMatter

Copy link
Copy Markdown
Owner

@heapoutofspace would love to see this merged. Any chance you could pick up the feedback by @lolimmlost ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants