Conversation
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
ece9042 to
eaab28c
Compare
a1339ec to
38ec2e4
Compare
|
Reviewed slice 2. The direction is already accepted (see #1723); this is the merit read, and the design holds up well. The three-valued status is the right call and it is the thing most people get wrong here. CBM_PROC_TREE_RSS_ERROR = -1,
CBM_PROC_TREE_RSS_EMPTY = 0,
CBM_PROC_TREE_RSS_OK = 1,"No trustworthy measurement" and "the tree has no observable members" are different facts, and collapsing them into "A watchdog that quietly stops watching is worse than no watchdog" is the right principle and you built to it. Using a Windows Job Object is the detail I want to single out. The error paths around it are thorough too: allocation failure, size overflow past Measuring the tree rather than the root process is the correct scope, and your reason for it — "a runaway child hiding behind a small parent is exactly the case that goes unattributed today" — is the sentence that justifies the extra complexity. Ignoring processes that exit mid-enumeration is the right way to handle the inherent race. Exposing Process notesClearance: this shows Rebase when #1723 lands. This diff currently carries #1723's commit; once that merges it collapses to Slice 3 onward is where the configuration surface really grows, so expect those to get a closer look than this one — but this slice is in good shape. |
70b411d to
87da442
Compare
87da442 to
42a3d71
Compare
|
@DeusData rebased One conflict worth naming: CI is restarting. I will wait for you to say when the next slice should move. |
42a3d71 to
81ff2ca
Compare
|
@DeusData rebased The previous matrix's reds were:
Local |
d48afe8 to
3743fe9
Compare
DeusData
left a comment
There was a problem hiding this comment.
Thank you — for the two rebases, for stopping exactly where you said you would, and for two fixes I was about to ask for and found already done.
I read head 42a3d710 in full this morning and wrote three blocking findings. By the time I could post, you had fixed two of them yourself:
- The memory-core ratchet — the process-tree probe now allocates through
mem_coreand the worker resource responses share one release helper.lint / lintis green on3743fe95. - The
Step 5fcollision — this one was ours, not yours, and you fixed it anyway.tests/test_worker_session_scope.shonmainhard-coded a two-key policy literal while the worker demands the exact key set, so any slice that adds a policy key turnsmainred after a textually clean merge. That is precisely what #1723 did to us at the weekend, and this slice (2 → 4 keys) would have done it again. Your last commit routes the fixture throughcbm_test_index_worker_policy_json, which is the durable fix: every later slice only has to touch the helper. For the record, I think the worker's exact-key-count check should stay as strict as it is — a supervisor that silently drops a key would silently disable a limit.
That leaves one blocker, and it is a one-word change with a long story behind it.
Blocking: macOS samples ri_resident_size; it must be ri_phys_footprint
src/foundation/subprocess.c:577 sums usage.ri_resident_size over the process group. On macOS, resident size keeps counting every page mimalloc has already handed back (MADV_FREE_REUSABLE) until the kernel gets round to reclaiming it. We measured this the hard way — it is written down in src/foundation/mem.h on main: on the Linux-kernel corpus the worker sat at 17.4 GB resident with 5.5 GB actually charged. With this slice as it stands, a user who sets index_max_rss_mb to 8 GB on a Mac can get a healthy run killed while the OS is charging it a fraction of that, with an error message naming a number the OS itself does not hold against them.
rusage_info_v2 already carries ri_phys_footprint, so the probe needs no new call — just the other field. That is also the number cbm_mem_charged() enforces the cooperative budget against, so the two limits would finally agree on what "memory" means. Linux and Windows are fine as they are. If you can, make the fake-RSS seam's test name say which quantity it represents, so nobody "fixes" it back.
Not blocking — but please read
- The daemon's default start path has no test.
application_worker_start_default(src/daemon/application.c, the policy parse pluscbm_index_worker_start_with_policy) is only reachable through the real.start; every case intests/test_daemon_application.cinstallsapp_fake_worker_start. So "a malformed internal policy makes the daemon refuse the job" is currently asserted by nothing. One case that feeds a bad_cbm_index_policythrough the default start and expects-1plusdaemon.index.policywould close it. index_max_duration_secondsis per attempt, not per request.started_msis set when a worker handle starts, and recovery starts a new handle per attempt — so a request that crashes and recovers can run for several times the configured duration. Either is defensible; what is not is the ambiguity. Please pick one, name it in the option's help text, and pin it with a fake-clock test across a recovery. My preference is per request: a user who writes "30 minutes" means the thing they are waiting for.- A duration limit under 15 minutes pre-empts hang quarantine.
DEFAULT_QUIET_TIMEOUT_MSis 900 000; a shorter duration limit kills a hung worker first, and because a limit kill (correctly) never enters the quarantine loop, as far as I can see the file that hangs is never identified, so the next run would hang on it again. Worth one sentence in the docs at minimum; better, log the in-flight file marker on a duration kill. - Windows probe, small.
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, …)matches the documented requirement forGetProcessMemoryInfo, so this is only a suggestion:PROCESS_QUERY_LIMITED_INFORMATIONis the documented lesser alternative toPROCESS_QUERY_INFORMATIONand succeeds on more kinds of process, which matters because a member you cannot open counts as a probe failure when it is the root. The Job Object membership list already closes most of the pid-reuse window between listing and opening; a comment saying so would help the next reader.
What I checked and found right
The limit kill reuses the existing graceful-then-forced tree shutdown rather than inventing one. A limit kill never enters quarantine. The daemon path and the direct path behave the same, and the client gets a structured error naming the limit, the observed value and the configured value. Sampling piggy-backs on the existing poll loops — no new sleep anywhere — and graph content is never affected by a limit, which is the line we care most about. The tests use clock and RSS seams instead of real time. The coexistence with cbm_index_worker_job_memory_limit is exactly as you describe it: OS cap at spawn, parent-side sampling on top.
One thing I have not done yet: build and run this head locally. External changes that spawn processes or edit executed scripts go through a per-head clearance step on our side first, and this head has not been through it. That is our queue, not a request to you.
When should the next slice move?
After this one merges — not before. Concretely: the ri_phys_footprint change, then a decision on point 2 (it changes an option's meaning, so it is cheaper to settle here than in #1725). Points 1, 3 and 4 can ride in this PR or a follow-up, your choice. Please keep #1725–#1728 untouched until then; each of them will need exactly one helper line for its new policy key now, instead of a main breakage — which is thanks to you.
A design question that is ours to decide, not yours to solve: whether index_max_rss_mb should also clamp the worker's cooperative memory budget, so a run stays under the cap instead of being killed at it. I will settle it and report back here.
Thank you for the care in this series. It shows in every error path.
|
@DeusData addressed on Blocker — macOS Point 2 — duration is per request. Point 3. Docs now state that a duration shorter than the 15-minute quiet timeout kills a hung worker before hang quarantine can name the in-flight file. Point 4. Comment on the Windows Point 1 (daemon default-start malformed-policy test) left for a follow-up. Local |
|
That was fast, and it is right. I read
The design question I said I would settle: yes — What is left is on our side: the full matrix on Thank you, again, for how you work on this series. |
5033f97 to
879aa5f
Compare
|
@DeusData current head |
|
Thanks for the precise diagnosis — that is exactly what is happening. I am not going to cancel run 2592, though: cancelling workflow runs is one of the few actions I keep off the table from this side, even for a superseded run, so nothing here is ever "cancelled by accident". Its one remaining job, |
879aa5f to
89030aa
Compare
|
Same request as I have just made on another PR, and for the same reason: please do not rebase again until you hear from me. Each rebase onto the newest The content of |
89030aa to
b47ba84
Compare
Discovery limits bound what indexing accepts, not what it then costs. A repository well inside those bounds can still exhaust the host through parser memory, or simply never finish, and a supervised worker that hangs leaves the parent waiting with nothing to report. Add index_max_rss_mb and index_max_duration_seconds, enforced by the parent against the worker process tree rather than the worker process alone, so a runaway child cannot hide behind a small parent. Resident memory is sampled through the platform interface on macOS, Linux and Windows. Crossing a limit terminates the tree and yields one trusted, structured terminal result that attributes the failure to the resource that caused it. Both limits default to off. A measurement that cannot be taken fails the attempt instead of passing it: a watchdog that quietly stops watching is worse than no watchdog at all. The shell fixture that stands in for the supervisor names the two new keys. The worker accepts only a policy that spells out every key it knows, which is what keeps a stale supervisor from starting a worker it cannot bound. Signed-off-by: 刘冲 <mail@liuchong.dev>
…ture PR 2257 still named only the two discovery keys in the worker-scope script. The worker now fail-closes unless every known policy key is present, so the fixture must stand in for the supervisor with cbm_test_index_worker_policy_json. Signed-off-by: 刘冲 <mail@liuchong.dev>
Watchdogs now sample the OS-charged footprint instead of resident size on Darwin, and index_max_duration_seconds covers the whole request so a crash/hang respawn cannot reset the clock. Signed-off-by: 刘冲 <mail@liuchong.dev>
b47ba84 to
3889b47
Compare
|
Rebased onto current |
…udget test-msan failed on daemon_bootstrap_fails_fast_when_daemon_dies_at_publication because the forked host did not publish listen_failed/ENOSPC inside 30s. index_supervisor was already green. Widen only the sanitized client budget, matching the frontend announce backstop. Signed-off-by: 刘冲 <mail@liuchong.dev>
|
Follow-up on |
|
@DeusData If you have a moment for the local clearance build, that would keep this slice from picking up more |
Related to #1347.
Problem
Discovery limits bound what indexing accepts, not what it then costs. A repository well inside those bounds can still exhaust the host through parser memory, or simply never finish. A supervised worker that hangs leaves the parent waiting with nothing to report and no way to attribute the stall.
What this changes
Two opt-in limits, enforced by the parent against the worker process tree:
index_max_rss_mbindex_max_duration_secondsBoth default to
off.Measuring the tree rather than the worker process alone matters: indexing spawns children, and a runaway child hiding behind a small parent is exactly the case that goes unattributed today. Resident memory is sampled through the platform interface on macOS, Linux and Windows.
Crossing a limit terminates the process tree and produces one trusted, structured terminal result attributing the failure to the resource that caused it, so the caller learns which limit ended the attempt rather than seeing a generic worker failure.
A measurement that cannot be taken fails the attempt instead of passing it. A watchdog that quietly stops watching is worse than no watchdog, so the probe is fail-closed and says so in the result.
Testing
make -f Makefile.cbm testandmake -f Makefile.cbm lint-cion macOS. New coverage: process-tree RSS measurement, watchdog termination on each limit, probe-failure fail-closed behaviour, terminal-result attribution, and policy validation for the two new keys.Stack