---
id: "derived-store-without-cdc-or-rebuild"
type: "rule"
severity: "error"
url: "https://localhost:3000/catalogue/rules/derived-store-without-cdc-or-rebuild"
---

# Search index without an asynchronous feed

A search index has no asynchronous in-edge from a stream, change data capture or worker, so it cannot be rebuilt or kept in sync off the write path.

| | |
|---|---|
| Severity | error |
| Selects | every node of kind search-index |
| Components in scope | 32 |

## Why

A search index is derived data and needs an asynchronous feed from a stream, change data capture or an indexing worker. Without one it is written inline on the request path, and it can never be rebuilt after it drifts.

## What it checks

It looks at every node of kind search-index, and fires when:

- this does not hold:
  - the node has at least one asynchronous incoming edge from a stream, change-data-capture or worker

Message template:

```
Search index '{{node.id}}' has no asynchronous feed from a stream, change data capture or worker. It cannot be rebuilt or kept in sync independently of the write path.
```

## Applies to

- [Search index](https://localhost:3000/catalogue/kinds/search-index.md) `search-index`

32 catalogue components fall under it.

## Tripped by

Reference designs whose broken twin names this rule in its header:

- [airbnb-search](https://localhost:3000/d/zeema/airbnb-search)
- [booking-search](https://localhost:3000/d/zeema/booking-search)
- [facebook-tao](https://localhost:3000/d/zeema/facebook-tao)
- [gitlab-architecture](https://localhost:3000/d/zeema/gitlab-architecture)
- [grab-geo](https://localhost:3000/d/zeema/grab-geo)
- [instagram-feed](https://localhost:3000/d/zeema/instagram-feed)
- [lyft-envoy](https://localhost:3000/d/zeema/lyft-envoy)
- [notion-sharding](https://localhost:3000/d/zeema/notion-sharding)
- [proximity-service](https://localhost:3000/d/zeema/proximity-service)
- [stack-overflow](https://localhost:3000/d/zeema/stack-overflow)
- [tinder-geo](https://localhost:3000/d/zeema/tinder-geo)
- [twitch-live](https://localhost:3000/d/zeema/twitch-live)
- [twitter-timeline](https://localhost:3000/d/zeema/twitter-timeline)
- [uber-dispatch](https://localhost:3000/d/zeema/uber-dispatch)
- [x-recommendation](https://localhost:3000/d/zeema/x-recommendation)

## Source (`rule` YAML, verbatim)

```yaml
# yaml-language-server: $schema=../schema/rule.schema.json
# A search index is derived data. If nothing feeds it asynchronously (a stream, change data
# capture or an indexing worker) it is either written inline on the request path or never
# rebuilt after drift. Tripped by: designs/twitter-timeline.broken.yaml
schemaVersion: "1"
type: "rule"
id: "derived-store-without-cdc-or-rebuild"
severity: "error"
select:
  node:
    kind: "search-index"
when:
  not:
    hasEdge:
      direction: "in"
      mode: "async"
      peerKind: ["stream", "change-data-capture", "worker"]
message: "Search index '{{node.id}}' has no asynchronous feed from a stream, change data capture or worker. It cannot be rebuilt or kept in sync independently of the write path."
```

Human page: https://localhost:3000/catalogue/rules/derived-store-without-cdc-or-rebuild
