with one click
repository-guide
// Guide for implementing Backend.AI repository patterns (create, get, search, update, delete, purge, batch operations, Querier, BatchQuerier, Creator, Updater, Purger, SearchScope, with_tables)
// Guide for implementing Backend.AI repository patterns (create, get, search, update, delete, purge, batch operations, Querier, BatchQuerier, Creator, Updater, Purger, SearchScope, with_tables)
Diagnose and fix Docker Compose halfstack issues ā config mapping, service health, DB/Redis/etcd inspection, supergraph regeneration
Local development tools ā service management (./dev) and v2 CLI testing (./bai)
Guide for implementing REST and GraphQL APIs (create, get, search, update, delete, purge, scope prefix patterns, admin_ prefix, SearchScope, BaseFilterAdapter, @api_function, Click CLI)
Guide for implementing Backend.AI client SDK and CLI (Session, BaseFunction, @api_function, Click commands, Pydantic models, FieldSpec, output handlers, APIConfig, testing)
Complete submission workflow - quality checks, commit, PR creation, changelog generation, and final push. Use after finishing implementation work.
Guide the Backend.AI release process - run release.sh, generate changelog via towncrier, consolidate RC entries for final releases with subsection grouping.
| name | repository-guide |
| description | Guide for implementing Backend.AI repository patterns (create, get, search, update, delete, purge, batch operations, Querier, BatchQuerier, Creator, Updater, Purger, SearchScope, with_tables) |
| invoke_method | automatic |
| auto_execute | false |
| enabled | true |
Guide for implementing Backend.AI repositories using base patterns and standard operations.
Repositories implement 6 standard operations:
Batch operations:
batch_update - Update multiple entitiesbatch_delete - Soft delete multiple entitiesbatch_purge - Hard delete multiple entitiesMethod naming (no prefix):
await repository.create(data)
await repository.get(id, scope=None)
await repository.search(scope, filters, pagination)
await repository.update(id, data)
await repository.delete(id)
await repository.purge(id)
await repository.batch_update(ids, data)
await repository.batch_delete(ids)
await repository.batch_purge(ids)
Located in: src/ai/backend/manager/repositories/base/
All repositories use these base utilities for standard operations:
Multi-tenant access control for queries.
Implementations: repositories/{domain}/types.py
repositories/fair_share/types.py - DomainFairShareSearchScoperepositories/group/types.py - GroupSearchScopePattern:
to_conditions() method converts to query conditionsAvailable utilities:
Creator[TRow] - Create operationsQuerier[TRow] - Single entity retrieval (get)BatchQuerier[TRow] - Search with filters and paginationUpdater[TRow] - Update with OptionalState patternPurger[TRow] - Hard delete operationsBatchUpdater[TRow], BatchPurger[TRow] - Batch operationsSee implementations:
repositories/base/ - Base utility source coderepositories/fair_share/repository.py - Usage examplesops/) ā Preferred for new codeops/provider.py wraps ExtendedAsyncSAEngine and exposes a spec-only operations
surface. Migration direction: db_sources are moving to this wrapper. New or modified
db_sources SHOULD use DBOpsProvider rather than holding the engine and calling
session.execute directly. Existing db_sources are migrated gradually when touched.
class FooDBSource:
def __init__(self, db: ExtendedAsyncSAEngine) -> None:
self._ops = DBOpsProvider(db)
async def foo(self, foo_id: FooID) -> FooData:
async with self._ops.read_ops() as r:
result = await r.query(Querier(row_class=FooRow, pk_value=foo_id))
if result is None:
raise FooNotFound()
return result.row.to_data()
async def create(self, spec: FooCreatorSpec) -> FooData:
async with self._ops.write_ops() as w:
result = await w.create(Creator(spec=spec))
return result.row.to_data()
read_ops() / write_ops() both open a READ COMMITTED session; the raw engine/
session is never exposed.Select/Insert/Update/Delete.async with self._ops.read_ops() as r:
# default ā scoped query:
result = await r.batch_query_with_scopes(sa.select(FooRow), querier, scopes)
# superadmin / internal only ā bypasses scope filtering:
result = await r.batch_query_in_global(sa.select(FooRow), querier)
Use batch_query_in_global only for superadmin-only endpoints or internal system
operations; it bypasses RBAC scope filtering. Empty scopes for the scoped variant
raises EmptySearchScopeError (400).
Each spec owns exactly one table ā never build child-table rows inside a creator/ updater spec. For a parent + dependent children, the repository coordinates the sequence procedurally (no tree object):
async with self._ops.write_ops() as w:
parent = (await w.create(Creator(spec=parent_spec))).row
dep = ChildDependency(parent_id=parent.id) # build dependency from the result
children = (await w.bulk_create_dependent(child_specs, dep)).rows
DependentCreatorSpec[TDependency, TRow].build_row(dependency) builds one row from a
dependency resolved at execution time (e.g. the parent's generated id).create_dependent (single) / bulk_create_dependent (multiple) execute them.bulk_create_dependent new children, all inside one write_ops() block.Pattern:
__init__begin_session() for writes, begin_readonly_session() for readsSee complete implementation:
repositories/fair_share/repository.py - Full repository examplerepositories/group/repository.py - Scope usage patternsrepositories/domain/repository.py - Standard operationsArchitecture principle:
Directory structure:
repositories/{domain}/
āāā repository.py # Public interface, source delegation
āāā db_source/
ā āāā __init__.py
ā āāā db_source.py # DB operations implementation
āāā cache_source/ # (Optional)
āāā __init__.py
āāā cache_source.py # Cache operations implementation
Pattern:
db_source modulebegin_session() or begin_readonly_session())Responsibilities:
See complete examples:
repositories/fair_share/repository.py:39 - DB Source import and usagerepositories/fair_share/db_source/db_source.py - DB Source implementationrepositories/scheduler/cache_source/cache_source.py - Cache Source examplerepositories/README.md:78 - Cache management referenceMigration status:
File: repositories/{domain}/types.py
Define SearchScope dataclass with to_conditions() method.
See examples:
repositories/fair_share/types.py - Multiple scope typesrepositories/group/types.py - Scope with conditionsFile: repositories/{domain}/repository.py
__init__File: tests/unit/manager/repositories/{domain}/test_{operation}.py
with_tables fixture for real DBSee: /tdd-guide skill for testing workflow
Pattern:
begin_readonly_session()begin_session()db_sess to base utilitiesSee examples: repositories/fair_share/repository.py
Tips:
See: repositories/base/ for optimization utilities
Pattern:
query(), search(), purge())to_conditions() converts to query filtersSee examples: repositories/fair_share/repository.py
Use domain types:
FairShareId instead of strDomainName instead of strPattern:
BackendAIErrorEntityNotFound, EntityAlreadyExists)See examples: repositories/fair_share/repository.py
Study these implementations:
src/ai/backend/manager/repositories/fair_share/ - Complete implementationsrc/ai/backend/manager/repositories/group/ - Scope usagesrc/ai/backend/manager/repositories/domain/ - Standard operations/service-guide - Service methods using repositories/api-guide - API handlers calling services/tdd-guide - TDD workflow with repositoriesrepositories/README.md - Architecture overviewSearchScope not filtering:
to_conditions() returns correct conditionsFK constraint violations:
with_tables fixtureType errors:
Querier[YourRow]Performance issues:
begin_readonly_session() for readsStandard operations:
Base utilities:
Key principles:
Next steps:
/tdd-guide/service-guide)