Task: Implement Hotfix: the nightly Valgrind run reports uninitialised members and library leaks
Table of Contents
This page documents a task in the Hotfix: the nightly Valgrind run reports uninitialised members and library leaks story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
Take the four suites the nightly dynamic analysis reports defects for to
zero: ores.trading.api.tests (4), ores.refdata.core.tests (3329),
ores.ore.core.tests (99) and ores.analytics.core.tests (214). Fix the
defects that are ours and suppress the allocations that are the libraries'.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Hotfix: the nightly Valgrind run reports uninitialised members and library leaks |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-09-29 |
3. Acceptance
- Every uninitialised-value defect the four suites reported is gone, and the
member that caused it is initialised by its model's
:default_value:. - The 3329 still-reachable leak records for
ores.refdata.core.testsare suppressed by rules anchored on the allocating library, each with the reason it is not a leak. - A nullable column whose
:cpp_type:is a bare value type can no longer be merged without a declared default:org_loaderrefuses it. - The full test suite passes, the codegen suite passes, and the sweep over every catalogue component reports no file this branch changed as drifted.
4. Plan
- Reproduce each reported defect locally with
--track-origins=yesso the member and the site that leaves it unset are named, rather than inferred from a truncated CDash page. - Fix each at the model:
:default_value:on the column, then regenerate the component. - Census the rest of the class so the size of what is left is on record.
- Extend
build/valgrind/custom.suppfor the library allocations, and add the codegen gate for the nullable shape. - Re-run the four binaries under valgrind until the logs are empty, and run the whole suite and the drift gates before raising the pull request.
5. Measurements
Every number this task claims about a tree, with the command that produced it and the commit it was measured at.
| What | Value | Command | Commit |
|---|---|---|---|
| Nightly baseline, 2026-09-29 linux-clang-debug | 1 test failed of 87; defects: trading.api 4, refdata.core 3329, ore.core 99, analytics.core 214 |
gh run view --job 109252162647 --log |
main at 3632865c |
ores.trading.api.tests, before |
3 conditional jumps on an uninitialised date | valgrind ... ores.trading.api.tests |
this branch, before the fix |
ores.analytics.core.tests, before |
190 uninitialised reads (111 conditional, 79 value) in std::to_string(double) from the sqlgen INSERT |
same | this branch, before the fix |
ores.ore.core.tests, before |
43 defects: 40 in yyjson writing bond_trs::funding_rate, 3 in the date reflector reading bond_future's dates |
tmp/ores.ore.core.tests.valgrind (CDash page) |
main at 3632865c |
ores.trading.api.tests, after |
valgrind log empty, 426 assertions in 99 test cases pass | valgrind ... ores.trading.api.tests |
this branch |
| The 3329 CI leak stacks against the final suppression file | 0 unmatched, of 203 rules | build/scripts/check_valgrind_suppressions.py |
this branch |
ores.analytics.core.tests, after |
valgrind log empty, 140 assertions in 39 test cases pass | same | this branch |
ores.ore.core.tests, after |
valgrind log empty, 436970 assertions in 576 test cases pass | same | this branch |
ores.refdata.core.tests, after |
valgrind log empty, 4085 assertions in 243 test cases pass | same | this branch |
| Full suite on a recreated database | 89 of 89 pass; a second run on the same database fails ores.iam.core.tests on residue its first run left |
ctest --preset linux-clang-debug-make -j 4 |
this branch |
| Codegen suite | 1001 passed | pytest projects/ores.codegen/tests -q |
this branch |
| Catalogue sweep | no file this branch touched would change | check_component_drift.py --sweep --dry-run |
this branch |
| Census after the fix | 236 non-nullable columns declare no default; 149 are int (synthesised by codegen), 87 are not |
build/scripts/census_generated_defaults.py |
this branch |
6. Notes
- The CDash pages saved under
tmp/are truncated at about a megabyte, so the analytics page listed 64 of the 214 defects and the first fix left 146 behind in a second entity. The complete set came from re-running each binary locally, which is also what proves the fix. - The uninitialised values are all created by a stack frame in the test or the generated generator, and read in a write path: sqlgen stringifies every entity member to build the INSERT, so an entity built by a generated generator that skips a column is read whole.
bond_trs::funding_rateandbond_repo::repo_ratearenullable: truewith a baredouble, so the generated entity wraps them instd::optionaland the generated mapper maps zero tonullopt. The declared default of0.0is that convention, not a value the domain intends.- The full suite passes on a recreated database, and only on a recreated
database:
ores.iam.core.testsis not idempotent. Its first run leaves three seed profiles open, and the second run fails three cases onseed_profiles_code_uniq_idx. That is a pre-existing defect of the tests rather than of this change, and it is filed as The iam seed-profile eventing tests only pass once per database. It also cost this task two verification runs: the residue was misread at first as damage the valgrind runs had done. - The gate refuses the nullable shape rather than synthesising a default, because the value that means "absent" is the model's to state. For the non-nullable shape the same choice is open and is filed as a capture; this task declares the defaults the reported instances needed and records the census, so the decision is taken with the size known.
7. PRs
| PR | Title |
|---|---|
| #2380 | Address the valgrind findings: indeterminate members and library leaks |
8. Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | t1 defaults to 0.0 although the model's own text says the upper bound is 1.0 in every shipped example, so the synthetic interval is degenerate. |
ores.analytics.credit_simulation_matrix_config.org |
Fixed | t1 declares 1.0 now, the component is regenerated, and the analytics valgrind run was repeated against the new value. |
| 2 | The gate's type set includes std::int16_t, std::int32_t, std::uint16_t and std::uint32_t, which the generated mapper's nullable-numeric convention does not cover, so for those the rule demands a default for a shape nothing implements. |
org_loader.py |
Fixed | The set is now exactly the types is_nullable_numeric maps zero to nullopt for, plus the two chrono value types. A nullable bare type outside it is a modelling question this rule cannot answer, and the comment says so. |
| 3 | The two libpq suppression rules carried no allocator anchor and wildcards on both sides, so they would hide any still-reachable allocation in a PQexec stack, including a PGresult the caller never clears. |
custom.supp |
Fixed | Both rules pin the allocator (malloc, calloc) and the libpq entry point, and their comments state the width: they match reachable blocks only, so a block that becomes unreachable is definitely lost and still surfaces. The matcher still covers all 3329 reported stacks. |
| 4 | The task says fifteen model columns where the diff adds twenty-four. | this file | Fixed | Corrected to twenty-four, in the task and in the pull request body. |
| 5 | The measurements cite scripts under build/hotfix_red/, which the branch does not carry, so the census and the suppression coverage are not reproducible from the pull request. |
this file, the census capture | Fixed | Both are now committed: build/scripts/census_generated_defaults.py and build/scripts/check_valgrind_suppressions.py, and every reference points at them. |
| 6 | The gate's comment and its test attribute the analytics reads to credit_simulation_config.seed, a column the truncated CDash page does not name. |
org_loader.py, the test |
Declined | The attribution is from the local run rather than the page: the valgrind log names that test's frame as the origin of the reads, and the census lists seed and paths as its nullable bare ints. The comment names it as an example among others. |
9. Result
The four suites the nightly reported are clean under valgrind: each log
holds no error and no leak record. Twenty-four model columns declare the default
their generated member needs, the nullable shape that caused several of them
is refused by org_loader, and the still-reachable library allocations are
suppressed with the reason each is not a leak.
The full suite passes 89 of 89 on a recreated database, the codegen suite passes 1001 tests, and the sweep over the catalogue reports no file this branch changed as drifted. What is left of the class is filed as a capture with the census: 87 non-nullable columns still generate an indeterminate member.