Tots els articles

Més enllà de la previsió: agregant alertes meteorològiques oficials per a la seguretat a l'aire lliure

Publicat 18 d’oct. del 20245 min de lectura
  • MeteoTrail
  • Enginyeria
  • Muntanya

Idees clau

  • El feed Atom de Meteoalarm i el format CAP de cada país són incòmodes de consumir directament, així que els normalitzem una sola vegada per a una llista fixa de regions en lloc de deixar que cada producte els analitzi pel seu compte.
  • El servei falla tancat davant claus d'API absents (401 per defecte) i distingeix les fallades parcials (200 amb un array d'errors) de les caigudes totals de l'origen (503).
  • A diferència de MeteoTrail, això és infraestructura interna, només accessible amb clau d'API, sense interfície pública, i els productes que en depenen n'hereten les fallades en silenci.

Després de llançar MeteoTrail —el nostre producte de previsió a nivell de punt de control per a esdeveniments esportius a l'aire lliure— la major part de la nostra atenció va anar a la precisió del model: vent, precipitació i temperatura en un punt concret d'una muntanya, a una hora concreta. El que no vaig arribar a entendre del tot fins a Val d'Aran by UTMB és quant pes organitzatiu pot acabar carregant un programari un cop els directors de cursa el tenen al davant per prendre decisions d'anar o no anar. Aquella experiència va canviar com penso sobre qualsevol cosa que construïm que toqui decisions de seguretat, encara que sigui indirectament.

En els mesos següents, un problema més petit i aparentment sense relació reapareixia una vegada i una altra. Les alertes meteorològiques oficials del govern —del tipus «avís taronja per vent per a aquesta regió a partir de demà a les 06:00»— són públiques i genuïnament útils, però ningú havia construït res perquè les consumíssim de manera programàtica. Cada producte a qui li importava havia d'anar a buscar-les pel seu compte. Aquest article tracta sobre el petit servei intern que vam construir perquè això deixés de passar.

1. Dades públiques, mal pensades per consumir-se

Europa ja va resoldre, més o menys, el problema del descobriment per a les alertes meteorològiques. Meteoalarm agrega les alertes oficials de totes les agències meteorològiques nacionals del continent i les republica com un feed Atom, un per país. Això és genuïnament útil: no has de saber a quina de les trenta i escaig agències consultar. Però el mateix feed és un resum: títol, un codi de regió aproximat, un color de severitat, un enllaç cap a fora.

El contingut real d'una alerta —quin fenomen, quins llindars, quina finestra temporal, en quina àrea precisa— viu en un payload CAP (Common Alerting Protocol), publicat per l'agència de cada país en el seu propi dialecte de l'estàndard. Per a Espanya, això és AEMET. «Estàndard», en el món de les alertes, encara deixa molt marge per a peculiaritats pròpies de cada agència en codis d'àrea, vocabularis de severitat i estructura XML.

Consumir això correctament significa dues peticions, dos formats, i reconciliar tots dos en alguna cosa sobre la qual un producte pugui ramificar lògica. Està bé fer-ho una vegada. No està bé demanar-li a cada equip intern que ho faci pel seu compte.

2. Què vam construir exactament

Vam construir el Spain Meteo Alerts Aggregator: un petit servei FastAPI amb una única feina —obtenir, filtrar, normalitzar i cachejar—. No intenta cobrir tota Espanya. Funciona sobre una llista fixa de regions rellevants per als nostres productes de turisme a l'aire lliure i de muntanya: Lleida, Aran (Val d'Aran), i diverses zones costaneres de Mallorca —sud, tramuntana, interior, llevant i nord-nord-est—.

El pipeline per petició és senzill:

  1. Obtenir el feed Atom de Meteoalarm Espanya.
  2. Filtrar les entrades fins a les regions de la llista.
  3. Per a cada alerta que coincideixi, obtenir el payload CAP d'AEMET amb el detall real.
  4. Normalitzar tot —la forma del feed de Meteoalarm i la forma del CAP d'AEMET— en un únic esquema JSON estable.
  5. Agrupar el resultat per regió, i després per severitat (vermella, taronja, groga, desconeguda).
  6. Descartar de la resposta qualsevol regió que ara mateix no tingui cap alerta activa, perquè qui la consumeixi no hagi de filtrar soroll buit pel seu compte.

Una forma il·lustrativa i simplificada del que retorna GET /alerts:

{
  "generated_at": "2024-10-18T07:15:00Z",
  "regions": {
    "aran": {
      "red": [],
      "orange": [
        {
          "id": "aemet-es-es610-20241018-0001",
          "phenomenon": "wind",
          "onset": "2024-10-19T06:00:00Z",
          "expires": "2024-10-19T18:00:00Z",
          "headline": "Orange wind warning"
        }
      ],
      "yellow": [],
      "unknown": []
    },
    "mallorca-tramontana": {
      "red": [],
      "orange": [],
      "yellow": [
        { "id": "aemet-es-illesbalears-20241018-0007", "phenomenon": "rain" }
      ],
      "unknown": []
    }
  },
  "errors": []
}

Res d'exòtic. El valor no és en cap pas concret, és en haver fet la reconciliació una sola vegada, en un sol lloc, en lloc de sis vegades repartides en sis productes.

3. Fallar tancat, i fallar parcialment

Dues decisions de disseny aquí van venir directament de lliçons que no volia haver de tornar a aprendre.

Primera: l'autenticació. /alerts, /areas i /areas/{country} requereixen totes una capçalera X-API-Key. /health i / es queden oberts, perquè els monitors d'uptime necessiten algun lloc on trucar sense credencials. El que m'importa és què passa quan el servei no té cap clau d'API configurada —un desplegament mal configurat, un secret que falta, el que sigui—. Rebutja tota petició protegida amb 401. No cau a accés obert. Un servei sense claus configurades és un servei que es va oblidar de configurar claus, no un servei que pretén ser públic.

Segona, i més subtil: GET /alerts distingeix dos modes de fallada molt diferents. Si l'obtenció del feed de Meteoalarm falla completament, o el servei no pot construir un resum del qual es fiaria, retorna 503 —«no tinc res fiable que dir-te, no actuïs sobre aquesta resposta»—. Però si el feed va bé i la majoria de les regions es resolen correctament mentre que, diguem-ne, una consulta al CAP d'AEMET fa timeout, retorna 200 amb les regions que sí es van resoldre i un array errors no buit que descriu què no es va resoldre —«això és el que sé, i això és el que no, tu decideixes què fer amb el forat»—. Els productes que consumeixen això poden reaccionar de manera molt diferent a aquests dos casos.

4. Per què això no és MeteoTrail

Aquest servei mai no tindrà una landing page. No hi ha interfície, no hi ha alta pública, no hi ha copy de màrqueting: només un backend FastAPI intern al qual un grapat dels nostres propis productes truca amb una clau. Precisament per això el disseny de fallada tancada i fallada parcial importa més aquí del que podria importar en alguna cosa de cara al públic.

Quan un producte públic es trenca, un usuari veu un error i recarrega. Quan es trenca una peça d'infraestructura interna de la qual altres serveis depenen en silenci —o pitjor, es degrada silenciosament i retorna alguna cosa que sembla correcta però no ho és— cada producte construït a sobre hereta aquesta fallada sense saber-ho. Aquest és un llistó molt menys indulgent que «té bona pinta la pàgina de previsió?». És també, crec, el llistó correcte per a qualsevol cosa que estigui silenciosament per sota de decisions que toquen la seguretat d'algú, encara que sigui a una certa distància.

No vam construir això perquè fos difícil o interessant. Ho vam construir perquè, després de Val d'Aran by UTMB, prefereixo passar-me una tarda dissenyant per endavant per al cas de fallada abans que assabentar-me més tard, per un producte que en depèn, que no ho havíem fet.