# La pasada

Qué hace una pasada de ghchronicle por la API de GitHub, en qué orden, y por qué cada familia de métricas tiene su propia cadencia.

Source: https://docs.jmrp.io/ghchronicle/es/how/

Una pasada es un recorrido por cada familia cuyo intervalo ya ha vencido, salvo
que el servicio en marcha arranca una familia de seis horas o más en cada
pasada, más solo donde una no mantendría todas las cadencias, y deja las demás
que tocan para los tics siguientes: [las familias
lentas se turnan](https://docs.jmrp.io/ghchronicle/es/configuration/cadences/#las-familias-lentas-se-turnan).
Es un incremento, no una reconstrucción: pide lo poco que puede haber cambiado
desde la vez anterior, escribe lo obtenido en todos los almacenes configurados
y anota cuándo corrió cada familia.

## La forma de una pasada

```mermaid
%% Generated by site/scripts/gen-figures.mjs from internal/config/config.go and internal/run/runner.go
flowchart TD
    T["Temporizador"] --> D["Descubrir repositorios<br/>(se rehace cada hora)"]
    D --> A["Familias de cuenta<br/>account, totals, ratelimit, events, notifs,<br/>billing, profile, outbound, history,<br/>achievements, keys"]
    D --> R["Familias por repositorio<br/>traffic, repo, branches, stars, issues,<br/>issueevents, actions, artifacts, security,<br/>stats, discussions, commits, activity,<br/>analyses, forks, planning, joblogs, settings,<br/>rulesets, inventory, deployments, policyfiles,<br/>deps"]
    A --> B{"¿Presupuesto por<br/>encima de la reserva?"}
    R --> B
    B -- "no" --> S["Saltar la familia<br/>y avisar"]
    B -- "sí" --> C["Recoger"]
    C --> P["Puntos fechados"]
    P --> H["Destinos que conservan la fecha<br/>InfluxDB, fichero, stdout, Telegraf, Graphite,<br/>PostgreSQL a fichero, PostgreSQL,<br/>Elasticsearch"]
    P --> L["Loki<br/>las veintidós representaciones de evento"]
    P --> RD["Reductor<br/>valores actuales"]
    RD --> G["Prometheus<br/>OTLP, cuando raw: false"]
    C --> M["Marcar la familia como hecha<br/>en el fichero de estado"]
```

Dos cosas de ese diagrama son todo el diseño. Cada colector produce puntos
_fechados_, y los almacenes que no pueden sostener una fecha los reciben ya
reducidos a valores actuales, por el mismo reductor, antes de ver los datos.
Ese es el tema de [la fecha del punto](https://docs.jmrp.io/ghchronicle/es/how/dating/).

## Por qué familias y no un intervalo único

Las superficies se mueven a velocidades muy distintas. Las ejecuciones de
workflows terminan cada pocos minutos en una cuenta activa. Las etiquetas y los
hitos se editan unas pocas veces por semana. La lista de forks cambia unas pocas
veces al año. Un único intervalo para todas o malgastaría el límite de
peticiones en las lentas o perdería las rápidas, así que cada familia lleva la
suya.

| Familia        | Por omisión | Recoge                                                                                                            |
| -------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- |
| `actions`      | 15m         | Ejecuciones de workflows, jobs, pasos, la caché de Actions                                                        |
| `activity`     | 15m         | El log de actividad del repositorio, donde queda un force push                                                    |
| `events`       | 15m         | El feed de eventos de la cuenta, que guarda solo los últimos 300                                                  |
| `notifs`       | 15m         | La bandeja de notificaciones                                                                                      |
| `ratelimit`    | 15m         | Lo que le queda por gastar al colector, en cada presupuesto                                                       |
| `deployments`  | 30m         | Despliegues y sus entornos, en lote sobre todos los repositorios                                                  |
| `account`      | 1h          | Perfil, calendario de contribuciones, totales                                                                     |
| `achievements` | 1h          | Los distintivos del perfil, y lo que falta para el siguiente nivel                                                |
| `analyses`     | 1h          | Análisis de code scanning, que GitHub poda                                                                        |
| `artifacts`    | 1h          | Artefactos y su caducidad                                                                                         |
| `billing`      | 1h          | Uso por día, producto, SKU y repositorio                                                                          |
| `commits`      | 1h          | Líneas cambiadas y estado de la firma, por commit                                                                 |
| `discussions`  | 1h          | La mitad de foro de un repositorio                                                                                |
| `issueevents`  | 1h          | La línea de tiempo de lo que se movió: etiquetas, asignaciones, transiciones                                      |
| `issues`       | 1h          | Pull requests, issues y revisiones, uno a uno                                                                     |
| `outbound`     | 1h          | Estrellas dadas, y trabajo en repositorios ajenos                                                                 |
| `repo`         | 1h          | Estrellas, forks, lenguajes, temas, releases, rulesets                                                            |
| `security`     | 1h          | Alertas de Dependabot y de code scanning                                                                          |
| `stars`        | 1h          | Estrellas por día de cada repositorio, y el recorrido de la lista de estrellas una vez, luego las cien más nuevas |
| `totals`       | 1h          | Las cifras de siempre, preguntadas a GitHub en vez de sumadas aquí                                                |
| `planning`     | 6h          | Etiquetas e hitos                                                                                                 |
| `settings`     | 6h          | Webhooks y sus entregas, entornos, claves de despliegue                                                           |
| `traffic`      | 6h          | La ventana entera de 14 días, reescrita                                                                           |
| `forks`        | 12h         | Quién hizo fork, y cuándo                                                                                         |
| `profile`      | 12h         | Paquetes, gists, cuentas sociales                                                                                 |
| `stats`        | 12h         | Commits por semana, el punch card, las definiciones de workflow                                                   |
| `branches`     | 24h         | Qué ramas siguen vivas y cuánto hace que no se toca cada punta                                                    |
| `inventory`    | 24h         | Qué puede hacer el token del propio workflow, los dos almacenes de secretos, el code scanning por omisión         |
| `keys`         | 24h         | Las claves SSH y GPG de la cuenta, y cuándo caduca cada una                                                       |
| `policyfiles`  | 24h         | SECURITY.md, CODEOWNERS, dependabot.yml y FUNDING.yml                                                             |
| `rulesets`     | 24h         | Cada versión del historial de cada ruleset                                                                        |
| `deps`         | apagada     | El SBOM de dependencias de cada repositorio, y lo que cambió                                                      |
| `history`      | apagada     | El calendario de contribuciones de cada año, el actual incluido                                                   |
| `joblogs`      | apagada     | La cola del log de cada job fallido                                                                               |

Poner cualquiera a `0` la apaga por completo. Ver
[cadencias](https://docs.jmrp.io/ghchronicle/es/configuration/cadences/).

## El descubrimiento

La lista de repositorios se rehace una vez por hora, en la primera pasada que
la encuentra con una hora de antigüedad, con medio tic de margen: una pasada
lee su reloj unos milisegundos antes o después de la hora, y sin ese margen un
repositorio creado entre medias esperaba un tic más. Los repositorios se crean
pocas veces y listarlos cuesta una página por cada cien, así que cualquier cosa
más corta gasta cuota para no aprender nada. Los forks y
los archivados quedan fuera por omisión, por la razón que se expone en
[objetivos](https://docs.jmrp.io/ghchronicle/es/configuration/targets/).

## El fallo es por repositorio, no por pasada

Una familia que falla en un repositorio se registra y se salta; la pasada
continúa. Esto importa más de lo que parece, porque aquí "fallo" suele ser una
función apagada: de cincuenta repositorios, la mayoría tiene Dependabot
desactivado, y cada uno responde 403. Tratarlo como error perdería los otros
cuarenta y nueve.

Hay una excepción, y es deliberada. Una familia en la que fallaron _todos_ los
repositorios no se marca como hecha. Marcarla escondería la caída hasta la
siguiente cadencia, que para las familias de doce horas es medio día.

```text
level=WARN msg="family failed everywhere, not marking it as run" family=security
```

## El fichero de estado

`state_file` guarda [nueve cosas](https://docs.jmrp.io/ghchronicle/es/configuration/#state_file), y
las dos por las que se juzga una pasada son cuándo corrió cada familia por
última vez y cuándo se vio cada repositorio por primera vez.

Lo segundo es lo que hace que el recorrido completo de la lista de stargazers
ocurra una vez y no en cada pasada, como hace `history_read` con el historial
diario de estrellas. Se escribe a través de un fichero temporal
y se renombra, así que una caída a mitad de escritura no puede dejar un estado
truncado que provocaría una recolección completa.

Junto a él el recolector guarda lo que hace barata una pasada y no lo que la
hace completa: la caché de ETags, las ejecuciones de workflow cuyos jobs ya se
escribieron, los rechazos y los tamaños de página de las pull requests, en
[`<nombre>-cache.bin`](https://docs.jmrp.io/ghchronicle/es/configuration/#la-caché-que-hay-a-su-lado).
Un reinicio lo vuelve a leer y empieza donde se paró el proceso anterior, no de
cero, y borrarlo cuesta una pasada a precio completo de cada familia y no pierde
nada.

> **Tres pasadas recogen todas las familias diga lo que diga el estado**
>
> Un exportador guarda sus muestras en memoria, así que un reinicio lo vacía y
> sigue vacío hasta que llega la cadencia de cada familia, lo que para las de
> doce horas es medio día de dashboard a cero. Pagar una pasada completa es el
> error más barato, así que cuando el servicio alimenta el [exportador de
> Prometheus](https://docs.jmrp.io/ghchronicle/es/sinks/prometheus/#cómo-consultarlo) su primera
> pasada tras el arranque corre todas las familias activas diga lo que diga el
> fichero de estado, salvo que `no_prime` esté puesto. Solo anota como hechas
> las familias a las que les tocaba, así que las demás conservan el turno que
> tenían. `-once` no arranca exportador y no hace esa pasada completa. Un
> [relleno histórico](https://docs.jmrp.io/ghchronicle/es/how/backfill/) también corre todas las
> familias, porque llegar tan atrás como permita GitHub es justo lo que se le
> pide, y también una pasada que dibuja [una tarjeta](https://docs.jmrp.io/ghchronicle/es/card/),
> porque cada número de la tarjeta sale de esa única pasada y una familia
> saltada por no tocarle sería un cero en la imagen.

## El freno

Antes de cada familia el colector mira los tres cubos de límite de los que
realmente gasta. Si alguno está en su reserva o por debajo y la ventana aún no
se ha reiniciado, la familia se salta y se registra un aviso en vez de gastar
el presupuesto hasta la última llamada. Ver
[límites de la API](https://docs.jmrp.io/ghchronicle/es/api/).

Un [relleno histórico](https://docs.jmrp.io/ghchronicle/es/how/backfill/) es la intención
contraria: espera a que la ventana se renueve en vez de saltarse la familia.
