# Flow Control (/connectors/amqp/how-to/flow-control)



In AMQP 1.0, **link credit** is what publisher confirms and consumer prefetch/QoS are in other
protocols: it is the unit of flow control. A sender may not transmit a `TRANSFER` until the
receiver has granted `link-credit > 0` via a `FLOW`. This guide is the practical playbook for
driving the KubeMQ AMQP 1.0 connector's credit machinery — and, first and foremost, for avoiding
the two ways credit mismanagement silently loses data.

For the conceptual credit model, see the
[Events](/connectors/amqp/concepts/events) and
[Events Store](/connectors/amqp/concepts/events-store) pattern pages.

## The two data-loss footguns — read this first [#the-two-data-loss-footguns--read-this-first]

These are the two most expensive mistakes you can make against this connector. Both lose
messages **silently** — no error, no `DISPOSITION`, no `DETACH` (footgun A) — because the events
and events-store patterns deliver **pre-settled** (at-most-once). Internalize them before you
write a subscriber.

<Callout type="warn">
  **Footgun A — Events at 0 credit are silently DROPPED.** On an `events/<ch>` consume link, a
  message that arrives while your **link-credit is 0 is silently dropped and counted** — there is
  no error and no `DISPOSITION`. This is true at-most-once: the message is simply gone. It bites a
  slow consumer that lets its credit drain, or a subscriber that attaches *after* a publish (events
  have no replay). &#x2A;*Defense: grant credit continuously.** Open the receiver with a healthy standing
  credit and replenish *eagerly*, well before it hits 0. Watch
  `kubemq_amqp10_events_dropped_no_credit_total` — a non-zero value is silent data loss.
</Callout>

<Callout type="warn">
  **Footgun B — Events-Store stalled credit loses the buffered, already-acked window.** An
  `events-store/<ch>` consume link fronts the durable subscription with a **deliver-first ring
  buffer** (cap `MaxUnsettledPerLink` ≈ 1024) so the broker callback never stalls. The buffer's
  positions are **auto-acked *before* you take delivery**. If the buffer fills while your credit
  stays at 0, the link `DETACH`es with `amqp:resource-limit-exceeded` (`"credit stalled"`) and the
  **entire buffered window — already auto-acked — is lost**; a durable re-attach resumes *after*
  it. **Defense: size `MaxUnsettledPerLink` to your real prefetch and replenish credit
  aggressively** so the buffer never fills with credit at zero. The lost window is counted in
  `kubemq_amqp10_events_store_dropped_stalled_total`.
</Callout>

<Callout type="info">
  **Why queues are safe by contrast.** A queue consume link never drops on low credit: when you
  stop granting, KubeMQ simply stops delivering and the messages wait in the queue. The drop
  footguns are specific to the **pre-settled pub/sub** patterns (events, events-store).
</Callout>

## Who grants credit — the rule that governs everything [#who-grants-credit--the-rule-that-governs-everything]

The single most important distinction: &#x2A;*on a *consume* (server-sender) link, YOU must grant
credit; on a *produce* (server-receiver) link, the *server* grants credit.**

| Your link                                          | Server role     | Who grants credit                                  | If credit is 0…                                                                                                                           |
| -------------------------------------------------- | --------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Receiver** from `<pattern>/<ch>` (you *consume*) | server-sender   | **you (the client)** must `FLOW` `link-credit > 0` | **queues**: delivery pauses, messages wait. **events**: dropped (footgun A). **events-store**: buffered then stalled-`DETACH` (footgun B) |
| **Sender** to `<pattern>/<ch>` (you *produce*)     | server-receiver | **the server** grants you credit                   | you may not `TRANSFER` until you receive the server's `FLOW`                                                                              |

### Produce path — the server grants you credit [#produce-path--the-server-grants-you-credit]

On attach, a server-receiver link &#x2A;*immediately emits a `FLOW`** granting
`link-credit = MaxUnsettledPerLink` (default 1024, clamped `[1, 1<<20]`; fallback 256 if unset).
You **MUST NOT** send a `TRANSFER` before you receive that credit. As you complete deliveries the
server **replenishes** the window (a fresh `FLOW`) when remaining credit falls below half, so a
steady producer never stalls. The session `incoming-window` (2048) is a secondary bound.

### Consume path — you grant the server credit [#consume-path--you-grant-the-server-credit]

A server-sender link delivers **nothing** until you send a `FLOW` with `link-credit > 0`. The
effective credit the server may use is
`(flow.delivery-count + flow.link-credit) − server.delivery-count`, clamped ≥ 0.

* For **automatic** credit, grant a modest standing credit (e.g. **100–1000*&#x2A;, but **≤
  `MaxUnsettledPerLink` = 1024**) when you open the receiver, and let your client replenish on
  settlement.
* For **manual** credit control (`IssueCredit` / `DrainCredit&#x60;), open the receiver with
  &#x2A;*`Credit: -1`** — otherwise your client auto-manages credit and your manual calls fight it.

## Prefetch, `GetBatchSize`, and `MaxUnsettledPerLink` [#prefetch-getbatchsize-and-maxunsettledperlink]

These three knobs shape how the connector pulls from the broker and how much it will hold
unsettled:

| Knob                       | Default    | Meaning                                                                                          |
| -------------------------- | ---------- | ------------------------------------------------------------------------------------------------ |
| **link-credit (prefetch)** | you choose | the standing credit you grant a consume link = your prefetch depth                               |
| **`GetBatchSize`**         | `32`       | the per-`Get` ceiling on a **queue** consume link                                                |
| **`MaxUnsettledPerLink`**  | `1024`     | the per-link unsettled / pub-sub buffer cap; also drives the **inbound** (produce) credit window |

For a queue consume link, each `Get` reserves
`min(credit, GetBatchSize=32, MaxUnsettledPerLink − unsettled)` and issues a downstream `Get`
with a 1000 ms long-poll. So even with high standing credit, a single `Get` pulls at most 32
messages — credit controls overall in-flight depth, `GetBatchSize` controls batch granularity.

<Callout type="info">
  `MaxUnsettledPerLink` is the dial that protects you from footgun B — it is the events-store
  ring-buffer cap. It is a server config field (`CONNECTORS_AMQP10_MAX_UNSETTLED_PER_LINK`); you
  respect it client-side by keeping your standing credit ≤ it and replenishing eagerly. See
  [Configuration](/connectors/amqp/concepts/configuration).
</Callout>

## Drain [#drain]

Drain is how a consumer says "give me everything you have, then stop." Send a `FLOW` with
`drain=true`. The server then:

1. **advances `delivery-count`** by the remaining credit,
2. **zeroes the credit**, and
3. **echoes a `FLOW`** with `link-credit=0, drain=true` (the drain response).

Drain completes **promptly** — it does not hang. A held remainder (messages that didn't fit the
drained credit) &#x2A;*resumes on a fresh `IssueCredit`**. Two requirements your client must honor:

* A `FLOW` &#x2A;*MUST carry `next-incoming-id`** once the session is established.
* To drive drain manually, the receiver must be in manual-credit mode (`Credit: -1`).

## Putting it together [#putting-it-together]

| Pattern                      | Consume-side credit hygiene                                                                                                                                                |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Queues**                   | Safe: grant standing credit; if you stop granting, messages wait. No drop.                                                                                                 |
| **Events**                   | **Footgun A.** Grant generous standing credit, replenish *before* it hits 0, subscribe before publishing. A gap at 0 credit = silent loss.                                 |
| **Events-Store**             | **Footgun B.** Size `MaxUnsettledPerLink` to your prefetch; replenish aggressively. A genuine stall overflows the deliver-first buffer and loses the already-acked window. |
| **Commands / Queries (RPC)** | The responder pump runs under credit; grant the responder credit and grant the dynamic reply node credit so replies can land.                                              |

## Monitoring [#monitoring]

Watch these metrics — a non-zero value on the first two **is silent data loss**:

| Metric                                             | Meaning                                                                  |
| -------------------------------------------------- | ------------------------------------------------------------------------ |
| `kubemq_amqp10_events_dropped_no_credit_total`     | **Footgun A** — events dropped at 0 credit                               |
| `kubemq_amqp10_events_store_dropped_stalled_total` | **Footgun B** — events-store buffered window lost to a credit stall      |
| `kubemq_amqp10_transfers_in_dropped_total`         | inbound transfers dropped (oversize / no-consumer / pre-settled failure) |

See [Connections endpoint](/connectors/amqp/reference/connections-endpoint) for the full
metric and dashboard surface.

## Related [#related]

<Cards>
  <Card title="Reliability" href="/connectors/amqp/how-to/reliability" description="Settlement modes, delivery-state outcomes, redelivery, and the teardown NAck-all guarantee on queues." />

  <Card title="Events" href="/connectors/amqp/concepts/events" description="Fire-and-forget pub/sub — subscribe before publishing; the 0-credit drop footgun in full." />

  <Card title="Events Store" href="/connectors/amqp/concepts/events-store" description="Durable, replayable subscriptions and the stalled-credit buffer footgun." />
</Cards>
