Repository navigation
fix(examples): keep the default Course loadable under browser override spam - #235
Merged
Merged
Conversation
…e spam Default and browser-override loads shared one load-start budget, one load semaphore with its queue, the outbound fetch cap and the source LRU. One client sending 20 failing overrides a minute made every fresh default load fail with RATE_LIMITED (new Tutor sessions without an example failed for all learners); slow overrides could fill the queue and successful ones could evict the default source. - Separate load slots, queues and load-start budgets per priority, each of the configured size; a failing override only spends override budget. - Override loads may hold at most three quarters of the outbound fetch cap; the priority reaches the fetches through AsyncLocalStorage. - The source cache never evicts the configured default source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Root cause (verified)
Loads of the operator's default Course and browser overrides shared every bound of
ExamplesLoadControllerandExamplesCache. Any client can trigger overrides with arbitrary repositories and refs, also cross-site via GET.UNOSIM_EXAMPLES_GLOBAL_LOAD_STARTS_PER_MINUTE, 20/min):RATE_LIMITED;resolveTutorContentneeds a fresh default for Tutor requests without a loaded example, so new Tutor sessions failed with 400 for all learners.LOAD_CAPACITY_EXCEEDED.UNOSIM_EXAMPLES_MAX_SOURCES): successful overrides evict the default source.The GitHub quota reserve from #227 only covered the GitHub REST quota, not these limiters.
Proof on the old code: all four cases in
tests/server/examples/default-source-protection.test.tswere red:RATE_LIMITEDafter sequential override spam;LOAD_CAPACITY_EXCEEDEDwith occupied override slots;Fix
ExamplesLoadController:defaultandoverride, each of the configured size, so the semantics per pool are unchanged;runLoad(operation, signal, priority)keepsoverrideas its default;SourceProviderpassescontext.sourcePriority ?? "default", like the GitHub budget.UNOSIM_EXAMPLES_MAX_OUTBOUND_FETCHES(16 → 12); the rest stays free for the default. The priority reaches the fetches throughAsyncLocalStorage, without API changes in loader, resolver or fetcher. Fetches outside a load are not limited by the share.ExamplesCache:protectedSourceKeys.ExamplesRepositoryprotects the configured default source; LRU eviction skips it.SECURITY.md.Tests
tests/server/examples/default-source-protection.test.ts, with realSourceProvider,ExamplesLoadControllerandExamplesCache:RATE_LIMITED), then a fresh default load succeeds.LOAD_CAPACITY_EXCEEDED); the default still loads.Existing tests are unchanged, including the controller test that uses the full outbound cap outside a load.
npm run check,npm run test:unit,npm run check:docs, Knip,git diff --check, the full./run-tests.sh(also after rebasing onto #234) and the pre-push gate are green.🤖 Generated with Claude Code