# Chunking

{% tabs %}
{% tab title="End users" %}
Chunking splits big writes into smaller batches.

It helps when a provider is strict or rate-limited.

#### When to enable chunking

Enable it if you see:

* rate limit errors
* timeouts on large batches
* “one bad item breaks the whole batch”

#### Safe starting point

* Start with `apply_chunk_size: 25`
* Add `apply_chunk_pause_ms: 200` if you still hit limits

Related:

* Runtime config keys: [Runtime](/blueprint-architecture/orchestrator/runtime.md)
  {% endtab %}

{% tab title="Power users" %}
This doc describes how the orchestrator decides batch sizes for writes, and how it slows down to avoid rate-limit pain.

**Code:** `cw_platform/orchestrator/_chunking.py` + `orchestrator/_applier.py`

***

### Why chunking exists

Providers vary wildly:

* Some accept big batches (Plex-ish).
* Some want small batches (Trakt-ish).
* Some accept batches but rate-limit aggressively (Simkl-ish depending on endpoint).

Chunking gives you:

* faster progress visibility
* less blast radius per API call
* fewer “one bad item ruins 500” failures

***

### Where chunking is applied

Chunking happens in the applier:

* `_applier._apply_chunked(...)`

The pipeline passes:

* `chunk_size`
* `chunk_pause_ms`

If chunking is off, applier calls provider once per add/remove.

***

### How chunk size is chosen

The orchestrator computes chunk size via:

* `effective_chunk_size(ctx, provider_name)` in `_chunking.py`

Inputs:

* `ctx.apply_chunk_size` (base size)
* `ctx.apply_chunk_size_by_provider` (overrides)
* provider name normalization (uppercased)

Resolution rule:

1. If per-provider override exists and > 0 → use it
2. Else if base size > 0 → use base size
3. Else → 0 (no chunking)

***

### Config keys

In `config.json`:

```json
{
  "runtime": {
    "apply_chunk_size": 50,
    "apply_chunk_pause_ms": 200,
    "apply_chunk_size_by_provider": {
      "TRAKT": 10,
      "SIMKL": 25
    }
  }
}
```

Accepted alias keys (same behavior):

* `apply_chunk_sizes_by_provider`
* `apply_chunk_sizes`

Provider keys are uppercased internally.

***

### Chunk pause (throttle between chunks)

If `runtime.apply_chunk_pause_ms > 0`:

* applier sleeps after each chunk

This is a simple “be polite” delay. It’s not adaptive and doesn’t look at headers.

***

### Rate-limit awareness (telemetry warnings)

Chunking itself doesn’t read rate-limit headers. Instead, the orchestrator can emit warnings if rate remaining is low.

At end of run:

* `ctx.emit_rate_warnings()`
* uses thresholds from `telemetry.warn_rate_remaining`

Default thresholds are provider-specific and conservative.

This is a warning system only; it doesn’t auto-throttle.

If you want adaptive rate limiting, you’d implement it in providers (based on 429 / Retry-After, etc.).

***

### Provider side: recommended behavior under chunking

Providers should treat each chunk as independent and return:

* `confirmed_keys` when possible
* `unresolved` as a list of items with IDs for failures

If a provider throws an exception for a chunk:

* applier retries that chunk up to 3 times with exponential backoff
* if it still fails, it treats that chunk as failed and returns `ok:false` at aggregate level

This is why chunking can help:

* one failing chunk doesn’t nuke the entire run.

***

### Common chunking setups

#### Conservative (good for Trakt/Simkl)

* base 25
* Trakt override 10
* pause 150–300ms

#### Aggressive (fast, but riskier)

* base 100
* no pause

#### “Debug mode”

* base 1
* pause 0

Helps isolate a single bad item and see which exact key fails.

***

### Gotchas

1. Chunking increases total calls → can *increase* rate-limit pressure if set too low.
2. If provider doesn’t return `confirmed_keys`, it can be harder to map successes/failures precisely.
3. If you chunk too large, one provider error can still fail a lot of items.

***

### Related pages

* How chunking aggregates results: [Applier](/blueprint-architecture/orchestrator/applier.md)
* Where chunk config is parsed: [Runtime](/blueprint-architecture/orchestrator/runtime.md)
* Progress events: [Eventing](/blueprint-architecture/orchestrator/eventing.md)
  {% endtab %}
  {% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.crosswatch.app/blueprint-architecture/orchestrator/chunking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
