Repository navigation
Say what the collector refused, fit requests to its limit, register a lost run again - #7
Open
MaxGhenis wants to merge 3 commits into
Conversation
MaxGhenis
force-pushed
the
fix/telemetry-settled-rejection-local-only
branch
from
October 10, 2026 01:54
7ca9e22 to
9d4ee06
Compare
… lost run again Three follow-ups to the settled-rejection rule in the telemetry service's collector delivery, made in step with Microcosm's copy of this code. A refused registration printed the "credential was not accepted as a PolicyEngine organization member" warning, because `_make_local_only` defaulted to it. That was false for a 409 (the owner registering with other metadata), a 422 and any other settled 4xx, and for a 403 on a registration or on events, where the collector had accepted the login and refused it the run. A rejected registration now names its HTTP status, a 403 names the Hugging Face user, and the message no longer has a default. A batch was bounded by count only. A full batch of the largest events (non-ASCII messages, details at their limit) is over the collector's 1 MiB request limit, which it answers with 413 for the whole request, and that made the run local-only. A batch is now the longest run of leading events whose request body fits, measured with the serialization `_http_post` sends. An event too large to send alone is sent alone, for the collector to refuse. A 404 for a run's events means the collector does not have a run this service registered. The service now registers the run again and sends the same batch on the next pass; a second 404 with no batch acknowledged in between makes the run local-only, as before. Stacked on #6 (0038e99). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…livery The 404 case wrote a joined-string key into CollectorDelivery._registered, which stops existing once this package keys runs by the pair, as microcosm#1177 now does. It now answers the first registration as accepted without passing it on, so the pinned collector really does not have the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The collector fixture's Alembic migrations log to stderr, so an empty stderr failed two contract tests whose behaviour had passed against the real collector. They now check that no local-only warning was printed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
force-pushed
the
fix/telemetry-collector-followups
branch
from
October 10, 2026 01:56
3b8a4d7 to
5df012b
Compare
This was referenced Oct 19, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three follow-ups from #6 (the settled-rejection port) in
CollectorDelivery, the telemetry service's collector delivery. All three are in Microcosm's copy of this code too; PolicyEngine/microcosm#1179 makes the same change there._registercalled_make_local_onlywithout amessage, so the service's own run got the defaultREJECTED_CREDENTIAL_MESSAGE: "the ambient Hugging Face credential was not accepted as a PolicyEngine organization member". By then the token exchange had accepted that credential as a member. The warning was false for a 409 (the run's owner registering it with other metadata), a 422 (a registration outside the collector's schema) and any other settled 4xx. It was also loose for a 403 on registration or on events, where the collector accepted the login and refused it this run.EventSpool.batchreturns up to 100 events. The collector answers a request body over 1 MiB with 413 before reading it, and since Make a settled collector rejection local-only instead of retrying it forever #6 a 413 makes the run local-only. At the service's own bounds (message cut to 500 characters, details to 8,192 bytes, identifiers at the collector's 192-character maximum), a full batch is 964,904 bytes with ASCII messages, 1,214,904 with 2-byte messages and 1,514,904 with emoji, becausejson.dumpsescapes each non-ASCII character to 6 or 12 bytes.collector_rejected_events).Change
All in
src/microcosm_emitter/telemetry/service/collector.pyandconstants.py._make_local_only'smessageno longer has a default; every caller names one.REJECTED_REGISTRATION_MESSAGE, "the collector rejected its registration (HTTP {status}), so it will not be retried".REJECTED_RUN_USER_MESSAGE, "the collector refused the ambient Hugging Face user for it (HTTP 403). A run belongs to the user who registered it first."NO_CREDENTIAL_MESSAGEonce per service and nothing per run, now passed asmessage=None.MAX_EVENTS_REQUEST_BYTES = 1_048_576, the collector's own figure, with no margin: the contract test below has the collector accept a body of exactly that size._events_fitting_requestkeeps the longest run of leading events whose request body fits. It measures with_request_body, the same function_http_postnow sends, so the bytes measured are the bytes sent. Events go in sequence order, one request after another, with none skipped._registering_again). The next pass registers the run and sends the same batch, with no retry delay. A second 404 with no batch of the run acknowledged in between is the collector's settled answer, and the run goes local-only as before. An acknowledged batch clears the mark.README and a
changelog.dfragment describe all three.Is registering again safe?
The follow-up asked for this to be worked out before anything was decided. The answer does not turn on a judgment call, so it is implemented here rather than queued:
register_run: insert on conflict do nothing, then compare owners). So the re-registered run belongs to the login that registers it again. For a build's own run that is the build's login. For a run a service adopted, it is the adopter's login, which the collector had already accepted for that run: an adopter reaches the events request only after its own registration of the run was answered 201. If another user registered the lost run id first, registering again gets 403 and is handled as any registration 403 is: the build's own run goes local-only with the user warning, and an adopted run is passed over for that login. This is the collector's existing first-registrant rule, applied to a run it lost; nothing new decides ownership.How the collector answers (read for this change)
From PolicyEngine/calibration-diagnostics
telemetry-service/telemetry_collector/at80a2a4ac, the commit this repo's CI pins.app.py, which holds the body limit and every status below, is unchanged from there to that repo'smain(280459c).Content-Lengthover 1,048,576, or a streamed body that grows past itRequestBodyLimitMiddleware, before routing or authentication. A body of exactly 1,048,576 bytes passes.Invariants
Each is checked by the tests named. The scope is stated per item and nothing is claimed beyond it.
test_every_refusal_prints_one_warning_about_the_request_refused; exact texts for 409, 422 and 403:test_a_rejected_registration_warning_says_what_the_collector_refused; the same three against the real collector:test_a_refused_registration_warns_about_the_registration_not_the_credential(CIcollectorjob). Another build's run is never warned about, and a missing credential is reported once per service, not per run (existing tests, now also killing the mutants listed below).test_every_request_fits_and_events_arrive_in_order.test_the_events_sent_are_the_longest_leading_run_that_fits, 400 examples plus the exact 18- and 17-byte edges).test_two_events_of_exactly_the_limit_go_together_and_a_byte_more_apart)._http_postsends exactly_request_body's bytes and says so inContent-Length(test_the_bytes_measured_are_the_bytes_sent, a loopback server).collectorjob).test_an_event_too_large_to_send_alone_is_left_to_the_collector). The limit is the collector's to enforce: were it raised there, such an event would be delivered like any other (test_an_event_over_the_limit_that_the_collector_takes_does_not_stop_the_run).test_a_lost_run_costs_a_bounded_number_of_registrations, 200 examples plus two explicit). No other answer to events (every status from 200 to 599 but 202 and 404) registers a run again (test_no_other_answer_to_events_registers_the_run_again, exhaustive).test_registering_again_is_answered_like_any_registration,test_registering_again_waits_out_a_transient_answer). The first 404 sets no retry delay: the next pass, with no time passed, registers and delivers (test_a_run_the_collector_lost_is_registered_again_and_delivered).forget) and can be set never to find one run's events. With that, after every step: another producer's run goes local-only only on an answer about the run itself, which now includes a second 404 with no batch acknowledged in between, never because of this service's login; and once every service has gone, each pending run is delivered in full by a login that owns it, unless the collector refuses the run itself (now including never finding it).test_no_service_decides_another_producers_run_from_its_own_credential, with an explicit example where Alice's run is lost and Bob's build of the same run id registers it first. Deterministic cases:test_an_adopted_orphan_the_collector_loses_is_registered_again,test_a_lost_run_another_user_registers_first_is_refused_its_first_owner, and anunfindable_runcase in the orphan-refusal tests.Both packages. Microcosm's copy of this code gets the same change in PolicyEngine/microcosm#1179. With #6 now on #5's
093efb1, which mirrors the rest of microcosm#1177, the twocollector.pyfiles are identical apart from the package path. Checked with:diff <(sed 's/microcosm\.build\.telemetry_emitter_service/microcosm_emitter.telemetry.service/g' microcosm/packages/microcosm-build/src/microcosm/build/telemetry_emitter_service/collector.py) microcosm-emitter/src/microcosm_emitter/telemetry/service/collector.pywhich prints nothing at #1179's
b3710aa04and this PR's5df012b.constants.pydiffers only by #5's comment on the lease-file layout. One script applied each change to its base (615d84f89there,9d4ee06here) and reproduces each head's two files byte for byte.tests/telemetry/test_collector_delivery.pyis Microcosm'stest_telemetry_collector_delivery.pywith its import block swapped, and otherwise identical.Tests
tests/telemetry/test_collector_delivery.py(new): the warning, request-size and lost-run tests named above, against a fake collector that answers as the hosted one does, including its body limit and repeatable registration.tests/telemetry/test_foreign_runs.py: the fake collector can lose a run (forget) or never find one (unfindable_run_ids); the invariant check accepts a second 404 as a refusal about the run, and only that; the property test draws both; two deterministic tests and an explicit example as above.tests/telemetry/test_delivery.py: 404 leaves the settled-rejection table (it is now registered again first), and the exhaustive 4xx table expects one re-registration for events 404, the status named in every warning, and no "organization member" in any.tests/test_collector_contract.py(CIcollectorjob only): the 422 case kept, the 404 case replaced by one where the real collector accepts the second registration and the batch; refused registrations (409, 422, 403) and their warnings; 100 of the largest events in two accepted requests; a body of exactly the limit accepted. The lost run is simulated by answering the first registration without passing it on, so the test does not depend on how the delivery keys runs.Verification
Local results, the mutation pass and CI are being added here as they finish. Until this section says otherwise, treat the PR as unverified.
Limits and follow-ups
collectorjob, which needs the pinned collector and PostgreSQL; locally they were collected and skipped.main, merge Make a settled collector rejection local-only instead of retrying it forever #6, then retarget this tomainand merge. Don't delete a base branch through the API or--delete-branchbefore the PRs on it are retargeted: that closes them.🤖 Generated with Claude Code