Repository navigation
feat(BA-8635): choose how the AppProxy worker receives ingresses - #15890
Merged
Merged
Conversation
[proxy-worker.ingress] mode picks circuit (default, the existing circuit event path), api (the coordinator sends ingresses to the worker) or redis. The worker announces its authority and mode as service discovery labels. /v2/ingress on the API port is wired to the ingress state and writes only in the api mode; upsert and replace return the revisions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks done
…essState IngressState holds the single in-memory writer and the configured mode. The /v2/ingress handler writes only in the api mode and the circuit event path only in the circuit mode, instead of splitting the same writer into api_writer and circuit_writer fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ingress mode is api or redis, as BA-8178 settles, and defaults to redis with the in-memory and redis layers. Circuit events are written into the ingress state in either mode, so the existing circuit path keeps working while pushes or Redis are added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f the mode The mode is configuration, not runtime state, so IngressState keeps the IngressConfig it was built from and the /v2/ingress handler reads the mode from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setting chooses how ingresses are delivered to the worker, so the enum becomes IngressDeliveryMode, the config key delivery and the service discovery label ingress-delivery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rom the state IngressState goes back to the reader and writer it holds on main. The /v2/ingress handler takes the ingress config through IngressConfigCtx, next to IngressStateCtx, and reads the delivery from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Generated local configuration fails validation, and revision reporting can differ from the worker’s effective state.
2 open issues
What changed in this PR
Adds configurable ingress delivery for AppProxy workers, preparing them for coordinator pushes in #15833.
Changes:
- Adds API/Redis delivery modes and required-layer validation.
- Publishes delivery mode and authority through service discovery.
- Connects ingress endpoints to worker state and returns applied revisions.
| File | Description |
|---|---|
| tests/unit/appproxy/worker/test_config.py | Tests delivery modes and layer validation. |
| tests/unit/appproxy/worker/ingress/test_factory.py | Updates factory tests with delivery modes. |
| tests/unit/appproxy/worker/api/test_ingress.py | Tests revision responses and write restrictions. |
| src/ai/backend/appproxy/worker/server.py | Adds worker discovery labels. |
| src/ai/backend/appproxy/worker/dto/context.py | Provides request-scoped state and configuration. |
| src/ai/backend/appproxy/worker/config.py | Defines delivery settings and validation. |
| src/ai/backend/appproxy/worker/api/ingress.py | Enables state-backed ingress operations. |
| src/ai/backend/appproxy/common/types.py | Adds the delivery-mode enum. |
| src/ai/backend/appproxy/common/defs.py | Defines discovery label constants. |
| configs/app-proxy-worker/sample.toml | Documents the delivery setting. |
| changes/15890.feature.md | Records the feature. |
Review effort: Balanced
| async def revisions(self) -> APIResponse: | ||
| ingresses = await self._require_state().reader.get_all() | ||
| async def revisions(self, state_ctx: IngressStateCtx) -> APIResponse: | ||
| ingresses = await state_ctx.ingress_state.reader.get_all() |
| "manager writes to Redis. Circuit events are written in either delivery mode." | ||
| ), | ||
| added_version=NEXT_RELEASE_VERSION, | ||
| example=ConfigExample(local="redis", prod="api"), |
HyeockJinKim
approved these changes
Oct 20, 2026
Merged
Merged
2 tasks done
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.

📚 Stacked PRs
Part of the AppProxy worker Ingress stack (epic BA-8433). Merge in order (bottom-up):
feat(BA-8635): choose how the AppProxy worker receives ingresses ← you are herefeat(BA-8443): push ingresses from the coordinator to api-delivery workersResolves #15891 (BA-8635)
Summary
[proxy-worker.ingress] delivery_mode(IngressDeliveryMode) chooses how ingresses are delivered to the worker, as BA-8178 settles: each worker picks its method in its server config and reports it to the coordinator when it starts.api: the coordinator sends ingresses to the worker on its API port.redis(default): the worker reads what the manager writes to Redis. The defaultlayersbecomes["in-memory", "redis"]to match.apirequires the in-memory layer andredisthe redis layer.authorityandingress-delivery-modelabels to its existing service discovery entry, which the coordinator reads in feat(BA-8443): push ingresses from the coordinator to api-delivery workers #15833./v2/ingressstays on the existing API port and is now wired to the ingress state. The handler takesIngressStateCtxandIngressConfigCtx,MiddlewareParams that read the ingress state and the ingress config per request, as the storage proxy'sStorageRootCtxdoes. It writes only with theapidelivery and answers 501 otherwise.upsertandreplacereturnIngressRevisionsResponse;removestays 204.Assumptions
api-delivery worker with the redis layer then falls back to a complete set:get_allreads the last layer, and a key removed by a push stays removed in Redis.Constraints from sharing the in-memory state with circuits
/v2/ingress/replacereplaces the whole state, including the ingresses from circuits. The coordinator in feat(BA-8443): push ingresses from the coordinator to api-delivery workers #15833 calls onlyupsertandremove.Test plan
pants check/pants testontests/unit/appproxy::andsrc/ai/backend/appproxy::tests/unit/appproxy/worker/api/test_ingress.py— writes with theapidelivery, 501 otherwisetests/unit/appproxy/worker/test_config.py— a delivery mode without the layer it needs is rejected🤖 Generated with Claude Code