# TiDB Search Fabric Integration

Date: 2026-03-28
Status: Frozen architecture note
Owner: Search Platform Architecture

## 1. Scope

This document defines the steady-state integration architecture between TiDB and Search Fabric.

It covers:
- the boundary between TiDB-owned SQL/DDL domains and Search-Fabric-owned retrieval services
- the two admitted TiDB query routes
- the ownership-split write model
- the control-plane bootstrap and schema-sync boundary
- the TiDB-side integration seam required to keep planner, executor, DDL, and remote-DML lowering contract-clean
- the steady-state consistency, fallback, and fault-isolation model

It does not cover:
- current-code coupling inventory
- migration sequencing, cutover rehearsal, or rollback execution plans
- pre-cutover remediation checklists
- implementation task breakdown for the TiDB codebase

Those transitional materials live here instead:
- `docs/plans/2026-03-28-tidb-search-fabric-current-coupling-audit.md`
- `docs/plans/2026-03-28-tidb-search-fabric-cutover-plan.md`

This document is subordinate to the canonical owner stack:
- machine-readable wire contract: `api/proto/searchfabric/v1/search_fabric.proto`
- normative API semantics: `docs/specs/core/search-fabric-api.md`
- canonical error semantics: `docs/specs/core/error-codes.md`
- global topology and owner boundaries: `docs/specs/core/search-fabric-architecture.md`

## 2. Boundary Statement

TiDB and Search Fabric communicate only through stable, language-neutral APIs.

Hard architectural constraints:

1. Query integration is limited to exactly two routes:
- auxiliary-index acceleration for collections whose authoritative descriptor declares `collection_truth_mode=COLLECTION_TRUTH_MODE_EXTERNAL`
- federated-table access to Search-Fabric-owned collections whose authoritative descriptor declares `collection_truth_mode=COLLECTION_TRUTH_MODE_SEARCH_FABRIC`

2. Write ownership is split by collection truth mode:
- external-truth collections: `TiDB -> TiCDC -> CDC Adapter -> Ingest Gateway`
- Search-Fabric-truth collections: `TiDB federated SQL remote DML -> Ingest Gateway`

3. Shared time contract is limited to TiDB TSO:
- `commit_ts`
- `read_ts`

4. TiDB internals do not cross the boundary:
- no `sessionctx`, `chunk.Chunk`, planner/operator structs, or TiDB protobuf internals on the Search Fabric wire
- no TiCI-specific request shapes, KV flags, or schema blobs on the steady-state contract

Non-goals:
- Search Fabric participating in TiDB 2PC
- deep physical-operator binding beyond the two admitted TiDB query routes
- TiDB-internal object/schema leakage over service boundaries

## 3. Steady-State Data and Control Flows

```text
                     +-------------------+
                     |       TiDB        |
                     | Planner/Executor  |
                     | DDL / Session     |
                     +----+----+----+----+
                          |    |    |
      Projected/Primary   |    |    |  Schema / Identity Sync
      Query RPC           |    |    +--------------------------+
                          |    |                               |
                          v    |                               v
                +-----------------------+           +-----------------------+
                |    Search Fabric      |           |  Catalog Control     |
                |   Query Gateway +     |<--------->|      Service         |
                |  Relational Service   |           +-----------------------+
                +-----------+-----------+
                            |
                            | Query Broker / Readers
                            v
                      Search Serving Path

Write paths (ownership split):

  External truth:
    TiDB -> TiCDC -> CDC Adapter -> Ingest Gateway -> Search Fabric ingest

  Search-Fabric truth:
    TiDB federated SQL remote DML -> Ingest Gateway -> Search Fabric ingest
```

Steady-state route binding:
- TiDB aux-index query uses `RelationalQueryService.ExecuteProjectedQueryOrFallback`
- TiDB federated-table query uses `RelationalQueryService.ExecutePrimaryQuery`
- TiDB optimizer costing uses `QueryGatewayService.GetCollectionStatistics`
- TiDB remote DML uses `IngestGatewayService.StreamMutations`
- optional omitted-ID preallocation, when explicitly admitted by schema policy, uses `IdAllocatorService.AllocateIds`
- TiDB bootstrap / schema sync uses `CatalogControlService`

## 4. TiDB-Side Integration Surfaces

TiDB should keep one dedicated integration layer instead of spreading Search Fabric semantics across planner, executor, DDL, and write call sites:

- `pkg/searchfabric/client/`
  - RPC transport, retries, deadlines, auth, and circuit breaking
- `pkg/searchfabric/acl/`
  - Anti-Corruption Layer from TiDB domain carriers to generated canonical protobuf carriers
- `pkg/searchfabric/planner/`
  - route selection, fallback policy, and consistency propagation
- `pkg/searchfabric/executor/`
  - response decoding and chunk materialization
- `pkg/searchfabric/write/`
  - federated-table remote-DML lowering, request shaping, and ACK/result reconstruction

Direct `pkg/tici/*` dependencies are not part of the steady-state planner/executor/DDL/query path after cutover.

### 4.1 Mandatory Planner -> Executor Seam

Planner and executor exchange exactly one TiDB-local integration fragment:

```go
type SearchRouteKind int

const (
    SearchRouteKindUnspecified SearchRouteKind = iota
    SearchRouteKindProjected
    SearchRouteKindPrimary
)

type SearchPlanRequest struct {
    Query                    *searchfabricv1.QueryRequest
    ProjectedSnapshotOverlay *searchfabricv1.SnapshotOverlay
    ProjectedSnapshotReadTs  uint64
}

type SearchPlanFragment struct {
    RouteKind SearchRouteKind
    Request   *SearchPlanRequest
}
```

Rules:
- no TiCI protobuf or KV request flags are admitted in this seam
- `SearchRouteKindProjected` maps only to the projected route on `COLLECTION_TRUTH_MODE_EXTERNAL`
- `SearchRouteKindPrimary` maps only to the primary route on `COLLECTION_TRUTH_MODE_SEARCH_FABRIC`
- planner populates canonical query semantics directly:
  - `collection`
  - `expected_schema_generation_token`
  - `consistency`
  - `search_payload`
  - `fetch_spec`
- executor owns only:
  - `RequestMeta`
  - transport retries/deadlines
  - response decoding
- `ProjectedSnapshotOverlay` / `ProjectedSnapshotReadTs` are legal only on the projected route
- primary-route fragments keep overlay fields empty in the steady-state model

### 4.2 ACL Responsibilities

The ACL is the only TiDB-side layer that understands both domains.

It converts TiDB domain inputs such as:
- `sessionctx.Context`
- `expression.Expression`
- `chunk.Chunk` target schemas

into canonical Search Fabric carriers such as:
- typed predicate trees
- typed scalar/vector values
- canonical `DocumentID`
- columnar projection headers and aligned result values

Guardrails:
- no TiDB AST names or TiDB enum strings cross the wire
- no marshaled TiDB `TableInfo`-style schema blobs cross the wire
- range/order semantics must lower into canonical typed branches, not stringly-typed adapters
- unsupported source semantics fail closed before boundary RPC emission

## 5. Canonical Cross-Boundary Service Bindings

| TiDB concern | Canonical Search Fabric surface | Steady-state rule |
|---|---|---|
| Aux-index query | `RelationalQueryService.ExecuteProjectedQueryOrFallback` | Only for collections whose resolved descriptor declares `COLLECTION_TRUTH_MODE_EXTERNAL`. |
| Federated-table query | `RelationalQueryService.ExecutePrimaryQuery` | Only for collections whose resolved descriptor declares `COLLECTION_TRUTH_MODE_SEARCH_FABRIC`. |
| Optimizer statistics | `QueryGatewayService.GetCollectionStatistics` | Query Gateway fronts a CCS-owned optimizer-statistics snapshot family on the current query-serving `latest READY` lineage. |
| Readiness diagnostics | `QueryGatewayService.GetReadiness` | Query Gateway / Query Broker own the helper RPC; TiDB does not treat CCS as a second public readiness API. |
| Federated-table remote DML | `IngestGatewayService.StreamMutations` | One admitted SQL statement maps to one canonical `MutationBatch`. |
| Optional omitted-ID helper | `IdAllocatorService.AllocateIds` | Only when schema policy explicitly enables allocator-backed create/insert identity. |
| Namespace bootstrap | `CatalogControlService.PutNamespaceMetadata` | TiDB-owned only for external-truth aux-index collections. |
| Collection bootstrap | `CatalogControlService.PutCollectionMetadata` | TiDB-owned only for external-truth aux-index collections. |
| Schema sync | `CatalogControlService.PutCollectionSchema` | Search-affecting DDL uses typed fencing and replay-stable request identity. |
| Collection drop | `CatalogControlService.DropCollection` | Collection-scoped plus optional fence; no `ddl_commit_ts` field on this RPC. |

canonical query service behavior is defined by `docs/specs/core/search-fabric-api.md` Sections `5.1`, `5.4`, `5.10`, and `5.11`.
Stable TiDB-specific conformance invariants are tracked in `docs/testing/api-contract-conformance-matrix.md` entries `SF-CONF-TIDB-001` through `SF-CONF-TIDB-004`.

## 6. Identity Bootstrap and Ownership Modes

### 6.1 External-Truth Aux-Index Collections

For `collection_truth_mode=COLLECTION_TRUTH_MODE_EXTERNAL`:

- TiDB DDL/bootstrap owns first-time namespace and collection identity ensure
- TiDB query/statistics/CDC callers treat resolved identities as prerequisites
- query route is always the projected route
- TiDB remote DML is not admitted on these collections
- canonical bindings come from CCS:
  - TiDB may cache `ClusterID -> namespace_id`
  - TiDB may cache `TableID -> collection_id`
  - CCS descriptors remain the source of truth

### 6.2 Search-Fabric-Truth Federated Collections

For `collection_truth_mode=COLLECTION_TRUTH_MODE_SEARCH_FABRIC`:

- namespace and collection identity are pre-provisioned by an external control-plane workflow
- federated-table query and write paths therefore start from a control-plane identity inventory and consume already-resolved canonical bindings rather than performing gateway-local identity inference
- TiDB must not allocate replacement identities through `PutNamespaceMetadata` / `PutCollectionMetadata`
- TiDB may only verify or resolve those identities through public CCS reads (`GetNamespaceMetadata.lookup.{namespace_id|namespace_name}` and `GetCollectionMetadata.lookup.{collection|name_binding}`)
- query route is always the primary route
- write route is TiDB federated SQL remote DML into `IngestGatewayService.StreamMutations`

### 6.3 Caller Identity Split

Steady-state TiDB-originated caller roles are fixed:

- query, statistics, and readiness callers use `CLIENT_KIND_TIDB_COMPUTE`
- query, statistics, and readiness callers:
  - `client_kind=CLIENT_KIND_TIDB_COMPUTE`
  - `client_id=tidb-compute`
- federated-table remote-write callers:
  - `client_kind=CLIENT_KIND_TIDB_WRITE_GATEWAY`
  - `client_id=tidb-write-gateway`
- DDL/schema-sync callers:
  - `client_kind=CLIENT_KIND_TIDB_DDL_ADAPTER`
  - `client_id=tidb-ddl-adapter`
- TiCDC-driven ingest callers:
  - `client_kind=CLIENT_KIND_TIDB_CDC_ADAPTER`
  - `client_id=tidb-cdc-adapter`

`client_id` remains a stable logical caller identity, not a per-process or per-replica instance token.

## 7. Query Consistency and Fallback Boundaries

### 7.1 Projected Route

The projected route exists for aux-index acceleration on external-truth collections.

Rules:
- request carrier is `ProjectedQueryRequest`
- `ProjectedQueryRequest.query` remains the canonical semantic payload
- projected-route overlay snapshot binding, deadline/fallback behavior, and degraded-success details are owned by `docs/specs/core/search-fabric-api.md` Sections `5.1`, `5.4`, `5.10`, and `5.11`, plus `docs/testing/api-contract-conformance-matrix.md` invariants `SF-CONF-TIDB-001` and `SF-CONF-TIDB-003`
- route-specific architecture boundary: projected route is the only TiDB query route that may return transport-`OK` `ProjectedQueryResponse.result=fallback`
- non-`OK` gRPC status plus canonical `ErrorStatus` is the fail-closed path for malformed/authz/unsupported/runtime/backend failures
- non-`OK` gRPC failures remain canonical errors; TiDB must not reinterpret them as native-plan fallback

### 7.2 Primary Route

The primary route exists for federated-table access on Search-Fabric-truth collections.

Rules:
- request carrier is `PrimaryQueryRequest`
- primary-route transport and overlay-free execution rules are owned by `docs/specs/core/search-fabric-api.md` plus `docs/testing/api-contract-conformance-matrix.md` invariant `SF-CONF-TIDB-002`
- no overlay carriers exist on the steady-state primary route
- route is success/error only; no TiDB-native fallback branch exists

### 7.3 Consistency Modes

TiDB must propagate canonical consistency fields explicitly.

- `STRICT`
  - preserves caller `read_ts`
  - route-specific waiting / fallback behavior is tracked by `SF-CONF-TIDB-001` and `SF-CONF-TIDB-002`
  - primary route fails closed with `SF_VISIBILITY_TIMEOUT` on deadline expiry
- `BOUNDED_STALENESS`
  - preserves caller `read_ts` plus `max_staleness_ms`
  - route-specific waiting / fallback behavior is tracked by `SF-CONF-TIDB-001` and `SF-CONF-TIDB-002`
  - primary route fails closed with `SF_VISIBILITY_TIMEOUT` on deadline expiry
- `BEST_EFFORT`
  - sends `read_ts=0`
  - first page executes on the admitted best-effort snapshot
  - continuation remains bound to token-lineage snapshot, not the newest `safe_ts`

### 7.4 Readiness and Statistics

- `GetCollectionStatistics` is for costing, not visibility waiting
- statistics fast-path shape, freshness semantics, and fail-closed taxonomy are owned by `docs/specs/core/search-fabric-api.md` plus `docs/testing/api-contract-conformance-matrix.md` invariant `SF-CONF-TIDB-003`
- `GetReadiness` remains available for diagnostics and non-hot-path planning
- `ExecuteProjectedQueryOrFallback` internalizes readiness evaluation and fallback selection on the projected-route hot path
- TiDB must not treat CCS as a second public helper endpoint for either readiness or optimizer statistics

### 7.5 Shadow-Read Parity

- shadow-read route pairing and overlay carry-forward rules are tracked by `docs/testing/api-contract-conformance-matrix.md` invariant `SF-CONF-TIDB-004`

## 8. Remote DML and DDL Boundaries

### 8.1 Federated-Table Remote DML

For `COLLECTION_TRUTH_MODE_SEARCH_FABRIC` collections:

- one admitted SQL statement maps to one canonical `MutationBatch`
- current revision still requires explicit canonical `RowMutation.doc_id` on every lowered row
- optional allocator-backed omitted-ID support, when enabled by schema policy, is a pre-ingest control-plane step through `AllocateIds`
- default TiDB-facing remote-write policy is:
  - `DURABILITY_POLICY_WAL_CHUNK_DURABLE`
  - `VISIBILITY_POLICY_WAIT_FOR_VISIBLE`
  - `ATOMICITY_POLICY_ALL_OR_NOTHING`
- unsupported SQL forms remain fail-closed:
  - multi-statement transactions
  - mixed local-TiKV + federated-table write transactions
  - locking reads / pessimistic locking / savepoints / XA
  - `INSERT ... SELECT`
  - non-point `UPDATE` / `DELETE` that cannot lower into one deterministic bounded `doc_id` plan

### 8.2 Search-Affecting DDL

Search-affecting DDL uses canonical control-plane APIs with typed fencing:

- `PutCollectionSchema`
  - replay-stable non-empty `RequestMeta.idempotency_key`
  - typed `SchemaFencePrecondition`
  - authoritative `schema_version`
  - explicit `update_policy`
- `DropCollection`
  - replay-stable non-empty `RequestMeta.idempotency_key`
  - collection scope plus optional fence
  - no `ddl_commit_ts` field

Failure policy:
- search-related DDL: fail closed on rejection, conflict, or unavailable dependency
- unrelated DDL: may remain fail-open with async compensation outside this integration contract

## 9. Fault-Isolation Model

| Path | Trigger | Deterministic action |
|---|---|---|
| OLTP DML / 2PC | Search Fabric unavailable | fail open |
| Aux-index query | projected-route fallback condition | explicit transport-`OK` fallback |
| Aux-index query | malformed/authz/runtime/backend failure | canonical non-`OK` gRPC error |
| Federated-table query | visibility deadline expiry | `SF_VISIBILITY_TIMEOUT` |
| Federated-table query | runtime/backend failure | canonical non-`OK` gRPC error |
| Federated-table remote DML | backend unavailable | `SF_BACKEND_UNAVAILABLE` |
| Federated-table remote DML | unsupported statement/transaction shape | `SF_UNSUPPORTED_TABLE_OPERATION` |
| Search-affecting DDL | schema sync rejected/unavailable | fail closed |

The integration boundary never introduces a hidden second fallback language. Projected-route `fallback` and canonical `ErrorStatus` remain distinct.

## 10. Summary

The steady-state TiDB/Search Fabric architecture stays intentionally narrow:

- two admitted query routes,
- one ownership-split write model,
- one control-plane identity/schema boundary,
- one TiDB-local integration seam,
- one canonical consistency/fallback taxonomy.

This keeps TiDB focused on transactional SQL while allowing Search Fabric to remain the independent owner of retrieval serving, visibility, and search-optimized storage.
