@@ -224,24 +224,114 @@ def __repr__(self) -> str:
224224
225225class RecordsConcurrencyOperation (Enum ):
226226 WRITE = "write"
227+ QUERY_MUTABLE = "query_mutable"
228+ QUERY_IMMUTABLE = "query_immutable"
229+ RETRIEVE_MUTABLE = "retrieve_mutable"
230+ RETRIEVE_IMMUTABLE = "retrieve_immutable"
231+ AGGREGATE_MUTABLE = "aggregate_mutable"
232+ AGGREGATE_IMMUTABLE = "aggregate_immutable"
227233
228234
229235class RecordsGlobalConcurrencyConfig (ConcurrencyConfig ):
230236 """
231- Global concurrency settings for the Records API. Named "global" to distinguish from
232- future per-endpoint rate limits that may be added later.
237+ Global concurrency settings for the Records API.
238+
239+ The Records API has separate rate-limit budgets for reads and writes, and read budgets
240+ differ between mutable and immutable streams. Read budgets are hierarchical: the
241+ retrieve and aggregate endpoints each have a dedicated budget that is checked *before*
242+ the shared query budget (both must pass).
243+
244+ - **write**: Shared across ingest, upsert, and delete (same limit for both stream types).
245+ - **query_mutable / query_immutable**: Shared read budget consumed by all query endpoints
246+ (list/filter, sync, retrieve, aggregate).
247+ - **retrieve_mutable / retrieve_immutable**: Dedicated budget for retrieve, checked
248+ *in addition to* the shared query budget.
249+ - **aggregate_mutable / aggregate_immutable**: Dedicated budget for aggregate, checked
250+ *in addition to* the shared query budget.
233251
234252 Args:
235253 concurrency_settings (ConcurrencySettings): Reference to the parent settings object.
236- write (int): Maximum concurrent write requests (ingest, delete).
254+ write (int): Maximum concurrent write requests (ingest, upsert, delete).
255+ query_mutable (int): Maximum concurrent query requests against mutable streams.
256+ query_immutable (int): Maximum concurrent query requests against immutable streams.
257+ retrieve_mutable (int): Dedicated retrieve concurrency for mutable streams.
258+ retrieve_immutable (int): Dedicated retrieve concurrency for immutable streams.
259+ aggregate_mutable (int): Dedicated aggregate concurrency for mutable streams.
260+ aggregate_immutable (int): Dedicated aggregate concurrency for immutable streams.
237261 """
238262
239263 def __init__ (
240264 self ,
241265 concurrency_settings : ConcurrencySettings ,
242266 write : int ,
267+ query_mutable : int ,
268+ query_immutable : int ,
269+ retrieve_mutable : int ,
270+ retrieve_immutable : int ,
271+ aggregate_mutable : int ,
272+ aggregate_immutable : int ,
243273 ) -> None :
244274 super ().__init__ (concurrency_settings , "records" , read = 0 , write = write , delete = 0 )
275+ self ._query_mutable = query_mutable
276+ self ._query_immutable = query_immutable
277+ self ._retrieve_mutable = retrieve_mutable
278+ self ._retrieve_immutable = retrieve_immutable
279+ self ._aggregate_mutable = aggregate_mutable
280+ self ._aggregate_immutable = aggregate_immutable
281+
282+ @property
283+ def query_mutable (self ) -> int :
284+ return self ._query_mutable
285+
286+ @query_mutable .setter
287+ def query_mutable (self , value : int ) -> None :
288+ self ._check_frozen ("query_mutable" )
289+ self ._query_mutable = value
290+
291+ @property
292+ def query_immutable (self ) -> int :
293+ return self ._query_immutable
294+
295+ @query_immutable .setter
296+ def query_immutable (self , value : int ) -> None :
297+ self ._check_frozen ("query_immutable" )
298+ self ._query_immutable = value
299+
300+ @property
301+ def retrieve_mutable (self ) -> int :
302+ return self ._retrieve_mutable
303+
304+ @retrieve_mutable .setter
305+ def retrieve_mutable (self , value : int ) -> None :
306+ self ._check_frozen ("retrieve_mutable" )
307+ self ._retrieve_mutable = value
308+
309+ @property
310+ def retrieve_immutable (self ) -> int :
311+ return self ._retrieve_immutable
312+
313+ @retrieve_immutable .setter
314+ def retrieve_immutable (self , value : int ) -> None :
315+ self ._check_frozen ("retrieve_immutable" )
316+ self ._retrieve_immutable = value
317+
318+ @property
319+ def aggregate_mutable (self ) -> int :
320+ return self ._aggregate_mutable
321+
322+ @aggregate_mutable .setter
323+ def aggregate_mutable (self , value : int ) -> None :
324+ self ._check_frozen ("aggregate_mutable" )
325+ self ._aggregate_mutable = value
326+
327+ @property
328+ def aggregate_immutable (self ) -> int :
329+ return self ._aggregate_immutable
330+
331+ @aggregate_immutable .setter
332+ def aggregate_immutable (self , value : int ) -> None :
333+ self ._check_frozen ("aggregate_immutable" )
334+ self ._aggregate_immutable = value
245335
246336 def _semaphore_factory (self , operation : RecordsConcurrencyOperation , project : str ) -> asyncio .BoundedSemaphore :
247337 key = (operation .value , project , asyncio .get_running_loop ())
@@ -254,13 +344,31 @@ def _semaphore_factory(self, operation: RecordsConcurrencyOperation, project: st
254344 match operation :
255345 case RecordsConcurrencyOperation .WRITE :
256346 sem = asyncio .BoundedSemaphore (self ._write )
347+ case RecordsConcurrencyOperation .QUERY_MUTABLE :
348+ sem = asyncio .BoundedSemaphore (self ._query_mutable )
349+ case RecordsConcurrencyOperation .QUERY_IMMUTABLE :
350+ sem = asyncio .BoundedSemaphore (self ._query_immutable )
351+ case RecordsConcurrencyOperation .RETRIEVE_MUTABLE :
352+ sem = asyncio .BoundedSemaphore (self ._retrieve_mutable )
353+ case RecordsConcurrencyOperation .RETRIEVE_IMMUTABLE :
354+ sem = asyncio .BoundedSemaphore (self ._retrieve_immutable )
355+ case RecordsConcurrencyOperation .AGGREGATE_MUTABLE :
356+ sem = asyncio .BoundedSemaphore (self ._aggregate_mutable )
357+ case RecordsConcurrencyOperation .AGGREGATE_IMMUTABLE :
358+ sem = asyncio .BoundedSemaphore (self ._aggregate_immutable )
257359 case _:
258360 assert_never (operation )
259361 self ._semaphore_cache [key ] = sem
260362 return sem
261363
262364 def __repr__ (self ) -> str :
263- return f"Concurrency[records](write={ self ._write } )"
365+ return (
366+ f"Concurrency[records]("
367+ f"write={ self ._write } , "
368+ f"query_mutable={ self ._query_mutable } , query_immutable={ self ._query_immutable } , "
369+ f"retrieve_mutable={ self ._retrieve_mutable } , retrieve_immutable={ self ._retrieve_immutable } , "
370+ f"aggregate_mutable={ self ._aggregate_mutable } , aggregate_immutable={ self ._aggregate_immutable } )"
371+ )
264372
265373
266374class FileConcurrencyConfig (ConcurrencyConfig ):
@@ -425,7 +533,16 @@ def __init__(self) -> None:
425533 write_schema = 1 ,
426534 )
427535 self ._files = FileConcurrencyConfig (self , read = 4 , write = 2 , upload = 5 , download = 5 , delete = 2 , open_files = 15 )
428- self ._records = RecordsGlobalConcurrencyConfig (self , write = 20 )
536+ self ._records = RecordsGlobalConcurrencyConfig (
537+ self ,
538+ write = 20 ,
539+ query_mutable = 30 ,
540+ query_immutable = 10 ,
541+ retrieve_mutable = 20 ,
542+ retrieve_immutable = 10 ,
543+ aggregate_mutable = 10 ,
544+ aggregate_immutable = 5 ,
545+ )
429546
430547 @functools .cached_property
431548 def _all_concurrency_configs (self ) -> list [ConcurrencyConfig ]:
0 commit comments