# How polls and `max_concurrent:` run

This is the detail behind [Polls](what-package-code-may-do.md#polls) and a connection's
`max_concurrent:`: what a poll declares, when it runs and what it keeps,
and how the cap on calls at once is counted.

- **A poll's keys.** Each entry of `polls:` in `provider.jadpo` is a
  block of `call:`, `every:` and `emits:`, each required and nothing
  else (`package.provider_invalid`). `call:` names one of the package's
  operations, public or not, that takes no `input:` and isn't `changes:
  true`, since a poll reads a feed that doesn't depend on the app's data
  (checking each of the app's records is the app's schedule calling the
  package), and whose `output:` is an `Object` read by the package's
  types, with no `decode:` of its own, as a queue's mapping is. `every:`
  is a duration of a minute or more: the tick runs once a minute.
- **What it emits.** `emits:` names an `Event` of the package that is
  `public`, since only the app's subscribers hear it, and gives no
  `wire_name:`, which marks one the provider sends; it is the package's
  own, as one its action emits is, so a package that receives webhooks
  needs no `wire_name:` or `occurred_at` on it. Each of its fields is a
  field of the answer of the same name and type, and only those count
  when the runtime asks whether the answer changed: a time the answer was
  fetched, left out of the event, never does. An event of fields the
  answer hasn't is refused at `emits:`.
- **The connection's interval.** `polls: { <name>: <interval> }` on a
  connection gives one of its package's polls another interval, a
  duration of a minute or more; a name the package's `polls:` hasn't is
  `connection.key_invalid` with the nearest, a shorter or unwritten
  interval `connection.value_invalid`, and `polls:` on a connection to a
  package with none `connection.key_invalid`.
- **Only while subscribed.** A poll runs for each connection to its
  package while one of the app's subscribers listens for its event, and
  is left out of the plan otherwise, so nothing is called and no state
  kept. Its events are kept where the package's webhooks would be, in
  `events: { store: }` or the only store of the app's records
  (`event.store_required` at the connection otherwise), with
  `jadpo_polls`, by `poll <connection>.<poll>`: when it is next due, the
  digest of the event it last kept and when that changed.
- **Running it.** On each tick, after the queues, one statement takes a
  poll that is due, or has never run, and moves it on by its interval, so
  of several instances only one calls it; one due later than a whole new
  interval away, since shortened, is due at once. The row keeps the
  interval it was last claimed under, `jadpo_polls.every_ms`, so only a
  poll claimed under another interval than the connection's now counts
  as shortened, and a second tick at the same moment, or an instance
  whose clock is behind, never calls the provider again within the
  interval. The column may be `NULL`, since a migration adds it to a
  table that may hold rows, and a row without it counts once as claimed
  under another interval. The operation is called
  as the runtime's own work, through the connection, with no request and
  no function of the package's, logged and retried as any call is. The
  event is made from the answer's fields, written with its codec and
  read back, and the SHA-256 of what is written compared with the last
  digest: the same keeps nothing; a change, or the first answer, keeps
  the event with a delivery for each subscriber and the digest in one
  transaction, the digest checked again there. A failed call keeps
  nothing, is reported by the tick under the poll's name, and is due
  again a minute later. A poll's first answer is emitted, so the app
  learns the current prices without waiting for them to change.
- **`max_concurrent:`** in `provider.jadpo` is a whole number of at
  least 1 (`package.provider_invalid`), and a connection's may only lower
  it (`connection.value_invalid` past it); a connection may give one when
  its package gives none. The cap is per app instance: the process's,
  shared by its worker threads, or an isolate's on Workers, and a test's
  host keeps its own. A provider call takes one of its connection's slots
  for each attempt and gives it back once answered, so none is held while
  a retry waits; a further call waits for one, a route's within its
  deadline. A subscriber's delivery calling out through a connection
  whose slots are all out is given back as claimed, its attempt not
  counted, due again a second later for the next sweep; its wait counts
  towards `within:`, as a release after a shutdown does. Polls' calls
  wait as any call does.
- **On Workers** the cap is counted per isolate: Workers runs many
  isolates, and a cap across them would need a Durable Object and a round
  trip per call. So the deploy report and `jadpo check` say "on Workers,
  per instance" (`jadpo check`'s "On Workers" lists each capped
  connection a deployable built for Workers reaches, and `jadpo build`'s
  `calls` line each Worker's); a provider that needs a hard global limit
  enforces it on its side, and its rate-limit answers already make calls
  back off.
- **Between an isolate's invocations** a slot one invocation gives back
  is handed to the call of another that has waited longest, which finds
  it on its own timer every 10 ms rather than being woken by the first,
  since Workers runs each invocation's work in its own context. A call
  holding a slot says so every second, and one not heard from for five
  seconds is forgotten, as is a waiter that stopped looking, so an
  invocation Workers ended mid-call never keeps a slot. Natively the
  process's waiters are woken by the call that gives a slot back.
- **The surface.** A poll's event is one of the package's public types
  and in its surface as any is; the polls themselves and
  `max_concurrent:` join the provider's entry once the surface tells an
  addition inside an entry from a breaking change, since any change to
  the provider's entry counts as breaking.
