u70b3 opened a new pull request, #67754:
URL: https://github.com/apache/doris/pull/67754
> Target: branch-4.1. Stacks on #67201, #67235 and #67630 (slice 3C).
Implements slice 3E of design v5.1 (issue #66497): `RESOLVE LANCE INDEX JOB
<id> AS FORCE_RELEASE COMMENT '<note>'` — the operator escape hatch that
durably releases an UNKNOWN job — plus the retention GC that removes resolved
job records. There is no dispatcher yet (next slice): no real UNKNOWN job can
be produced by this stack, so FORCE has unit-test coverage only and the
regression suite is deliberately negative/static (see Tests).
## What lands
1. **RESOLVE SQL** (design 2.3/7.1): grammar (`RESOLVE` and `FORCE_RELEASE`
are new non-reserved keywords, COMMENT is mandatory), `ForwardWithSync` to
master. Flow: load job (missing and unauthorized share the same fixed 5103,
non-disclosing) → authorize against the persisted target (table ALTER when it
resolves, global ADMIN otherwise) → idempotent short-circuit (an already
released job returns OK + the late-commit warning, even with the pre-release
revision) → UNKNOWN-only state gate (5104) → note validation (non-empty, ≤1024
UTF-8 bytes, 5100) → for a live catalog: one authoritative latest-metadata read
and one external-table refresh with current credentials, both outside every
lock, then the durable release inside the admission critical section
(`captureLanceIndexTarget` → read/refresh → `withLanceIndexAdmission` recheck),
serialized against DROP CATALOG / identity ALTER exactly like admission. The
durable release is one ordinary job upsert with the five FORCE
audit fields set; the fence, the unresolved quota charge and the possible-live
slot flip off through the existing field semantics in `applyToMemory` — no new
teardown code.
2. **Failure semantics** (7.1 step 4): any read/refresh failure is the typed
5105 (`ERR_LANCE_INDEX_JOB_RESOLUTION_INCOMPLETE`); nothing is logged, the job
stays UNKNOWN holding fence/quota/slot, and the operator fixes the cause
(including the 4.3 credential rotation case) and retries the same statement.
Failure details pass through the catalog's sanitized root-cause chain only — no
locator, credential or dataset URI is echoed.
3. **Retention GC**: resolved records (force-released UNKNOWN and
refresh-DONE terminal alike) are audit-only; a master-only
`LanceIndexJobCleaner` daemon (interval
`lance_index_job_clean_interval_second`, default 1h) removes records resolved
longer than `lance_index_job_keep_max_second` (default 7d, aligned with
`history_job_keep_max_second`) through one batch edit-log record
(`OP_LANCE_INDEX_JOB_REMOVE = 501`) per round, so every FE serves the same SHOW
LANCE INDEX JOBS view. Unresolved records are never removed regardless of age
(fail-closed, including corrupt identity-less records); the retention clock is
the durable update time, which the force release bumps onto the force time.
4. **Config**: the two retention items are mutable, masterOnly,
positive-validated (same handler as the existing quotas).
## Two design readings that need reviewer confirmation
1. **RESOLVE is not behind `enable_lance_index_mutation`.** The gate
controls mutation admission; FORCE is the escape hatch and must stay usable
exactly when the gate is off — otherwise PENDING/UNKNOWN jobs could never be
released once the gate is closed, and the catalog DDL guard would freeze that
catalog forever. Section 2.3 does not list RESOLVE among the gated statements.
2. **Full-orphan jobs skip the authoritative read and the refresh.** Section
7.1 step 1 explicitly opens the global-ADMIN branch for a target that no longer
resolves, but steps 3-4 ("with current credentials") cannot execute when the
catalog itself is gone — there are no credentials and nothing to invalidate.
Requiring them mechanically would make an orphan job unreleasable,
contradicting step 1. So: catalog gone → release directly after ADMIN
authorization; catalog alive but db/table unresolvable (half-orphan) → skip the
read, refresh with `ignoreIfNotExists=true` as best-effort invalidation. A
non-null exception during target resolution is never treated as an orphan
verdict — it fails with 5105 so the fence is kept when "table gone" cannot be
told apart from "network down". (Slice 3C's guard makes orphans unreachable on
the normal path; they can only come from pre-guard journals/images.)
## Operational notes
- A FORCE retry is a success, not an error: the idempotent short-circuit
deliberately runs before the expected-revision check, so a retry carrying the
pre-release revision still observes the existing release record (unlike the
stale-callback convention of the dispatch paths).
- The release does not clear `possibleLiveOwned`/`terminationProof`; the
derived slot is released by `forceReleased=true` and the raw values stay for
audit.
- Success returns an OK packet with one warning row carrying the late-commit
warning, persisted verbatim as the job's `forceWarning`: the old worker may
still overwrite, remove, or reintroduce the index name; the mutation outcome
remains UNKNOWN. (SHOW WARNINGS is a stub in the new planner; the OK packet is
the only working warning channel.)
- Audit needs no extra code: the coordinator FE's standard audit log carries
the full SQL (note included), the durable force* fields replay everywhere, and
SHOW LANCE INDEX JOB(S) already renders the force columns.
- The GC daemon is registered in `startMasterOnlyDaemonThreads`
(MasterDaemon itself does no master check); both retention configs are re-read
every round, so ADMIN SET takes effect without restart.
## Tests
- FE UT (JUnit5, no FE startup): manager FORCE transition (success with
atomic fence/quota/slot release and exactly one journal record, idempotent
retry with stale revision, four non-UNKNOWN states rejected without a journal,
revision CAS, bounded-text rejection, same-name re-admission, corrupt
identity-less unblock, refresh-queue filter); retention GC (both resolved
families removed, unresolved never removed at any age, one batch record per
round, per-round cap, replay convergence/idempotence, image shrink, 501
JournalEntity round-trip, update-time clock base); config validator (defaults,
wiring, rejection, setMutableConfig end-to-end); RESOLVE parser (full forms,
`resolve`/`force_release` stay usable as identifiers/aliases, rejection
family); RESOLVE command (5103 byte-identical for missing vs unauthorized,
ALTER vs ADMIN authorization, proxy identity, note validation, 5104 gate,
idempotent replay, full/half-orphan paths, read/refresh failures keep
everything with zero manager c
alls, REST defensive 5101, FORWARD_WITH_SYNC). Full FE suite (`mvn test -pl
fe-common,fe-core -am`, no checkstyle skip): fe-common all green; fe-core 9392
tests, the only failures being 19F+2E in 14 classes (date/timezone literals,
huggingface network unreachable, native-lib link, etc.) reproduced identically
on the base commit — environmental, unrelated to this PR. The 97 tests in the
seven touched/new Lance classes are all green.
- Regression (`external_table_p0/lance/test_lance_index_resolve.groovy`,
nonConcurrent, per-run suffix, master-config capture/restore): syntax rejection
family, missing job 5103, unauthorized 5103 under a second identity, PENDING
job rejected with 5104, retention config SHOW/SET smoke (defaults,
zero/negative rejected), GC never touches an unresolved job. Needs the external
MinIO/REST docker env — runs in pipeline, not locally.
- Not covered here (needs the dispatcher slice): FORCE happy-path e2e,
same-name reuse e2e, quota reclaim e2e, expiry-GC e2e — there is no way to
produce a real UNKNOWN job in this stack.
## Deliberately not done
- No dispatch/worker (next slice); no gate flip (9.7 evidence gates).
- No "persisted locator + storage options" bypass read for half-orphans
(optional enhancement; the late-commit warning already covers the risk).
- No count-based GC cap (the time bound plus the global unresolved quota
already bounds the table).
- RESOLVE takes one job id per statement (the 2.3 form); no WHERE/batch form.
- SHOW output unchanged (the force columns already render since slice 3C).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]