# Cadencias

Las tres capas del bloque every, la cadencia interna de cada familia, el aviso cuando una configuración la acelera, y el heartbeat.

Source: https://docs.jmrp.io/ghchronicle/es/configuration/cadences/

Cada familia de métricas de GitHub se recoge con su propio intervalo, y el bloque `every` es donde se cambian esos intervalos.

```yaml
every:
  default: 15m
  groups:
    ci: 1m
    feeds: 10m
  families:
    actions: 30s
```

## Las tres capas

Gana la más específica. La entrada de una familia gana a la de su grupo, la de
un grupo gana a `default`, y `default` gana al valor interno. Lo que se omite
cae a la capa de abajo, así que un fichero que solo tenga `families:` se
comporta exactamente como siempre. El valor es una duración de Go: `30s`,
`15m`, `2h`, `36h`.

| Capa             | Alcanza                                    | Se escribe              |
| ---------------- | ------------------------------------------ | ----------------------- |
| `every.families` | una familia                                | `families: {keys: 24h}` |
| `every.groups`   | todas las familias de un grupo             | `groups: {ci: 1m}`      |
| `every.default`  | todas las que no nombren las dos de arriba | `default: 15m`          |
| la tabla interna | todas las que no nombre ninguna capa       | nada                    |

Son tres claves de un bloque anidado y no tres claves planas porque un mapa
plano no puede con este vocabulario. `security` y `account` son a la vez nombre
de familia **y** nombre de grupo, así que `every: {security: 1m}` tiene dos
lecturas y ninguna forma de elegir entre ellas. Bajo `families:` la palabra es
la familia; bajo `groups:` es el grupo; ya no queda sitio donde plantear la
duda. Ese mismo anidamiento es lo que hace seguras las palabras `default` y
`groups`: son campos de una estructura fija, y una clave desconocida se rechaza
al arrancar, así que ninguna familia puede tapar una capa ni ninguna capa una
familia.

> **Las dos capas amplias nunca encienden una familia**
>
> `default` y `groups` no alcanzan a una familia cuya cadencia interna es `0`.
> `deps`, `history` y `joblogs` vienen apagadas y se activan nombrándolas bajo
> `families:` y de ninguna otra forma. Solo `deps` son 1,8 MB de SBOM por
> repositorio, y un `default` escrito para acelerar las familias rápidas no debe
> encender además tres familias que nunca mencionaste. Es la misma regla que ya
> sigue `groups`.

Del revés sale la forma más corta de recoger poco: apagarlo todo con `default`
y luego nombrar lo que quieres de vuelta.

```yaml
every:
  default: 0
  families:
    traffic: 6h
    actions: 15m
```

## Cada familia, su grupo y su cadencia interna

Los valores internos no son números redondos elegidos por prolijidad. Salieron
de una auditoría con sus costes, y el motivo de cada uno vive junto al número en
el código. Esa es la fuente, y esta tabla está anclada a ella: un test de
`internal/config` compara cada grupo, cada duración y cada motivo de aquí abajo
con el código en ambos sentidos, así que una familia que se añada, se mueva o
cambie de coste sin que esta tabla la siga rompe la compilación.
`ghchronicle -groups` imprime esa misma pertenencia.

| Grupo       | Familia        | Por omisión | Motivo                                                                                                                                                                                                                                                                                                                                                                                                   |
| ----------- | -------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account`   | `account`      | `1h`        | el calendario de contribuciones se mueve con cada contribución y los contadores del perfil con cada seguimiento y cada estrella, y una pasada es un punto de GraphQL y siete peticiones REST, de las que solo se cobra la del perfil: GitHub casi nunca la respondió con un 304, una vez en 48 lecturas condicionales, y los seis listados de paquetes lo responden hasta que cambia un paquete                                                     |
| `account`   | `achievements` | `1h`        | las insignias de la página pública del perfil, que ninguna API lista, y lo que le falta a cada una con niveles para el siguiente; una pasada transfiere la página, 36 KB y fuera del presupuesto, y las pull requests fusionadas desde el día anterior por dos puntos de GraphQL, así que un nivel alcanzado por la tarde se ve por la tarde, y el historial entero se recorre una vez por semana, 24 MB |
| `account`   | `billing`      | `1h`        | el mes en curso se mueve mientras corre la integración continua: había cambiado en cada una de las 46 lecturas cada seis horas medidas, y una pasada son dos peticiones, ese mes y el anterior, de las que solo se cobra la primera                                                                                                                                                                      |
| `account`   | `history`      | `0`         | apagada hasta que se la nombre: recorre cada año pasado y lo que va de este, y las filas son idempotentes                                                                                                                                                                                                                                                                                                |
| `account`   | `keys`         | `24h`       | una clave SSH o GPG cambia cuando alguien la cambia, y lo que importa es su fecha de caducidad, no la hora en que se vio                                                                                                                                                                                                                                                                                 |
| `account`   | `outbound`     | `1h`        | las estrellas dadas, el trabajo en repositorios ajenos y las respuestas aceptadas allí, por unos nueve puntos de GraphQL por pasada, así que la hora no cuesta nada que se note y una respuesta aceptada esta tarde está en el panel esta tarde                                                                                                                                                          |
| `account`   | `profile`      | `12h`       | paquetes, gists y cuentas sociales, todo ello editado a mano                                                                                                                                                                                                                                                                                                                                             |
| `account`   | `totals`       | `1h`        | los números de toda la vida se mueven con cada estrella, fork, fusión y push, y una pasada es un punto de GraphQL por la cuenta, uno por cada diez repositorios y uno por cada veinticinco archivados que se dejan fuera, y una petición del presupuesto de búsqueda                                                                                                                                     |
| `audience`  | `forks`        | `12h`       | la lista entera cabe en una página, y un fork es un suceso raro                                                                                                                                                                                                                                                                                                                                          |
| `audience`  | `stars`        | `1h`        | una estrella puede llegar a cualquier hora, y tras el único recorrido completo de la lista una pasada es un punto de GraphQL por cada diez repositorios para las cien más nuevas y una petición condicional por repositorio para el historial diario, que respondió un 304 gratis a 184 de las 185 medidas                                                                                               |
| `audience`  | `traffic`      | `6h`        | la ventana de catorce días se reescribe entera cada vez, así que una pasada perdida se repara en la siguiente                                                                                                                                                                                                                                                                                            |
| `ci`        | `actions`      | `15m`       | una ejecución de workflow termina en minutos, y su tiempo en cola solo merece verse mientras ocurre                                                                                                                                                                                                                                                                                                      |
| `ci`        | `artifacts`    | `1h`        | los artefactos aparecen con la ejecución que los hizo y caducan en escala de días                                                                                                                                                                                                                                                                                                                        |
| `ci`        | `deployments`  | `30m`       | es la superficie que lee un dashboard de entrega, y la página más nueva es un punto de GraphQL por cada cinco repositorios, que trajo hasta cuatro filas nuevas por pasada medida, así que la media hora cuesta poco                                                                                                                                                                                     |
| `ci`        | `joblogs`      | `0`         | apagada hasta que se la nombre: es texto y no una medida, cuesta una petición por fallo, y solo tiene sentido con un almacén de logs conectado                                                                                                                                                                                                                                                           |
| `collector` | `ratelimit`    | `15m`       | es gratis, y merece la pena tenerla a la resolución de la familia más rápida                                                                                                                                                                                                                                                                                                                             |
| `feeds`     | `activity`     | `15m`       | el log del repositorio guarda cien entradas, que cubrieron veintiséis horas en el repositorio más activo medido, y una pasada son una o dos peticiones condicionales por repositorio, que respondieron un 304 gratis a 2.320 de las 2.356 medidas                                                                                                                                                        |
| `feeds`     | `events`       | `15m`       | una petición core por pasada, y el feed se había movido en 43 de las 46 medias horas medidas, así que el cuarto de hora es lo pronto que un evento llega al panel; la ventana, los últimos trescientos eventos de los últimos treinta días, es mucho más ancha que eso                                                                                                                                   |
| `feeds`     | `notifs`       | `15m`       | una petición core por pasada y veinte una vez al día por la bandeja entera, y un hilo muestra solo su último movimiento, así que un movimiento que otro adelanta antes de la siguiente lectura no se ve nunca; la mitad de las pasadas medidas trajo una fila nueva                                                                                                                                      |
| `repos`     | `branches`     | `24h`       | las ramas se crean y se borran todo el día, pero lo que responde la fila es cuáles están rancias ahora mismo, que es una pregunta diaria                                                                                                                                                                                                                                                                 |
| `repos`     | `deps`         | `0`         | apagada hasta que se la nombre: el SBOM son 1,8 MB por repositorio y tiene su propio presupuesto de cien por minuto                                                                                                                                                                                                                                                                                      |
| `repos`     | `inventory`    | `24h`       | cuatro peticiones core por repositorio, para ajustes que cambian solo cuando alguien los cambia                                                                                                                                                                                                                                                                                                          |
| `repos`     | `policyfiles`  | `24h`       | SECURITY.md, CODEOWNERS, dependabot.yml y FUNDING.yml se mueven una vez por trimestre                                                                                                                                                                                                                                                                                                                    |
| `repos`     | `repo`         | `1h`        | estrellas, forks, lenguajes y temas se mueven despacio, y una pasada son tres peticiones REST por repositorio, casi todas un 304 gratis, y dos puntos de GraphQL por cada diez repositorios                                                                                                                                                                                                              |
| `repos`     | `rulesets`     | `24h`       | un ruleset se edita unas pocas veces al año y cada versión conserva su propia fecha; las dos peticiones responden un 304 gratis hasta que alguien edita uno, también en la primera pasada tras un reinicio, porque la caché de ETag se guarda junto al fichero de estado                                                                                                                                 |
| `repos`     | `settings`     | `6h`        | webhooks, rulesets, entornos y claves de despliegue cambian solo cuando alguien los cambia                                                                                                                                                                                                                                                                                                               |
| `security`  | `analyses`     | `1h`        | GitHub poda los análisis de code scanning y cada push analizado añade alguno, y una pasada es una petición condicional por repositorio con code scanning, tres de ellas cobradas en la pasada mediana medida, con las negativas del resto recordadas un día                                                                                                                                              |
| `security`  | `security`     | `1h`        | una alerta es algo sobre lo que actuar hoy, y la lista de abiertas es corta                                                                                                                                                                                                                                                                                                                              |
| `work`      | `commits`      | `1h`        | un punto de GraphQL por cada repositorio cuya rama por defecto tiene un commit de las dos últimas cadencias, que tenían 38 de 999 respuestas medidas, y uno por cada veinticinco repositorios, compartido con issues e issueevents, para preguntar cuáles, así que la hora es lo pronto que se ve en el panel un push                                                                                    |
| `work`      | `discussions`  | `1h`        | una discusión se responde en horas o días y pocos repositorios tienen foro, y una pasada son dos puntos de GraphQL por cada uno que lo tiene y nada por el resto                                                                                                                                                                                                                                         |
| `work`      | `issueevents`  | `1h`        | la cronología de lo que se movió en dos cadencias, un punto GraphQL por cada repositorio donde se movió una issue o una pull request y nada por los demás, así que la hora es lo pronto que merece verse una transición                                                                                                                                                                                  |
| `work`      | `issues`       | `1h`        | uno o dos puntos GraphQL por cada repositorio cuyas issues o pull requests se movieron en dos cadencias, nada por los demás, y una vez al día un punto o más por sus elementos abiertos y hasta cuatro por página de lo que se movió en el día, así que la hora es lo pronto que se ve en el panel una revisión o una fusión                                                                             |
| `work`      | `planning`     | `6h`        | etiquetas e hitos se editan a mano, unas pocas veces por semana como mucho                                                                                                                                                                                                                                                                                                                               |
| `work`      | `stats`        | `12h`       | GitHub las recalcula despacio de todas formas, así que preguntar más a menudo devuelve los mismos números                                                                                                                                                                                                                                                                                                |

## El aviso cuando una cadencia va más rápido de lo que el valor merece

Una capa amplia es una forma barata de ralentizarlo todo y una forma cara de
acelerarlo todo. `default: 15m` le pide a GitHub las claves SSH de la cuenta
noventa y seis veces al día para un valor que cambia dos veces al año, y una
cadencia de grupo hace lo mismo un nivel más abajo: `work` contiene familias que
la auditoría midió en `1h` y en `12h`, así que un número ahí es un número sobre
seis respuestas distintas.

Por eso cada familia que una configuración recoge **cuatro veces más a menudo o
más** que su cadencia interna se nombra al arrancar, con la clave que la fijó,
los dos números, y el motivo de que ese número sea el que es:

```text
level=WARN msg="every.default sets keys to 15m against a built-in 24h, 96 times
  more often: an SSH or GPG key changes when somebody changes it, and what
  matters is its expiry date, not the hour it was noticed"
level=WARN msg="every.groups.work sets stats to 1h against a built-in 12h, 12
  times more often: GitHub recomputes these slowly anyway, so asking more often
  returns the same numbers"
```

Es un aviso y nunca un rechazo, y es siempre por familia, nunca por grupo. Una
línea que dijese "el grupo `work` va demasiado rápido" no nombraría nada sobre
lo que actuar: el motivo de que una cadencia sea la que es pertenece a la
familia, así que el aviso también.

Cuatro es el umbral porque los valores internos están sobre una escalera, `15m`
`30m` `1h` `2h` `6h` `12h` `24h`, y el mayor salto entre dos peldaños vecinos es
de tres, de `2h` a `6h`. Ninguna familia viene a `2h` desde que `discussions`
pasó a la hora, pero el peldaño sigue siendo donde cae un paso abajo desde `6h`.
Cuatro es por tanto el menor factor que ningún peldaño suelto alcanza. Bajar un
peldaño es un ajuste deliberado de quien está mirando esa familia y se queda
callado; cuatro o más solo puede ser una capa amplia cayendo donde nunca se la
eligió, o un número escrito sin leer esta tabla.

Nada avisa por ir más despacio. Esto va de desperdicio, no de gusto.

## Grupos: recoger menos que todo

`every` fija cada cuánto corre una familia. `groups` fija qué familias existen
de verdad para esta instalación. Si se omite la clave, recoge para todos los
grupos, que es el valor por defecto y lo que "cada métrica que GitHub expone"
ha significado siempre.

```yaml
groups: [audience, account, repos, security]
```

Nombrar cualquier grupo apaga los demás: sus familias no se piden nunca y sus
medidas no se escriben nunca. `ghchronicle -groups` imprime la lista, que es:

| Grupo       | Familias                                                                                 |
| ----------- | ---------------------------------------------------------------------------------------- |
| `audience`  | `forks`, `stars`, `traffic`                                                              |
| `account`   | `account`, `achievements`, `billing`, `history`, `keys`, `outbound`, `profile`, `totals` |
| `repos`     | `branches`, `deps`, `inventory`, `policyfiles`, `repo`, `rulesets`, `settings`           |
| `work`      | `commits`, `discussions`, `issueevents`, `issues`, `planning`, `stats`                   |
| `ci`        | `actions`, `artifacts`, `deployments`, `joblogs`                                         |
| `security`  | `analyses`, `security`                                                                   |
| `feeds`     | `activity`, `events`, `notifs`                                                           |
| `collector` | `ratelimit`                                                                              |

El `groups` de primer nivel y `every.groups` son dos preguntas distintas sobre
los mismos ocho nombres: la primera decide si un grupo llega a recogerse, la
segunda cada cuánto. Una cadencia bajo `every.groups` para un grupo que el
`groups` de primer nivel deja fuera es legal y avisa, en lugar de fallar:
estrechar una instalación no debería obligar además a podar un bloque `every`
que ajustaste el año pasado.

Los dos ejes tienen que decir que sí, y solo uno de ellos puede decir que no.
Un grupo que no se nombra apaga sus familias diga lo que diga `every`, y
nombrar un grupo nunca resucita una familia cuya cadencia es cero. En eso
consiste que una familia venga apagada mientras el valor por defecto es todo.

Escribir `groups: []` se rechaza en lugar de leerse como "no recojas nada":
omitir la clave es la forma de pedirlo todo, así que una lista vacía solo puede
ser un error.

Lo que GitHub borra no se puede recuperar después, se haga lo que se haga, y
está en tres grupos distintos: el feed de eventos, que guarda trescientos
eventos y ninguno de hace más de treinta días, y la bandeja, que conserva las
notificaciones tres meses salvo las guardadas (`feeds`); la ventana de catorce
días del tráfico (`audience`); y los logs de los jobs, que se borran al acabar
el periodo de retención del repositorio, noventa días por omisión, y con ellos,
desde el 1 de octubre de 2026, las propias ejecuciones de workflows (`ci`). Lo
que GitHub borró antes de que ghchronicle lo leyera no existe en ninguna parte.
Todo lo demás se puede rellenar luego con `-backfill`.

> **Los paneles de un grupo apagado darán error**
>
> Una medida que no se escribe nunca no existe como tabla, e InfluxDB 3 responde
> con un error, y no con cero filas, a una consulta que nombra una tabla que no
> tiene. Los dashboards se generan una vez, para el conjunto completo de
> métricas, así que cada panel alimentado por un grupo apagado muestra ese
> error. Es lo esperado. Son una demostración de lo que el colector sabe
> dibujar, no una vista que se reorganice según tu configuración. PostgreSQL y
> Elasticsearch se comportan igual; Prometheus y Graphite no tienen esquema que
> echar en falta, así que esos mismos paneles leen No data.

Las secciones de los dashboards tampoco están alineadas con los grupos, así que
un grupo apagado vacía algunos paneles de varias secciones en lugar de una
sección entera. `Stars and forks` lee `gh_repo` de `repos` además de `gh_star`
y `gh_star_day` de `audience`; `Code` lee `gh_workflow_run` de `ci` y
`gh_repo_activity` de `feeds` junto a sus propios commits; `Inventory` lee de
`repos` y de `account`, y sus paneles de licencias leen `gh_dependency_license`,
que es la familia `deps` y viene apagada se seleccione `repos` o no.

## Qué significa `0`

`0` apaga todas las familias que alcance la capa en la que está escrito. No es
"tan a menudo como se pueda" ni "usa el valor por defecto": esas familias no
corren nunca, no escriben nada y no cuestan nada.

```yaml
every:
  families:
    billing: 0 # nada de datos de facturación
    artifacts: 0 # nada de filas de artefactos
```

Tres familias vienen apagadas y se activan dándoles cualquier duración, bajo
`families:` y en ningún otro sitio:

- **`joblogs`** es la cola del log de cada job fallido. Es texto y no una
  medida, cuesta una petición por fallo, y solo tiene sentido con un almacén de
  logs conectado. El destino de InfluxDB lo excluye por omisión.
- **`history`** recorre el calendario de contribuciones de cada año pasado, un
  punto de GraphQL por año, hasta el día en que se creó la cuenta, y el año en
  curso de enero a hoy. Los años pasados no cambian y las filas son
  idempotentes; la fila del año en curso es una instantánea marcada `partial`,
  así que una cadencia diaria la mantiene al día y una única pasada la deja
  congelada en el día en que corrió.
- **`deps`** es el grafo de dependencias. El SBOM son 1,8 MB por repositorio y
  tiene su propio presupuesto de cien por minuto.

Una configuración que no deja nada activado lo dice al arrancar, en vez de
correr un bucle vacío en silencio.

> **Un nombre desconocido es fatal al arrancar**
>
> ```text
> every.families.trafic: unknown collector (known: account, achievements, actions, activity, ...)
> every.families.feeds: "feeds" is a group, not a family; every.groups.feeds is where a whole group's cadence lives
> every.groups.actions: "actions" is a family, not a group; it is in group "ci", and every.families.actions is where its cadence lives
> ```
>
> Cada nombre se comprueba contra los colectores conocidos y los grupos conocidos
> antes de la primera llamada a la API. De lo contrario, una errata significaría
> una familia corriendo en silencio con su valor por defecto para siempre, y a un
> nombre escrito en la capa equivocada se le dice en qué capa iba.

## `heartbeat`: el tic del bucle, que no es una cadencia

`heartbeat` fuerza cada cuánto despierta el bucle de pasadas a preguntar qué
familias tocan. No le da intervalo a ninguna familia, no se compara con nada de
la tabla de arriba, y no se gana ninguno de los avisos de esta página.

```yaml
heartbeat: 15s
```

Si se omite, el bucle late a la cadencia más corta configurada, entre un minuto
y una hora, que es lo que quiere una instalación real: una familia que corre
cada cuarto de hora no se retrasa por otra que corre cada doce horas. El techo
importa en una configuración cuyas cadencias son todas más lentas que una hora:
la búsqueda de la más corta arranca en una hora, así que el bucle sigue
despertando cada hora y no encuentra nada que hacer. Se pone cuando
lo que quieres bajo control es el bucle en sí, que casi siempre es una pasada de
prueba. Es la única forma de hacer girar el bucle más rápido que ese suelo de un
minuto, porque el suelo existe para sobrevivir a una cadencia mal tecleada y un
heartbeat explícito no lo es.

Una familia toca en el primer tic en que su cadencia ha pasado, con medio tic
de margen. El bucle lee el reloj unos milisegundos después de despertar, en una
cantidad que cambia de un tic a otro, y sin ese margen una familia cuya cadencia
es un número entero de tics esperaba un tic de más la mitad de las veces: hasta
la 2.6.0, que trajo la corrección escrita para la 2.5.2, las familias de cuarto
de hora corrían cada 24 minutos de media y las horarias cada 69. Medio tic absorbe eso y nunca deja correr a una familia un
tic antes de tiempo.

Un heartbeat **más largo** que la cadencia más corta frena a esa familia, y el
arranque lo dice:

```text
level=WARN msg="heartbeat is 1h and the shortest cadence is 15m (actions), so
  no family can run more often than every 1h"
```

## Las familias lentas se turnan

El servicio en marcha arranca una familia de cadencia de seis horas o más en
cada pasada, o las menos que aún mantienen todas las cadencias cuando una no
basta (más abajo), y deja cualquier otra que toque para el tic siguiente.

Las familias que corren en la misma pasada quedan marcadas con el mismo instante
en el fichero de estado, así que vuelven a tocar juntas en cada cadencia, para
siempre. Un grupo así se forma siempre que se marcan muchas familias a la vez:
una instalación nueva, un `-once` o un backfill antes del servicio, una familia
que se enciende. Hasta la 2.6.0 las ocho familias diarias de la cuenta de
producción corrían en la primera pasada del día UTC, todos los días, y sus cinco
familias de doce horas juntas en una pasada propia. El 2026-09-26 la pasada
diaria gastó 294 peticiones `core` facturables y 24,3 MB frente a 17,5
peticiones de la pasada mediana, y con la de doce horas 45 minutos después, su
hora fue ocho veces la hora mediana en peticiones `core`. Las puso ahí el
calendario, no el trabajo.

La familia que arranca es la que lleva más tiempo tocando, contado desde lo más
reciente entre su última pasada y la última vez que el proceso la dejó arrancar.
La segunda mitad importa para una familia cuyas pasadas fallan todas: una pasada
fallida no se marca como hecha, así que por su última pasada sola sería la
familia más atrasada en cada pasada y se quedaría con todos los turnos. Contada
así, se pone detrás de las demás.

Una familia espera a cada una de las otras como mucho una vez, así que la peor
espera es un tic por cada otra familia lenta, y solo la última de un grupo recién
formado espera tanto. Con el tic de cuarto de hora que dan las cadencias internas:

| Calendario                                 | Familias de 6h o más | Espera más larga |
| ------------------------------------------ | -------------------- | ---------------- |
| las cadencias internas                     | 11                   | 10 tics, `2h30m` |
| con `deps` e `history` a `24h`             | 13                   | 12 tics, `3h`    |
| con `deps`, `history` y `joblogs` a un día | 14                   | 13 tics, `3h15m` |

Esa espera se paga una vez. En cuanto dos familias han corrido en pasadas
distintas, tocan en pasadas distintas y nada vuelve a juntarlas: una semana
simulada de las trece, todas pendientes en una misma pasada al principio, tuvo
cada familia arrancando exactamente una cadencia después de su arranque anterior
desde el primer día.

El log dice qué familia arrancó y cuáles esperan, así que una familia lenta que
falta en una pasada en la que tocaba queda explicada:

```text
level=INFO msg="slow families due together take turns" starting=planning
  waiting=settings,traffic,forks,profile,stats,branches,inventory,keys,policyfiles,rulesets
```

Una por pasada vale mientras puede cumplir todas las cadencias, y una
configuración con un tic más largo puede tener más familias lentas que eso.
`every.default: 6h` late cada hora, porque nada en ella corre más a menudo, con
treinta y una familias a seis horas, y una por pasada arrancaría cada una cada
treinta y una horas. Ahí una pasada arranca las menos que caben, seis, y ninguna
familia espera tanto como su cadencia.

Solo el servicio se turna. `-once` corre todas las familias que tocan, porque no
tiene un tic siguiente al que dejar ninguna: lanzado una vez al día por un
planificador, la dejaría para el día siguiente. Un backfill, una tarjeta y la
primera pasada de un servicio que ceba el
[exportador de Prometheus](https://docs.jmrp.io/ghchronicle/es/sinks/prometheus/#cómo-consultarlo)
corren todas las familias diga lo que diga el fichero de estado. La pasada
cebada anota después solo las familias a las que les tocaba, así que un
reinicio no vuelve a poner las demás en un mismo instante, y cada una conserva
el turno que tenía.

## Hacerlas más lentas

Si el presupuesto que se queda corto es `core`, alarga `artifacts` y después
`actions`, los dos mayores costes REST, que crecen con lo activos que estén los
repositorios. Si es `graphql`, alarga `issues`, `issueevents` y `commits`, que
leen solo los repositorios donde algo se movió y por eso cuestan más cuantos
más se muevan. No son los únicos que siguen a la actividad y no al tamaño:
`activity` solo se cobra por un repositorio cuyo log se movió, y
`outbound` cuesta un punto más por cada centenar adicional de lo que lee. Ver
[qué crece con la actividad, no con el
tamaño](https://docs.jmrp.io/ghchronicle/es/api/cost/#qué-crece-con-la-actividad-no-con-el-tamaño).

El síntoma de unas cadencias demasiado rápidas es un aviso en cada pasada:

```text
level=WARN msg="rate limit reserve reached, family skipped" family=actions
```

## Cadencia no es resolución

Alargar una cadencia no hace más gruesa la historia, porque los puntos se fechan
por la cosa que ocurrió y no por la pasada. Recoger las ejecuciones de workflows
cada hora en vez de cada quince minutos sigue registrando cada ejecución en el
segundo en que terminó. Lo que arriesga una cadencia larga es perder una ventana
entera: `events` lee los últimos trescientos eventos, `notifs` solo el último
movimiento de cada hilo y `activity` las últimas cien entradas del log de cada
repositorio, así que lo que salga de una de ellas entre dos pasadas se pierde.
