Commit Graph
5 Commits
Author SHA1 Message Date
james.maes 4ed22a9164 fix(#24): recover a stuck REBUILDING status after a killed reindex (#25)
* fix(#24): recover a stuck REBUILDING status after a killed reindex

A full reindex now refuses to start while another is alive, judged by a per-page heartbeat on its FULL_REINDEX run records. A run stale for fullReindexStaleMinutes (default 30) is recovered by the next basepull or full reindex: its records become FAILED, its tables ACTIVE, and its captured deletes PENDING for basepull.

Closes #24

* fix(#24): keep a slow captured-delete drain from touching a newer run's rows

The drain now heartbeats every batch, reads only rows up to the newest one at its start and stops once a recovery takes the run over; recovery marks dead runs FAILED last, basepull skips replayed deletes of re-created records, and the heartbeat is best effort and FULL_REINDEX only.

Refs #24

* fix(#24): fail recovery when a dead run cannot be marked FAILED

Recovery now writes FAILED with an update that must succeed for every stale run record, so a new full reindex refuses to start rather than run beside a misjudged live one. Tests pin each heartbeat point and the unreadable-takeover branch.

Refs #24
2026-10-12 06:05:18 -05:00
james.maesandClaude Opus 5.5 e1771411e1 fix(#29): validate cron zone, warn when basepull is off, document schedulers (#31)
* fix(#29): validate cron zone, warn when basepull is off, document schedulers

A reconcileCronExpression without reconcileCronTimeZoneId passed the
QBit's validation, then QQQ's QInstanceValidator rejected the reconcile
process schedule at startup ("a cron schedule must specify a
cronTimeZoneId"); QQQ has no default zone. validate() now applies the
same checks QQQ does (Quartz CronExpression.validateExpression, a zone
from TimeZone.getAvailableIDs) and requires the zone, naming both
fields. It also rejects a cron on a scheduler in the instance that does
not support cron: QQQ's SimpleScheduler runs fixed intervals only and
fails when the schedule manager starts.

Config-driven tables without their basepull timestamp field now log the
same startup warning as annotated tables, once per table, through one
shared helper. A field set to null is an opt-out and stays quiet.

README and MIGRATION-1.0 document that schedules run only when the host
starts through QApplicationLauncher (qqq-middleware-javalin), and that a
reconcile cron needs a Quartz scheduler and a time zone ID.

Closes #29

* docs(producer): keep develop's hidden-and-password Javadoc after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:00:59 -05:00
james.maesandClaude Opus 5.5 316ac9484f feat: serve QQQ /qqq/v1/search from OpenSearch (record-search provider)
Implements QQQ core's RecordSearchProviderInterface so the Next UI global
search (POST /qqq/v1/search) is served from the Quick Search index for the
tables this QBit indexes. The provider returns record ids only; core re-reads
them through QueryAction, so table permissions, record security locks and
labels stay core's job. One bounded _msearch per call, with a short backoff
after a failure during which core falls back to its own LIKE search.

Builds against the released QQQ 4.1.0-RC.2, the first release that carries
the SPI (replaces the 4.1.0-SNAPSHOT pin from PR #32).

Re-signed from PR #32 (originally 75dc2b3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:23:22 -05:00
james.maes 2398e62711 fix(#15): harden transport config validation (#18)
* fix(#15): harden transport config validation

Warn when tls.hostnameVerification is off, treat empty resolved secrets and paths as missing with the field named, and reject credentials in opensearchUrl.

Closes #15

* fix(#15): never echo literals from secret fallback chains

Only a lone ${env.X} or ${prop.X} reference is named in resolution errors, and a happy-path test guards resolved BASIC credentials.

Refs #15
2026-10-06 18:57:57 -05:00
james.maes 0cd892d1a0 feat: 1.0 release readiness (#10)
* docs: add 1.0 PRD, deep-dive audit, plan, and session state

* build: target qqq 4.1.0-RC.1, opensearch-java 3.10.0, wrapper 3.9.11, real JaCoCo gate

* feat: add 1.0 config surface, instance-scoped runtime, and deprecated static facade

* feat: implement the 4.1 QBit contract with a record-change listener and failed-event capture

* feat: opensearch client with auth modes, TLS, timeouts, mapping v2, aliases, versioned bulk writes

* feat: trustworthy indexing steps and permission-aware bounded search

* docs: rewrite README for 1.0, add CHANGELOG and migration guide

* feat: table picker and result screens for the maintenance processes; close out 1.0 planning docs

* docs: add AGENTS.md so Codex shares the CLAUDE.md guidance

* fix: keep the live index after an alias swap, atomic legacy upgrade, exact paging under record locks

From the PR #10 review: never delete the new physical index once the alias points at it, upgrade a legacy concrete index in one aliases request, base hasMore on raw hits so filtered pages stay reachable, and deduplicate requested table names.

* docs: explain the hasMore trade-off under record locks

* docs: session state for the RC.1 hand-off
2026-10-05 09:50:05 -05:00