# Operation Guide

This document is the primary step-by-step operating guide for this skill. It is intended to be self-contained.

If you need more background, version-specific examples, or product documentation beyond this guide, look in the public `pingcap/docs` repository:

- `https://github.com/pingcap/docs`
- usually under the `ticdc/` directory

## Placeholders

Replace these placeholders before running commands:

- `<server>`: TiCDC HTTP address, for example `http://127.0.0.1:8300`
- `<keyspace>`: keyspace name, usually `default`
- `<changefeed_id>`: changefeed ID
- `<sink_uri>`: downstream sink URI
- `<config_file>`: path to a changefeed TOML file
- `<target_ts>`: target TSO
- `<overwrite_checkpoint_ts>`: numeric TSO used when resuming with checkpoint override

Always pass `keyspace` explicitly.

## Decide CLI Or API

Use CLI when:

- the `cdc` binary is available
- the task already has a TOML config file
- `--overwrite-checkpoint-ts=now` is required
- the operator wants shell commands

Use OpenAPI v2 when:

- the operation must happen over HTTP
- the workflow is part of automation
- the operator needs to read current config and send a merged `replica_config`
- the `cdc` binary is unavailable

## Safety Rules

- Query the current changefeed before any state-changing or config-changing operation.
- Re-query after every write operation.
- For `sink_uri`, `target_ts`, or config changes, follow `pause -> update -> resume`.
- Update is only safe when the changefeed is `stopped` or `failed`.
- API `replica_config` is a full replacement, not a partial patch.
- If resuming with checkpoint overwrite:
  - moving checkpoint forward can lose data
  - moving checkpoint backward can create duplicate delivery

## Common Read Operations

### List changefeeds with CLI

```bash
cdc cli changefeed list \
  --server=<server> \
  --keyspace=<keyspace>
```

Show removed and finished changefeeds too:

```bash
cdc cli changefeed list \
  --server=<server> \
  --keyspace=<keyspace> \
  --all
```

### List changefeeds with API

```bash
curl -X GET '<server>/api/v2/changefeeds?keyspace=<keyspace>'
```

Filter by state:

```bash
curl -X GET '<server>/api/v2/changefeeds?keyspace=<keyspace>&state=normal'
```

Supported state filters use TiCDC feed-state names: `all`, `normal`, `pending`, `warning`, `stopped`, `failed`, `removed`, and `finished`.
When `state` is omitted, the API keeps the default view and hides `removed` and `finished`.
Some list responses may render `pending` as `warning` in the JSON output even though the filter value is still `pending`.
The list response is wrapped as `{"total": <n>, "items": [...]}` rather than returned as a bare array.

### Query a changefeed with CLI

```bash
cdc cli changefeed query \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id>
```

Use simplified output if needed:

```bash
cdc cli changefeed query \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --simple
```

### Query a changefeed with API

```bash
curl -X GET '<server>/api/v2/changefeeds/<changefeed_id>?keyspace=<keyspace>'
```

For catch-up status:

```bash
curl -X GET '<server>/api/v2/changefeeds/<changefeed_id>/synced?keyspace=<keyspace>'
```

## Create A Changefeed

### Create with CLI

Minimal create:

```bash
cdc cli changefeed create \
  --server=<server> \
  --keyspace=<keyspace> \
  --sink-uri='<sink_uri>'
```

Recommended create with explicit ID and config:

```bash
cdc cli changefeed create \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --sink-uri='<sink_uri>' \
  --config=<config_file>
```

Optional `start-ts` and `target-ts`:

```bash
cdc cli changefeed create \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --sink-uri='<sink_uri>' \
  --config=<config_file> \
  --start-ts=<start_ts> \
  --target-ts=<target_ts>
```

If `--start-ts` is omitted, CLI create queries upstream TSO and starts from "now" at command time.

Verify after create:

1. Query the changefeed.
2. Confirm `state` becomes `normal`.
3. Confirm `sink_uri`, `target_ts`, and key config values are correct.

### Create with API

Minimal create:

```bash
curl -X POST '<server>/api/v2/changefeeds?keyspace=<keyspace>' \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'JSON'
{
  "changefeed_id": "<changefeed_id>",
  "sink_uri": "<sink_uri>"
}
JSON
```

`changefeed_id` is optional for API create. Omit it to let the server generate one.
For OpenAPI v2, the query parameter is authoritative for keyspace selection. A `keyspace` field in the JSON body is overwritten by `?keyspace=` or the default keyspace.

Create with `start_ts`, `target_ts`, and `replica_config`:

```bash
curl -X POST '<server>/api/v2/changefeeds?keyspace=<keyspace>' \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'JSON'
{
  "changefeed_id": "<changefeed_id>",
  "sink_uri": "<sink_uri>",
  "start_ts": <start_ts>,
  "target_ts": <target_ts>,
  "replica_config": {
    "...": "full config object"
  }
}
JSON
```

Unlike API update, API create starts from the default replica config before applying provided fields.
A partial `replica_config` can rely on defaults, but an explicit full object is safer when reproducibility matters.

## Pause, Resume, And Remove

### Pause with CLI

```bash
cdc cli changefeed pause \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id>
```

### Pause with API

```bash
curl -X POST '<server>/api/v2/changefeeds/<changefeed_id>/pause?keyspace=<keyspace>'
```

After pause:

1. Query the changefeed again.
2. Confirm `state == stopped`.

### Resume with CLI

Normal resume:

```bash
cdc cli changefeed resume \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id>
```

Resume with checkpoint overwrite:

```bash
cdc cli changefeed resume \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --overwrite-checkpoint-ts=<overwrite_checkpoint_ts>
```

CLI also supports:

```bash
cdc cli changefeed resume \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --overwrite-checkpoint-ts=now
```

### Resume with API

Normal resume:

```bash
curl -X POST '<server>/api/v2/changefeeds/<changefeed_id>/resume?keyspace=<keyspace>'
```

Resume with checkpoint overwrite:

```bash
curl -X POST '<server>/api/v2/changefeeds/<changefeed_id>/resume?keyspace=<keyspace>' \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'JSON'
{
  "overwrite_checkpoint_ts": <overwrite_checkpoint_ts>
}
JSON
```

API resume does not support `now`; it requires a numeric TSO.

After resume:

1. Query the changefeed again.
2. Confirm `state == normal`.
3. If needed, call `/synced` and confirm replication catches up.

### Remove with CLI

```bash
cdc cli changefeed remove \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id>
```

### Remove with API

```bash
curl -X DELETE '<server>/api/v2/changefeeds/<changefeed_id>?keyspace=<keyspace>'
```

After remove:

1. Query again.
2. Expect not found or the expected terminal state.

## Update A Changefeed

Use this standard sequence unless the task is explicitly limited to rendering commands only:

1. Query the current changefeed.
2. Pause the changefeed.
3. Apply the update.
4. Resume the changefeed.
5. Query again and verify the result.

### Change `sink_uri` only with CLI

```bash
cdc cli changefeed update \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --sink-uri='<sink_uri>'
```

### Change `sink_uri` only with API

```bash
curl -X PUT '<server>/api/v2/changefeeds/<changefeed_id>?keyspace=<keyspace>' \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'JSON'
{
  "sink_uri": "<sink_uri>"
}
JSON
```

### Change `target_ts` only with CLI

```bash
cdc cli changefeed update \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --target-ts=<target_ts>
```

### Change `target_ts` only with API

```bash
curl -X PUT '<server>/api/v2/changefeeds/<changefeed_id>?keyspace=<keyspace>' \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'JSON'
{
  "target_ts": <target_ts>
}
JSON
```

`target_ts` must be greater than `start_ts`.

### Update config with CLI

CLI is the easiest path when a TOML file already exists because CLI update applies the TOML patch on top of the current config:

```bash
cdc cli changefeed update \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --config=<config_file>
```

Combine config and `sink_uri` update:

```bash
cdc cli changefeed update \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --config=<config_file> \
  --sink-uri='<sink_uri>'
```

`--no-confirm` is not shown here intentionally.
On CLI create and update, it does more than skip prompts: if table verification finds ineligible tables, it also sets `ignore_ineligible_table=true`.

### Update config with API

API update requires a full `replica_config` object if `replica_config` is present in the request.
If a field is omitted inside `replica_config`, API update falls back to default values for that field instead of preserving the old value.

Use this manual procedure:

1. Query current config:

```bash
curl -X GET '<server>/api/v2/changefeeds/<changefeed_id>?keyspace=<keyspace>'
```

2. Copy the returned `config`.
3. Convert the TOML patch from `kebab-case` to API `snake_case`.
4. Deep-merge the patch into the copied `config`.
5. Send the merged object back as the full `replica_config`:

```bash
curl -X PUT '<server>/api/v2/changefeeds/<changefeed_id>?keyspace=<keyspace>' \
  -H 'Content-Type: application/json' \
  --data-binary @- <<'JSON'
{
  "replica_config": {
    "...": "merged current config plus requested patch"
  }
}
JSON
```

Do not send a partial TOML-derived object as the full `replica_config` unless resetting omitted fields back to defaults is explicitly intended.

### Full update example with CLI

```bash
cdc cli changefeed pause \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id>

cdc cli changefeed update \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id> \
  --config=<config_file> \
  --sink-uri='<sink_uri>' \
  --target-ts=<target_ts>

cdc cli changefeed resume \
  --server=<server> \
  --keyspace=<keyspace> \
  --changefeed-id=<changefeed_id>
```

## Verification Checklist

After any write operation, verify:

1. The changefeed is still queryable, unless it was removed.
2. `state` is the expected state:
   - `normal` after create or resume
   - `stopped` after pause
   - not found or terminal state after remove
3. `sink_uri`, `target_ts`, and key config fields match the intended change.
4. `checkpoint_tso` and `checkpoint_time` look reasonable.
5. If catch-up matters, call `/synced`.

## Common Pitfalls

- Forgetting `keyspace`.
- Updating config without pausing first.
- Assuming API `replica_config` behaves like a patch.
- Sending partial TOML-derived JSON as the whole `replica_config`.
- Using API resume with `overwrite_checkpoint_ts=now`; only CLI supports `now`.
- Claiming success before re-querying the changefeed.
