Files
james.maes 80b0b66c49 Merge remote-tracking branch 'origin/develop' into feature/job-progress
# Conflicts:
#	README.md
#	docs/DESIGN.md
#	pom.xml
#	src/main/java/com/kingsrook/qbits/jobs/JobsCoreContract.java
#	src/main/java/com/kingsrook/qbits/jobs/widgets/JobsWidgetRenderer.java
#	src/test/java/com/kingsrook/qbits/jobs/JobsRetentionTest.java
2026-10-14 21:12:47 -05:00

7.5 KiB

QBit Jobs: design

The approved design is QRun-IO/qqq docs/specs/DURABLE-JOBS-AND-ASSISTED-BULK-LOAD.md (#1171), section 2.12 and phase 4. Owner decisions: all of that spec's recommendations are accepted (2026-10-12/13).

Split of responsibilities.

  • Core (QQQ 4.1.x) owns the durable job kernel: the jobs_* tables (DurableJobsMetaDataProvider.defineAll), leases, the per-pod runtime, the plans, the control processes (record actions), uploads, realtime events, alerts and notifications. Without this qbit, jobs still run and the tables still have standard screens.
  • This qbit owns the Jobs app: the home dashboard, the job record panels, the alert type declarations, retention, and the Quick Search, Data Analysis and ESB registration hooks. It adds no tables and no UI code: everything is typed widget data served through /qqq/v1, so any client (Next, a CLI, iOS) can render it.

Requirements traced (owner R1-R6)

  • R3, reportable. Jobs, chunks, row errors, effects and events are standard tables, with full record screens, reports and Data Analysis. Quick Search registers jobs only. The home shows live progress, throughput, failures and history. Every job links to its audit trail.
  • R4, notifications. The alert types jobs.job-failed, jobs.job-stalled and jobs.effect-unresolved ("Job effect needs review", WARNING), and the jobs.reaper heartbeat monitor, are declared here so that qbit-alerts routes them. Completion notifications come from core.
  • R6, storage. Retention deletes finished jobs and their chunk objects from object storage. The qbit reads and writes no files.

Progress and log adoption (core RC.8)

Implements sections 2 and 7 of core JOB-PROGRESS-AND-JOBS-TRAY.md. The default BOM is 4.1.0-RC.8; local verification uses -Dqqq.snapshot.version=4.1.0-SNAPSHOT. Optional names remain in JobsCoreContract without importing core.jobs.

  • Home and Live Progress prefer nullable percentDone, including explicit zero, then retain rows/chunks derivation. The snapshot supplies the current phase label; missing or invalid JSON falls back to currentPhase. Both views include the progress message, optional binary byte counts, and explicit etaSeconds ahead of the existing rate-derived ETA. No HTML, fixed widths, or theme-specific colors.
  • Log is a full-width standard child list immediately after Live Progress, before Events, only if both table and join exist. It displays level, loggedAt, phase and message, sorted by id ascending, with the standard 25-row preview/View all. The panel reads through the current viewer and core joined owner lock.
  • A positive logDropped produces an earlier-lines-not-kept notice in Live Progress.
  • Retention deletes logs before chunks/jobs, using the existing 200-row keyset loop with a long child cursor. Core JobLogPermissions explicitly permits system sessions and denies user mutations even for admins. Tests load the actual checker.
  • Hosts set parentAppName to their existing Operations app under Admin; the qbit sets the parent and attaches Jobs as a child, while retaining configurable appName. Missing/self parents fail validation before registration. Default remains top-level.

The app

  • jobs app, label Jobs. The nav lists jobs_job only. Chunks, row errors, effects and events are child lists on the job record.
  • Home, built from typed widgets read in the viewer's session (record locks apply: a user sees their own jobs, jobs_admin sees all). Live widgets set defaultValues.refreshSeconds (polling; the realtime job.progress channel is the push path for clients that subscribe).
    1. Overview (MULTI_STATISTICS): two groups. Now has Running, Queued and Needs attention, each linking to the matching jobs. Last 24 hours has Completed, Rows processed and Rows/min (last hour). It refreshes every 5s.
    2. Running and queued (TABLE, three block columns): the job (its label links to the record, with the process label under it), the progress (a bar, the percent, and rows done of the total), and the status (a label or the waiting reason, then the rate and ETA, or the start time). It refreshes every 5s.
    3. Rows per hour (STACKED_BAR_CHART): rows per hour over the last 24 hours, by process label.
    4. Needs attention (TABLE): FAILED, NEEDS_REVIEW and COMPLETED_WITH_ERRORS jobs from the last 7 days. Failed and needs-review jobs come first, newest first within each group. Each row shows the job, the error on its own line, the rows failed and the finish time. The status cell includes "N effects need review" when applicable, from one viewer-scoped effects query for the listed jobs. RUNNING jobs remain excluded; core moves them to NEEDS_REVIEW.
    5. Workers (TABLE): pods from jobs_worker, with threads, draining, and last heartbeat age (admins only).
    6. Job history (STACKED_BAR_CHART): jobs per day over the last 30 days, by final status label. Times within a day are relative; older ones are absolute UTC. Data carries no inner titles, because the widget label is the card title.
  • Job record
    • A Live Progress panel (MULTI_STATISTICS) has three groups: Rows (succeeded, failed, total, and the percent done), Chunks (done, failed, total) and Speed (rows per minute, with the ETA, the finish time or the status). An optional fourth group, Effects, shows Sent, Pending (PENDING, SENDING and UNKNOWN without needsReview), Failed and Needs review. Review effects appear only in Needs review. The group is omitted when there are no effects or the effects table is absent. It refreshes every 3s while the job is active.
    • Child lists for Log, Chunks, Row errors, Effects and Events. Effects is included only when the effects table and its inferred job join exist; it uses the same joined owner lock as row errors.
    • A Changes made by this job link to the audit table, filtered to messages containing job:<id>/ (core writes job:<id>/chunk:<seq> into every audit the job causes).
    • The record actions are core's: Start, Pause, Resume, Cancel, Retry Failed, Retry Failed Rows, Download Failed Rows, Restart, Run Again.

Retention

jobsPurgeFinished is a hidden scheduled process, not in the nav. It runs daily by default.

  • It deletes jobs that finished more than retentionDays ago (default 30), with their chunks, row errors, effects, logs, events and chunk objects. Effects are removed before chunks and jobs.
  • Jobs with any effect outside SENT/FAILED, or any effect needing review, keep their rows and inputs. The summary reports "N jobs kept: effects still unsettled" when any are skipped.
  • For COMPLETED jobs with no failed rows, it deletes the chunk input objects after inputRetentionDays (default 7).
  • Each run's summary is the standard process summary.

Integrations (opt-in, no dependency on the other modules)

JobsIntegrations.esbTableNames(), quickSearchTables() and dataAnalysisTables(): a host passes these to qqq-esb, qbit-quick-search and qbit-data-analysis.

Dependencies

Requires QQQ 4.1.0-RC.8 or later (job progress and jobs_log). The default qqq-snapshot profile and the opt-in demo profile both use released RC.8. Core provides table metadata, joins, possible values, session security, signal identifiers and storage deletion. The qbit declares the alert types and retains only field names that core does not export. The demo server under src/demo/java uses the same released kernel.