Per-project MaxCheckReads override (#63)#65
Conversation
Add an optional per-project override of the global GATEWAY_MAX_CHECK_READS read budget, mirroring the data_region per-project envelope. The override rides projects.config_json (no schema change), is resolved per request, and threads into the engine's *WithModel entry points as a per-request budget; 0/unset falls back to the fleet default. Validated against the same floor as the global (config.MinMaxCheckReads).
Add engine, service, conformance, and AdminService+AuthzService e2e tests for the per-project max_check_reads override: round-trip on GetProject/ListProjects, the override taking effect (a high override completing a read-heavy Check a stingy global trips, and a low override tripping where the global allows), per-project isolation, sub-floor validation, clear-to-default, and config_json round-trip across both drivers.
Saturate the int32 proto conversion via the existing i32 helper, and document in README + authorization-model.md that max_check_reads overrides the global GATEWAY_MAX_CHECK_READS per project (positive replaces the base budget, 0/unset falls back to the global, clear_max_check_reads resets, same >=100 floor).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 179d0aa0d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return false, err | ||
| } | ||
| return e.CheckWithModel(ctx, m, projectID, tenantID, namespace, objectID, relation, userID, cc) | ||
| return e.CheckWithModel(ctx, m, projectID, tenantID, namespace, objectID, relation, userID, cc, 0) |
There was a problem hiding this comment.
Apply project budget to product-surface checks
When a project overrides the workspace namespace with a wide rewrite and sets a higher MaxCheckReads, product-surface authorization still ignores that override because Engine.Check always passes 0, so callers using this path fall back to the global budget. In this repo, service.allowed still calls s.engine.Check for workspace role checks (internal/service/service.go:166), so operations like GetWorkspace/AddMember can return ResourceExhausted under a low global budget even though the same project's data-plane Check succeeds with the per-project override.
Useful? React with 👍 / 👎.
Closes #63 — an optional per-project read-budget override so one wide-model tenant can carry a higher (or lower) Check budget without changing the global GATEWAY_MAX_CHECK_READS ceiling for everyone. Mirrors the data_region pattern end-to-end.
Project.max_check_reads,CreateProjectRequest.max_check_reads,UpdateProjectRequest.max_check_reads+clear_max_check_reads. 0 = unset → global default; positive = override; clear = reset to default.config_jsonenvelope (both drivers, no migration) → resolverresolved.maxCheckReads→ threaded per-request into the engine*WithModelcalls viaeffectiveMaxReads(override)(>0 wins, else global). fanOutBudget/expandBudget scale off the effective base identically; BatchCheck inherits per item.budget_changed.Additive proto ·
go test -race ./...✓ (real Postgres) · lint 0 ✓.Closes #63