Skip to content

fix(ci): verify CN exit before collecting BVT coverage - #463

Open
XuPeng-SH wants to merge 1 commit into
mainfrom
codex/fix-cn-coverage-collection
Open

XuPeng-SH wants to merge 1 commit into
mainfrom
codex/fix-cn-coverage-collection

Conversation

@XuPeng-SH

Copy link
Copy Markdown
Contributor

Summary

Require BVT coverage to contain counters from SQL-serving CNs that exited normally. The current workflows can publish a syntactically valid profile containing only the other service roles, which then appears as low PR test coverage.

Observed in matrixorigin/matrixone#29241, run 36205017094: both BVT profiles have zero hits in mysql_cmd_executor.go despite successful SQL cases. The coverage parser's union is correct. Reproduction with the exact CI binary showed that both a 10-second stop and a 120-second stop ended at exit 137 with no CN counters: the global Stopper waited on the cgroup watcher's uncancellable unix.Read.

The runtime fix is included in matrixorigin/matrixone#29241. Rollout dependency: land the runtime repair before enabling these stricter checks for branches that still contain the old watcher. A longer stop timeout alone does not fix the hang.

Changes

  • Stop both original CN containers while their TN/log dependencies remain available; allow bounded shutdown grace and require exited/0/not-OOM states before removing the containers.
  • Always perform the existing container cleanup. Publish a profile only after successful stop and cleanup, and only when it contains positive SQL executor counters.
  • Clear this run's raw counter directory at startup and atomically publish the validated profile; reject stale, malformed, empty, and other-role-only profiles.
  • Share the small collector between proxy and pessimistic BVT jobs. Preserve the existing legacy-branch behavior when the Compose file does not support coverage.
  • Keep the coverage threshold and SQL result assertions unchanged.

Validation

  • Python collector tests: 9/9 pass, including timeout/CLI errors, missing CNs, either CN failing exit/OOM checks, malformed or incomplete counters, repeated publication, temporary-file cleanup, and legacy configuration.
  • Actionlint 1.7.12 on repository workflows; action/workflow JSON schemas; Python pyflakes: pass.
  • Real two-CN source-built instrumented MatrixOne at ec6aa1a1f66bc36ac0b83400ee7e07d107cd28ff: strict table_dump_load.sql 96/96 twice and TimeToInt.sql 267/267 twice. Each fresh run used new counters; both CNs exited 0 in about 7.4–7.5 seconds and wrote separate counter files. The helper then performed cleanup and produced a valid profile with SQL executor hits.
  • Original bad CI profiles and an actual loader-failure exit were rejected by the helper.

The local integration used a Debian 13 runtime matching the local native build; the failing baseline used the downloaded CI binary and its original image. These are lifecycle/collection proofs, not a claim that every remote BVT job or #29241's full coverage gate has already passed. Independent review found no code blocker in this collector.

Risk and rollback

This intentionally turns missing CN counters into a producer failure. Healthy jobs do not sleep for the 120-second limit; measured CN stop was about 7.5 seconds. No database query path or coverage mode changes. Revert the workflow/action change to restore the previous collection behavior.

XuPeng-SH added a commit to matrixorigin/matrixone that referenced this pull request Sep 26, 2026
## What type of PR is this?

- [ ] API-change
- [x] BUG
- [ ] Improvement
- [ ] Documentation
- [ ] Feature
- [ ] Test and CI
- [ ] Code Refactoring

## Which issue(s) this PR fixes:

Fixes #28594

## What this PR does / why we need it:

- Applies the session `div_precision_increment` setting to exact DECIMAL
division type derivation and evaluation, including prepared statements
and CTAS metadata.
- Derives DATE, YEAR, TIME, DATETIME, and TIMESTAMP division precision
from their packed numeric values. Ordinary DATETIME(6) division now
returns DECIMAL(24,10) instead of an unnecessary DECIMAL256(42,10).
- Widens Decimal64 operands to Decimal128 when a temporal or integer
operand can exceed Decimal64's 18-digit range; retains Decimal256 when
the declared result or input requires it.
- Bounds Decimal256 shifts and validates declared precision after
rounding. Handles negative and 64-bit divisor edges without overflow or
divide-by-zero panics.
- Expands unit and BVT coverage for all temporal types, increments
0/4/10/30, reversed operands, full-range TIME(6), prepared execution,
CTAS, overflow, and metadata.

Validation on rebased head `aafa3abeac` (base `71c415705c`):

- Full `pkg/sql/plan/function` and `pkg/sql/plan` UT: pass. Focused
frontend prepared-plan and plan-cache UT: pass.
- `make build-with-prebuilt-native`: pass. Strict metadata BVT on the
rebased service: `issue_28594_div_precision.test` 70/70 and
`TimeToInt.sql` 267/267, both with zero failures.
- Before rebase, strict metadata BVT passed twice for each case (70/70
and 267/267 per run); `time.test` passed 82/82, `datetime.test` 149/149,
and `timestamp.test` 126/126. `go vet` passed; golangci-lint v2.6.2
reported 0 issues.
- A 1,024-row expression benchmark reduced retained bytes for implicit
DATETIME(6) division from 81,952 to 32,784, matching the explicit
DECIMAL(20,6) path. On an idle host, the new path measured 373–393 µs
versus the earlier 394–402 µs; performance did not regress in this local
probe.
- Independent final review found no remaining blocker. New-head remote
CI is tracked in the PR checks.


## Mixed-version and direct execution follow-up (`9d0519ba54`)

- Prevent new decimal DIV/0 plans from running on v96 CNs: placement
falls back to one CN during rollout, and the sender rechecks the actual
destination before transmission. v97 receivers continue to accept legacy
v96 plans, whose stored scale the new executor honors.
- Require the v97 catalog admission floor when authoring persisted
decimal division expressions, including views whose division folds
during planning.
- Rebuild the direct-execution Decimal128 return type so its physical
Size is 16 bytes; serialized result vectors now round-trip.
- Added focused regression tests for feature detection, v96/v97
placement and send behavior, unknown workers, legacy-plan execution,
folded view admission, and direct-result serialization.

Validation on this commit: full `pkg/pb/plan`, `pkg/sql/plan/function`,
`pkg/sql/plan`, and `pkg/sql/compile` unit tests pass; focused
mixed-version tests and `go vet` pass. `make build-with-prebuilt-native`
passes. Strict #28594 BVT on the new service passes twice on the same
instance, 70/70 each run, with empty error reports. Independent review
found no remaining confirmed blocker. New-head CI is pending.

Rolling-upgrade note: legacy views executed locally on a v96 CN keep
their previous decimal division semantics until that CN is upgraded.
Newly authored views using the revised semantics wait for the v97
catalog admission floor.

## DDL expression and QA follow-up (`3903e9cbf5`)

- Apply the session `div_precision_increment` setting to CREATE/ALTER
generated columns, defaults, ON UPDATE, CHECK constraints, and CTAS
defaults. Internal COPY ALTER SQL inherits the session variable
resolver.
- Preserve already-bound expressions during COPY ALTER and CREATE TABLE
LIKE when their definitions and referenced types are unchanged. New
expressions bind under the current setting; expressions that depend on
changed types rebind. Existing column references are remapped across
inserted, reordered, and hidden columns.
- Added unit tests for DDL binders, replay remapping,
type/enum/auto-increment incompatibility, and internal variable
resolution. Expanded #28594 BVT to cover old/new precision, CHECK
success and failure after COPY ALTER, defaults, LIKE, and type-change
rebinding.

Validation on this commit: full `pkg/sql/plan` and `pkg/sql/compile` UT
passed; `go vet` on both packages passed; golangci-lint v2.6.2 reported
0 issues; native `mo-service` build passed. Strict metadata BVT on the
rebuilt service passed twice, 117/117 statements per run. Existing
`generated_column.sql` passed 334/334 with historical metadata
differences ignored; `check_constraints.sql` passed 20/20 and
`create_table_like.sql` 25/25 under the same comparison mode. The new
case used strict metadata comparison. The local SQL probe independently
confirmed retained 12-digit values after column reorder while current
direct division used 6 digits.

Root-cause lesson: earlier tests covered operator inputs and precision
values but omitted the DDL consumers and persisted-expression lifecycle.
This change adds a consumer × setting-change × schema-change matrix,
including negative CHECK cases, so rebinding mistakes surface as value
or metadata mismatches.


## CTAS inherited DEFAULT precision repair (`cb7025e3a3`)

- Keep the source's already-bound row-reference DEFAULT when CTAS only
changes the session precision. Check assignment type and every local
operand against the final target schema; rebind when an actual operand
or assignment type changes. Explicit target defaults still bind under
the target session.
- Reconcile nullability annotations when an operand becomes nullable.
Continue dependency validation and persisted-expression protocol
admission for the new table, even when reusing a source binding.
- Added planner UT and strict metadata BVT for inherited rows and future
inserts, alias and reorder mapping, changed types, explicit target
defaults, nullable operands, source-catalog immutability, and protocol
admission. The 19-case manual matrix records pre-fix 9/19 versus
post-fix 19/19 twice.

Validation on this commit: full `pkg/sql/plan` UT passed; `go vet
./pkg/sql/plan` passed with repository CGo paths; golangci-lint v2.6.2
reported 0 issues. Rebuilt `mo-service`; strict
`issue_28594_div_precision.test` BVT passed twice on one service,
140/140 per run with zero failed, ignored, or abnormal statements.
Existing `expression_default_column_reference.sql` BVT passed 107/107
using its historical metadata-ignored mode. All manual probe databases
and BVT database were removed. New-head CI is pending.

Why repeated review was needed: the first planner mock omitted the
catalog `Type.Table` lineage marker. That made a type comparison appear
correct in UT while the SQL service still rebound inherited defaults.
The regression test now models that marker, and public SQL verifies the
persisted behavior. The new reuse path also required an explicit
protocol-admission test; adding it caught a second omission before
submission. The case inventory in `test/manual/issue28594/README.md`
records 74 coverage/gap entries and 10 remaining separate gaps.


## Table DUMP/LOAD binding repair (rebased head `08495b29d8`)

A table can contain persistent expressions authored under different
`div_precision_increment` values. The v1 DUMP manifest carries text and
a schema hash, but no per-expression binding. LOAD could accept a
matching target created under another setting, then evaluate newly
inserted rows at the target's precision rather than the source's. A real
mixed DEFAULT=10/GENERATED=4 table reproduced this mismatch.

- Emit manifest v2 only when a persisted expression is
precision-sensitive. Carry bounded bound-expression metadata, and accept
it at LOAD only if ordinary DDL binding of the target's own SQL origin
under a legal increment 0..30 reproduces each executable tree. Match
each expression independently, so mixed values survive. A SHA256 digest
detects corruption but does not authorize executable data.
- Apply the same protobuf wire preflight at DUMP and LOAD before
`Unmarshal`: at most 16,384 columns/checks, 100,000 fields, depth 64,
and a 32 MiB payload. Reject duplicate JSON fields and incompatible
declarations. Validate relation/object/auto-increment metadata before
one guarded catalog replacement, preserving the target's owner and
creation time. An ambiguous v1 dump fails with a new-dump remedy before
mutation.
- `SHOW CREATE` copied independently into a new session is a separate
SQL contract: the current CREATE syntax cannot express different
increments per expression. Snapshot/PITR ordinary clone already carries
bound expressions and is unaffected by this manifest change.

Design: `docs/design/20260925-table-dump-expression-binding.md`,
revision 10, independent review PASS (SHA256
`89d3587ff02d50cee59a442cd7eff6ba311e24ea045598550d67b6537a570868`).
Final independent implementation review: PASS on clean head
`08495b29d8`, with no remaining blocker.

Validation on rebased production commit `9c18076b98` (base `3f0a68bd80`)
plus test-only commit `08495b29d8`:

- `make build`: PASS. Full `pkg/sql/plan` and `pkg/frontend` UT: PASS
(17.211 s and 98.044 s). New exporter-cap test targeted: PASS.
Incremental `go vet` on both changed packages with repository CGo flags:
PASS.
- Strict `table_dump_load.sql` BVT on the rebuilt service: 83/83 twice,
zero failures, ignored, or abnormal cases. Covers both mixed-precision
directions, old and new rows, and generated-column UPDATE results.
White-box tests cover 0/4/10/30, nested/folded division, tampered
executable tree, parser mode differences, producer/reader resource
limits, and immutable target-definition preparation.
- Failure-path SQL probe: a v2 full dump with a deliberately altered
object size failed after `ReplaceDef`; after an explicit `COMMIT`, the
target still bound `(1,3)` at its original 4-digit setting. A downgraded
v1 manifest was rejected before mutation. A service restart preserved
the correctly loaded source precision and the rolled-back target
precision.
- Comparable one-relation/one-object metadata manifests: v1 1,096 B; v2
1,584 B (277 B raw bound metadata). Ten LOADs per round on one service,
alternating v1/v2/v2/v1, took 1.250/1.421/1.797/1.985 s respectively;
ranges overlap under background service load. This is an
administrative-path probe, not a throughput benchmark. No row-evaluation
hot path changed.

Compatibility: older LOAD readers reject v2; upgrade readers before
producing v2 dumps. v1 remains for insensitive tables. The dump source
must be kept when reverting a reader that cannot load v2. Remote CI on
the new head is pending.

## DUMP binding and resolver repair (`414c2d8f49`)

- Keep complete frontend compiler-context delegation, but query a
partial internal process resolver only for session
`div_precision_increment`. This restores the old nil/default behavior
for unrelated variables such as `foreign_key_checks`, which caused 27
exact-head UT failures.
- Rebind generated expressions with every visible column in declaration
order, then reject self-reference explicitly. CREATE and DUMP now agree
when a generated column appears first, middle, or last.
- Rebind each CHECK using a shallow, read-only table header instead of
deep-copying every CHECK on every candidate. Stop trying increments
after both a legal match and table sensitivity are known. Remove the
redundant frontend nil check flagged by SCA.

Validation: full `pkg/sql/compile`, `pkg/sql/plan`, and `pkg/frontend`
unit suites passed locally; golangci-lint v2.6.2 incremental analysis of
these packages reported 0 issues. The rebuilt service passed strict
`table_dump_load.sql` twice on one instance (96/96 each), including old
rows and future inserts with generated columns at first and middle
positions; strict `issue_28594_div_precision.test` passed 140/140. The
100-CHECK local probe measured about 856 MB cumulative allocation and
430 ms on the previous head; the final benchmark measured 18.4 MB/op and
36.7 ms/op on the same host. These are local administrative-path
measurements, not a peak-RSS or throughput claim. Independent final
review: PASS. New-head remote CI is pending.

Why this needed another pass: earlier tests put generated columns only
after their operands, so a shifted column coordinate escaped. The work
budget counted AST visits but missed full-schema copies. Plain `go vet`
did not cover the CI `nilness` analyzer. This repair adds
first/middle/last position cases, a count-scaling benchmark, an explicit
schema-immutability assertion, and the CI-configured linter check.

## CN shutdown and adversarial QA follow-up (`5ef74a001e`)

- Fixed a shared shutdown bug that made both BVT jobs lose their
SQL-serving CN coverage counters. The cgroup watcher now uses a
nonblocking inotify descriptor through the Go poller; cancellation
closes it, and setup/admission failures release it. The exact old CI
binary hung even with a 120-second grace period. This also repairs
ordinary service shutdown, independent of coverage.
- Added correctly hashed malformed DUMP metadata cases and exact
CHECK-count/field-count boundaries. Rejected input produces no
replacement and leaves the target definition unchanged. A valid round
trip is the positive control.
- Shared collection integrity checks are in matrixorigin/CI#463. That PR
documents its dependency on the runtime fix; it does not lower the
coverage threshold.

Local validation: system package normal/race tests pass; focused watcher
race tests pass 100 repetitions; new frontend QA normal/race tests pass;
changed-package configured SCA reports 0 issues. Strict source-built
two-CN BVT passes DUMP/LOAD 96/96 twice, TimeToInt 267/267 twice, and
#28594 division 140/140 twice after normal catalog admission. Both CNs
exit 0 and write counters in the validated teardown. The latest
production binary is `ec6aa1a1f6`; `5ef74a001e` and `58058cf406` add
tests only.

Coverage update at `58058cf406`: the prior local 551/725 (76.00%)
calculation included a locally controlled BVT teardown. The actual
remote CI run for `5ef74a001e` reported **532/725 (73.38%)**, although
DUMP/LOAD BVT ran 96/96; its collected CN profiles recorded zero hits in
the DUMP binding files. The new frontend declaration-matrix UT directly
executes the restore and rejection paths. Merging its focused UT
coverage with that exact failed CI merged profile using the unchanged
parser, filters, and strict >75% gate yields **550/725 (75.86%)**, an
18-block gain with the same 725 changed blocks. This is a local replay
of the CI gate, pending the new-head remote result. The separate
collector integrity fix remains in matrixorigin/CI#463.

Performance evidence from the unchanged SQL repair remains: 100 CHECKs
measured 36.7 ms / 18.4 MB allocated per operation versus the previous
reproducer's 430 ms / 856 MB; the 1,024-row temporal-division probe
reduced retained bytes from 81,952 to 32,784. The watcher adds bounded
process-level state with no per-row work. These are scoped local
measurements, not universal throughput or peak-RSS guarantees.

---------

Co-authored-by: XuPeng-SH <xupeng3112@163.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant