You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -335,7 +335,7 @@ Full documentation at [bedrock-python.github.io/grpc-client-kit](https://bedrock
335
335
|[Load balancing](https://bedrock-python.github.io/grpc-client-kit/guide/load-balancing/)| round-robin, random and weighted selection with health filtering |
336
336
|[Health](https://bedrock-python.github.io/grpc-client-kit/guide/health/)|`grpc.health.v1` probing, per-target backoff and cold starts |
337
337
|[Observability](https://bedrock-python.github.io/grpc-client-kit/guide/observability/)| log records, CLIENT spans and the metrics an RPC emits |
338
-
|[Dependency injection](https://bedrock-python.github.io/grpc-client-kit/guide/dependency-injection/)| the Dishka providers, what they own, and one component per upstream |
338
+
|[Dependency injection](https://bedrock-python.github.io/grpc-client-kit/guide/dependency-injection/)| the Dishka providers, what they own, one component per upstream, one pool shared by several|
339
339
|[Advanced](https://bedrock-python.github.io/grpc-client-kit/guide/advanced/)| interceptors that re-issue calls, target validation and ownership |
340
340
|[API reference](https://bedrock-python.github.io/grpc-client-kit/reference/)| generated from the source |
341
341
|[For AI agents](https://bedrock-python.github.io/grpc-client-kit/agents/)| the whole API surface, the rules that break code when broken and a map of the rest, on one page to hand to a coding assistant |
Copy file name to clipboardExpand all lines: docs/agents.md
+12-5Lines changed: 12 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -325,15 +325,22 @@ The label names, their order and the buckets are grpc-server-kit's `GrpcServerMe
325
325
|---|---|---|
326
326
|`GrpcClientSettingsProvider(settings, *, component=None)`|`GrpcClientSettingsProtocol`| holds the settings object; `BaseGrpcClientSettings` or anything structural |
327
327
|`PrometheusGrpcClientMetricsProvider(*, prefix=None, component=None)`|`GrpcClientMetricsProtocol \| None`|`get_grpc_client_metrics(prefix)` when `settings.metrics_enabled`, else `None`; `None` with a warning without `[metrics]`|
328
-
|`AsyncGrpcClientProvider(*, shutdown_grace=5.0, ready_timeout=10.0, component=None)`|`GrpcClientFactory`| APP scope, async generator: `async with GrpcClientFactory(settings, metrics=...)` entered on first resolution, left on `container.close()`|
329
-
|`grpc_client_providers(settings, *, component=None, metrics_prefix=None, shutdown_grace=5.0, ready_timeout=10.0)`|`tuple[Provider, ...]`| the three above, for `make_async_container(*grpc_client_providers(settings))`|
328
+
|`AsyncGrpcClientProvider(*, shutdown_grace=5.0, ready_timeout=10.0, shared_pool=False, component=None)`|`GrpcClientFactory`| APP scope, async generator: `async with GrpcClientFactory(settings, metrics=...)` entered on first resolution, left on `container.close()`; `shared_pool=True` borrows the default component's `ChannelPool` (`FromComponent("")`) instead of building one |
329
+
|`AsyncChannelPoolProvider(settings=None, *, metrics=None, shutdown_grace=5.0, component=None)`|`ChannelPool`| APP scope, async generator: the pool shared upstreams borrow, sized by a `ChannelPoolSettingsProtocol`, `close_all(grace=shutdown_grace)` on `container.close()`; `metrics` is handed in, not requested |
330
+
|`grpc_client_providers(settings, *, component=None, metrics_prefix=None, shutdown_grace=5.0, ready_timeout=10.0, shared_pool=False)`|`tuple[Provider, ...]`| the first three above, for `make_async_container(*grpc_client_providers(settings))`|
330
331
331
332
The factory provider requests the settings and the registry through their protocols, so a
332
333
container with `AsyncGrpcClientProvider` and no provider of `GrpcClientMetricsProtocol | None`
333
334
is refused when it is built. Several upstreams are several Dishka components: one bundle per
334
335
upstream with `component="users"`, resolved with `container.get(GrpcClientFactory,
335
336
component="users")` or `Annotated[GrpcClientFactory, FromComponent("users")]`; each component
336
-
resolves its own settings, and all of them hand out the one cached collector.
337
+
resolves its own settings, and all of them hand out the one cached collector. One pool for all
338
+
of them is `AsyncChannelPoolProvider(settings.grpc_pool, metrics=get_grpc_client_metrics())` in
339
+
the default component and `shared_pool=True` on every bundle: the factories borrow it, the
340
+
container drains it once, after them, with the pool provider's grace (the bundle's
341
+
`shutdown_grace` then applies to nothing), and each upstream keeps its own settings and its own
342
+
health checker. A `shared_pool=True` bundle with no `ChannelPool` in the default component is
`ChannelWrapper` and `chain_token` in `grpc_client_kit.channel`, and `MethodCircuitState` in
366
373
`grpc_client_kit.interceptors.circuit_breaker`, are internals left out of the public surface on
@@ -633,7 +640,7 @@ Fetch a page when the task is the one named beside it.
633
640
|[Native gRPC or the kit?](guide/native-vs-kit.md)| deciding which layer owns LB, retries, idling and health |
634
641
|[Health checking](guide/health.md)| the probe loop, cold starts, backoff, status callbacks |
635
642
|[Observability](guide/observability.md)| what a log record, a metric sample and a span actually contain, and the shipped Prometheus collector |
636
-
|[Dependency injection](guide/dependency-injection.md)| the Dishka providers, what resolving and closing does, one component per upstream |
643
+
|[Dependency injection](guide/dependency-injection.md)| the Dishka providers, what resolving and closing does, one component per upstream, one pool shared by several|
shutdown_grace=5.0, ready_timeout=10.0)` returns the three providers below,
31
-
which can also be registered one by one:
30
+
shutdown_grace=5.0, ready_timeout=10.0, shared_pool=False)` returns the first
31
+
three providers below, which can also be registered one by one; the fourth is
32
+
registered on its own, by the containers whose upstreams
33
+
[share one pool](#one-pool-for-several-upstreams):
32
34
33
35
| Provider | Provides | Notes |
34
36
| :--- | :--- | :--- |
35
37
|`GrpcClientSettingsProvider(settings)`|`GrpcClientSettingsProtocol`| Holds the settings object: [`BaseGrpcClientSettings`](configuration.md#from-the-environment) or anything structural |
36
38
|`PrometheusGrpcClientMetricsProvider(prefix=None)`|`GrpcClientMetricsProtocol \| None`| The [shipped collector](observability.md#the-shipped-collector) when `settings.metrics_enabled`, `None` otherwise |
37
-
|`AsyncGrpcClientProvider(shutdown_grace=5.0, ready_timeout=10.0)`|`GrpcClientFactory`| APP scope, from an async generator: entered on first resolution, left when the container closes |
39
+
|`AsyncGrpcClientProvider(shutdown_grace=5.0, ready_timeout=10.0, shared_pool=False)`|`GrpcClientFactory`| APP scope, from an async generator: entered on first resolution, left when the container closes; `shared_pool=True` borrows the default component's `ChannelPool` instead of building one |
40
+
|`AsyncChannelPoolProvider(settings=None, metrics=None, shutdown_grace=5.0)`|`ChannelPool`| APP scope, from an async generator: the pool several upstreams share, sized by a `ChannelPoolSettingsProtocol`, drained with `shutdown_grace` when the container closes |
38
41
39
42
The factory provider *requests* the settings and the registry through their
40
43
protocols rather than taking them in its constructor, so a settings object
@@ -101,6 +104,62 @@ collector is the exception by design — it is cached per prefix, so both
101
104
components hand out the same instance, and the two upstreams' series are told
102
105
apart by the `service` label rather than by separate metrics.
103
106
107
+
### One pool for several upstreams
108
+
109
+
Registered that way, each component's factory builds a pool of its own, as it
110
+
does outside a container. Upstreams whose retry and timeout policy differ but
111
+
whose channels should sit in one pool — one set of
112
+
[pool statistics](observability.md#pool-statistics), one section configuring
113
+
it — register the pool once, in the default component, and tell each bundle to
114
+
borrow it:
115
+
116
+
```python
117
+
from grpc_client_kit.dishka import AsyncChannelPoolProvider, grpc_client_providers
118
+
from grpc_client_kit.metrics import get_grpc_client_metrics
0 commit comments