A table on a backend module with no query interface (a storage-only module such as qbit-edi's edi_preview_download) was classified DATA, read by the keyset pager, and failed the whole run with "IllegalStateException: Query is not implemented in this module". TableRoles.isQueryable() now resolves the module through QBackendModuleDispatcher (cached per backend type) and such tables are EXTERNAL and always excluded by ScopeResolver with reason NOT_QUERYABLE, even when selected by name or role. The Run backup review lists them, and no per-table export process is offered for them. Failed runs now persist "Backup failed: Class: message" in error_message and the class, cause chain and top root-cause frames (bounded to 8 KB) in error_detail, both sanitized by the new FailureText (URL user-info, presigned query parameters, key=value secrets, Authorization/Bearer values, AWS access key ids, PEM blocks and Postgres row values are redacted; object keys and bucket names are kept). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.0 KiB
5.0 KiB
Changelog
All notable changes to this project are documented here. The format follows Keep a Changelog; versions follow semantic versioning.
[0.1.0] - Unreleased
Added
- Central
backups.slaand per-active-planbackups.destination.<planId>health checks, with bounded read-only destination diagnostics and secret-free status details. - Always-durable backup, resume, scheduled backup, restore and verify processes with conservative
NONEidempotency and stable backup identities on explicit job retry. - Weighted backup phases, table/file counts, stored bytes, safe per-table/artifact logs, warnings, failures and resume notices through QQQ
JobProgress. - Cooperative job cancellation during backup pages, media copying, restore pages and verification scans; local media-reference spooling keeps phase separation from growing heap usage across tables.
- Complete host-neutral in-app help bundle with live references and a help coverage gate. Hosts can override entries with a HOST bundle.
- Guided empty states: help bundle
help/qbit-backups.yaml(app and all nine tables, with "How rows get created") applied byBackupsHelp, and a Setup card on the Backups home (plan, scope rules, storage, schedule, verification, prune bucket). - "Create default plan" table action on Backup plans: adds "Nightly full" for a storage bucket alias; insert-only.
backupVerifyPlan: the scheduled verification process. A plan verify cron created a scheduled job for a process that did not exist, so scheduled verification never ran.- Repository scaffold:
BackupsQBitConfigandBackupsQBitProducer(table prefixbackups_, permissionsbackups.*), build, CI and repository standard files.
Changed
- Tables whose backend module has no query interface (storage-only modules such as qbit-edi
edi_preview_download) are classified EXTERNAL and always left out of a backup with the reasonNOT_QUERYABLE, even when selected by name or role; the Run backup review lists them. Before, such a table was read as DATA and the whole run FAILED with "Query is not implemented in this module". - Failed runs persist an actionable
error_message("Backup failed: Class: message") and fillerror_detailwith the exception class, the cause chain and the top root-cause frames (bounded to 8 KB). Both go throughFailureText.sanitize, which redacts URL user-info, presigned query parameters, key=value secrets, Authorization/Bearer values, AWS access key ids, PEM blocks and Postgres row values while keeping object keys and bucket names. - Multi-node deployments: the cached Prometheus text (
BackupsMetrics) is registered with QQQClusterCachesasqbit-backups.metricsand cleared on every node when a run finishes or a plan is inserted, updated or deleted; the 60 s TTL remains the safety net.SlaChecker's manifest-head and bucket-audit throttles andBackupsRuntime's process-permission map are registered as node-local, with reasons.BackupsClusterCachesTestaudits the package for unregistered static caches and covers a remote invalidation. - Builds against QQQ
4.1.0-SNAPSHOT(qqq.snapshot.version) forClusterCachesuntil the next QQQ RC ships; a release must pin that RC. - Read the QBit metadata version from the Maven artifact version so release builds report their released version.
- Target QQQ
4.1.0-RC.8(job progress). - Persist controlled backup failure messages instead of untrusted provider exception text.
- Pin QQQ to released 4.1.0-RC.7 and replace the temporary help loader with core HelpBundles registration.
- Saving a plan with Use read replica on is refused (it was never read); the not-yet-available settings (read replica, native dump, incremental, PARQUET, ZSTD) say so in their field help; scheduledJobId is documented as unused.
- Build against the released QQQ BOM 4.1.0-RC.5; replace reflective alerts binding with core QAlerts and real sink integration tests.
- Register backup alert types for every instance while preserving custom backup sinks and no-op behavior without a configured core sink.
- Use RC.5-valid alert names
backups.run.missing-in-storageandbackups.plan.owner-invalid(and matching deduplication keys). Hosts using the old underscore names must update routing rules. - Target QQQ 4.1.0-RC.6 and replace the fingerprint, keyset pager and reflective restore bridges with typed core APIs.
- New manifests identify
qqq-table-schema-v1and carry core schema descriptors; restore and content verification still accept pre-RC.6 manifests with the original fingerprint algorithm. - Preserve primary-key ordering, exclusive resume cursors, bounded pages and caller-owned transactions; reject non-orderable primary keys and scope filters with skip/limit.
- Require the core
backups.restorepermission during Review when using a core restore writer, including anonymous-mode hosts; preserve supplied timestamps throughRestoreWriteFlag.
Removed
- The legacy
BackupsHealthIndicatorand its optionalqqq-middleware-healthdependency; central health registration replaces manual indicator configuration.