The Job Processing module is the asynchronous execution engine of the frontend web application. It owns the lifecycle of a documentation-generation request from the moment it is queued until the generated documentation is either served from cache or produced fresh, persisted to disk, and made discoverable to future requests. The module is composed of two tightly cooperating components:
BackgroundWorker– a background thread that pulls queued jobs, orchestrates repository cloning and documentation generation, tracks job state, and persists job history to disk.CacheManager– a content-addressable cache that avoids redundant documentation generation by mapping repository URLs to previously generated documentation paths, with expiry and corruption-recovery handling.
Together, these components decouple the web-facing request layer from the (potentially long-running) documentation generation pipeline, allowing the web server to respond immediately while work happens asynchronously in the background.
Job Processing is a child module of the Frontend Core module. It sits between the HTTP-facing request layer and the heavier backend documentation-generation pipeline:
- Request Handling (
WebRoutes) receives HTTP submissions and calls intoBackgroundWorker.add_jobto enqueue work, then pollsBackgroundWorker.get_job_statusto report progress back to clients. - GitHub Integration (
GitHubRepoProcessor) is used byBackgroundWorkerto resolve repository metadata and clone repositories prior to generation. - Configuration And Data Models supplies the
JobStatusandCacheEntrydata structures that both components operate on, as well asWebAppConfigfor directory/queue settings. - The backend documentation pipeline (
DocumentationGenerator) and the sharedConfigobject are invoked directly byBackgroundWorkerto perform the actual analysis and generation work; see the backend module documentation for details on that pipeline.
graph TD
Routes["Request Handling: WebRoutes"] -->|"add_job()"| Worker["BackgroundWorker"]
Routes -->|"get_job_status()"| Worker
Worker -->|"reads/writes"| Cache["CacheManager"]
Worker -->|"get_repo_info() / clone_repository()"| GH["GitHub Integration: GitHubRepoProcessor"]
Worker -->|"DocumentationGenerator.run()"| DocGen["Backend: DocumentationGenerator"]
Worker -->|"Config.from_web_job()"| CoreConfig["Config Core: Config"]
Cache -->|"uses"| CacheEntry["CacheEntry model"]
Worker -->|"uses"| JobStatus["JobStatus model"]
Worker -->|"reads settings"| WebAppConfig["WebAppConfig"]
BackgroundWorker runs a dedicated daemon thread that continuously drains a bounded Queue of job IDs and processes them one at a time. It is the central coordinator for the entire documentation generation workflow on the frontend side.
Responsibilities:
- Queueing –
add_job(job_id, job)registers aJobStatusobject and enqueues the job ID for processing. - Worker loop –
start()spawns a daemon thread executing_worker_loop(), which polls the queue and dispatches jobs to_process_job(). - Job execution –
_process_job(job_id)drives the full pipeline:- Marks the job
processingand records the start time. - Checks
CacheManager.get_cached_docs()for existing documentation; if found and the docs path still exists on disk, marks the jobcompletedimmediately. - Otherwise, resolves repository metadata via
GitHubRepoProcessor.get_repo_info()and clones the repository into a temporary directory usingGitHubRepoProcessor.clone_repository(). - Builds a
ConfigviaConfig.from_web_job()pointing at the cloned repository and a job-specific docs output directory. - Instantiates
DocumentationGeneratorand runs its asyncrun()coroutine inside a freshly created event loop (since the worker thread has no default asyncio loop). - On success, stores the resulting docs path in the cache via
CacheManager.add_to_cache()and marks the jobcompleted. - On any exception, marks the job
failedand records the error message. - Always cleans up the temporary cloned repository directory.
- Marks the job
- Persistence –
save_job_statuses()serializes all known jobs tojobs.jsoninside the configured cache directory;load_job_statuses()restores previously completed jobs on startup (in-flight/failed jobs are intentionally not restored, to avoid resuming inconsistent state). - Cache-based recovery –
_reconstruct_jobs_from_cache()rebuilds job records fromCacheManager.cache_indexentries when nojobs.jsonexists yet, providing backward compatibility for deployments upgrading from a cache-only history. - Introspection –
get_job_status(job_id)andget_all_jobs()expose current job state to callers such as Request Handling.
CacheManager maintains an on-disk index (cache_index.json) mapping a SHA-256 hash of a repository URL to a CacheEntry describing where that repository's generated documentation lives and when it was created/last accessed.
Responsibilities:
- Hashing –
get_repo_hash(repo_url)produces a stable, filesystem-safe cache key (first 16 hex characters of the SHA-256 digest of the URL). - Lookup –
get_cached_docs(repo_url)returns the cached docs path if an entry exists and is not older thancache_expiry_days(fromWebAppConfig); it refresheslast_accessedon hit and evicts expired entries automatically viaremove_from_cache(). - Insertion –
add_to_cache(repo_url, docs_path)creates or overwrites aCacheEntryfor the given URL with the current timestamp for bothcreated_atandlast_accessed. - Eviction –
remove_from_cache(repo_url)deletes a specific entry;cleanup_expired_cache()sweeps and removes all entries older than the expiry window in a single pass. - Resilience –
load_cache_index()guards against a corruptedcache_index.jsonby renaming the bad file to a timestamped.corrupted.<timestamp>backup and starting with an empty index rather than crashing the application.
Both components operate on plain data classes defined in Configuration And Data Models:
JobStatus– tracksjob_id,repo_url,status(pending/processing/completed/failed), timestamps,progresstext,error_message,docs_path, andmain_model.CacheEntry– tracksrepo_url,repo_url_hash,docs_path,created_at, andlast_accessed.
WebAppConfig supplies operational settings consumed by both components: TEMP_DIR (clone staging area), QUEUE_SIZE (bounded queue capacity), CACHE_DIR (location of jobs.json and cache_index.json), and CACHE_EXPIRY_DAYS (cache TTL).
classDiagram
class BackgroundWorker {
+cache_manager: CacheManager
+temp_dir: str
+running: bool
+processing_queue: Queue
+job_status: dict
+jobs_file: Path
+start()
+stop()
+add_job(job_id, job)
+get_job_status(job_id) JobStatus
+get_all_jobs() dict
+load_job_statuses()
+save_job_statuses()
-_worker_loop()
-_process_job(job_id)
-_reconstruct_jobs_from_cache()
}
class CacheManager {
+cache_dir: Path
+cache_expiry_days: int
+cache_index: dict
+load_cache_index()
+save_cache_index()
+get_repo_hash(repo_url) str
+get_cached_docs(repo_url) str
+add_to_cache(repo_url, docs_path)
+remove_from_cache(repo_url)
+cleanup_expired_cache()
}
class JobStatus {
+job_id: str
+repo_url: str
+status: str
+created_at: datetime
+started_at: datetime
+completed_at: datetime
+error_message: str
+progress: str
+docs_path: str
+main_model: str
+commit_id: str
}
class CacheEntry {
+repo_url: str
+repo_url_hash: str
+docs_path: str
+created_at: datetime
+last_accessed: datetime
}
BackgroundWorker "1" --> "1" CacheManager : delegates cache lookups
BackgroundWorker "1" o-- "many" JobStatus : tracks
CacheManager "1" o-- "many" CacheEntry : indexes
The following sequence illustrates a single job's journey through BackgroundWorker, including the cache short-circuit path and the full generation path.
sequenceDiagram
participant Caller as "Caller (Request Handling)"
participant Worker as "BackgroundWorker"
participant Cache as "CacheManager"
participant GH as "GitHubRepoProcessor"
participant DocGen as "DocumentationGenerator"
Caller->>Worker: add_job(job_id, JobStatus)
Worker->>Worker: processing_queue.put(job_id)
Worker->>Worker: _worker_loop() dequeues job_id
Worker->>Worker: _process_job(job_id)
Worker->>Cache: get_cached_docs(repo_url)
alt Cache hit and path exists
Cache-->>Worker: docs_path
Worker->>Worker: status = "completed"
Worker->>Worker: save_job_statuses()
else Cache miss
Worker->>GH: get_repo_info(repo_url)
GH-->>Worker: repo_info
Worker->>GH: clone_repository(clone_url, temp_dir, commit_id)
GH-->>Worker: success/failure
Worker->>Worker: Config.from_web_job(repo_path, docs_dir)
Worker->>DocGen: run() (async, new event loop)
DocGen-->>Worker: documentation generated
Worker->>Cache: add_to_cache(repo_url, docs_path)
Worker->>Worker: status = "completed"
Worker->>Worker: save_job_statuses()
Worker->>Worker: cleanup temp_repo_dir
end
Caller->>Worker: get_job_status(job_id)
Worker-->>Caller: JobStatus
stateDiagram-v2
[*] --> pending : add_job()
pending --> processing : _process_job() starts
processing --> completed : cache hit or generation success
processing --> failed : clone or generation error
completed --> [*]
failed --> [*]
flowchart TD
Start["get_cached_docs(repo_url)"] --> Hash["get_repo_hash(repo_url)"]
Hash --> Exists{{"Entry in cache_index?"}}
Exists -->|"No"| Miss["Return None"]
Exists -->|"Yes"| Fresh{{"Within cache_expiry_days?"}}
Fresh -->|"Yes"| Touch["Update last_accessed<br/>save_cache_index()"]
Touch --> Hit["Return docs_path"]
Fresh -->|"No"| Evict["remove_from_cache(repo_url)"]
Evict --> Miss
Both components persist their state as JSON files under the directory configured by WebAppConfig.CACHE_DIR:
| File | Owner | Purpose |
|---|---|---|
jobs.json |
BackgroundWorker |
Durable record of job history; only jobs with status == 'completed' are reloaded on startup to avoid resuming stale in-flight or failed state. |
cache_index.json |
CacheManager |
Maps repository URL hashes to CacheEntry records pointing at generated documentation directories; corrupted files are backed up and replaced with an empty index rather than crashing. |
If jobs.json is absent (e.g., first run after introducing job persistence), BackgroundWorker reconstructs a best-effort job history directly from CacheManager.cache_index via _reconstruct_jobs_from_cache(), deriving a job_id from the repository's full_name (using GitHubRepoProcessor.get_repo_info()).
BackgroundWorkerruns exactly one daemon thread (_worker_loop) that serially processes jobs from a boundedQueue(WebAppConfig.QUEUE_SIZE), meaning documentation generation jobs are processed one at a time rather than in parallel.- Since
DocumentationGenerator.run()is a coroutine,_process_jobcreates and manages its ownasyncioevent loop per job (asyncio.new_event_loop()/loop.run_until_complete()/loop.close()), isolating async execution from the synchronous worker thread. job_statusis an in-memory dictionary shared between the worker thread and any caller (e.g., HTTP request handlers) inspecting job progress; state transitions are written back to disk after each significant change viasave_job_statuses().