La lliçó 05-01 va acabar amb un èxit incòmode. La Marta va trobar la comanda de vuit segons: 7.402 ms
dins de PostgreSQL, un patró N+1 amb 39 consultes. Però per arribar-hi va necessitar quatre consultes
de Logs Insights, una cronologia muntada a mà i, sobretot, que algú hagués tingut la precaució de
propagar la capçalera X-Peticion-Id per tots els components. Si demà el problema és a la connexió a
la base de dades, o a la passarel·la de pagament, o en un import de la Lambda que triga dos segons
a arrencar, cal començar de zero i endevinar una altra vegada on mirar.
El problema de fons no és d'esforç: és de model de dades. Les mètriques agreguen i perden el cas individual. Els registres conserven el cas individual però perden la relació entre el que va passar a la botiga i el que va passar a la Lambda. Cap dels dos no guarda l'estructura de la petició: qui va cridar qui, en quin ordre, quant va trigar cada tram i quins se solapen.
AWS X-Ray guarda exactament això. La seva unitat de treball no és un número ni una línia de text:
és una petició completa, amb el seu arbre de crides i la seva cronologia. Del «alguna cosa va
lenta» al «aquesta crida concreta triga 6,2 segons, i 5,9 són dins d'una consulta a pedidos».
Aquesta lliçó instrumenta la botiga de MercadoFresco, activa el rastreig a la Lambda
mercadofresco-estado-pedido i torna a la comanda de 8,14 segons per veure-la, aquesta vegada, en un
sol gràfic.
Avís de cost. X-Ray cobra per traça registrada i per traça recuperada o escanejada. És barat si el mostreig està ben configurat i ruïnós si es traça el 100 % d'un trànsit alt. Hi ha una secció sencera sobre estratègia de mostreig econòmica.
Contingut
- Per què les mètriques i els registres no basten
- El model de dades: traça, segment i subsegment
- L'identificador de traça i la seva propagació
- Mostreig: per què no es tracen totes les peticions
- Regles de mostreig de MercadoFresco
- Anotacions enfront de metadades
- Una traça completa d'una comanda, en un diagrama
- Permisos que necessita X-Ray
- Instrumentar la botiga amb
aws_xray_sdk - Subsegments manuals i anotacions útils
- El dimoni d'X-Ray a EC2 i en contenidors
- Activar el rastreig a
mercadofresco-estado-pedido - Activar el rastreig a l'ALB, a CloudFront i a API Gateway
- El mapa de serveis: com es llegeix
- Filtres de traça: el llenguatge de cerca
- Anàlisi de latència: histogrames i percentils
- El cas guiat: la comanda de 8,14 segons
- X-Ray Insights
- Relació amb CloudWatch ServiceLens
- OpenTelemetry i ADOT: l'alternativa oberta
- Cost i estratègia de mostreig econòmica
- Neteja
Per què les mètriques i els registres no basten
| Mètriques (05-01) | Registres (05-01) | Traces (X-Ray) | |
|---|---|---|---|
| Unitat | Sèrie temporal agregada | Línia de text | Petició completa |
| Respon | Quant? Va bé? | Què ha passat exactament? | On se n'ha anat el temps? |
| Conserva el cas individual | No | Sí | Sí |
| Conserva la relació causal | No | No | Sí |
| Cardinalitat admesa | Molt baixa | Alta | Alta |
| Cost dominant | Nre. de sèries | GB ingerits | Nre. de traces |
| Retenció típica | 15 mesos | Dies o setmanes | 30 dies |
| Detecta | Que alguna cosa va malament | Quin error ha sortit | Quin component té la culpa |
La columna que ho canvia tot és «conserva la relació causal». Un registre de la botiga diu
llamada a lambda estado-pedido i un altre registre, en un altre grup, diu REPORT Duration: 480 ms.
Que aquests dos esdeveniments pertanyin a la mateixa petició és una cosa que tu has de
reconstruir. X-Ray ho sap de naixement, perquè l'identificador viatja amb la petició.
I hi ha tres preguntes que només una traça pot respondre:
- Quanta estona ha estat la petició esperant enfront de treballant? Un registre amb «durada total 8.140 ms» no distingeix entre calcular i esperar algú altre.
- Quines crides s'han fet en paral·lel i quines en sèrie? És la diferència entre 39 consultes de 190 ms seqüencials (7,4 s) i 39 consultes en paral·lel (0,2 s).
- Què ha passat abans del codi? El temps d'arrencada en fred d'una Lambda, l'establiment de la connexió TLS, la resolució de DNS. Res d'això no apareix al registre de la teva aplicació, perquè la teva aplicació encara no s'estava executant.
El model de dades: traça, segment i subsegment
Tres conceptes i una jerarquia:
- Traça (trace): tot el que passa arran d'una petició. S'identifica amb un ID de traça i agrupa segments de tots els serveis implicats.
- Segment (segment): la feina d'un servei o recurs dins de la traça. La botiga genera un segment; la Lambda genera el seu. Conté nom, hora d'inici i de fi, i estat.
- Subsegment: un tram dins d'un segment. Una consulta SQL, una crida HTTP a un altre servei, un bloc de càlcul que vols mesurar.
flowchart TD
T["TRACA 1-68a2f4c1-3b8d9e2a5f7c1b4d8e0a2f6c<br/>durada total: 8.140 ms"]
T --> S1["SEGMENT: mercadofresco-tienda<br/>0 ms - 8.140 ms"]
S1 --> SS1["subsegment: validar_stock<br/>27 ms - 84 ms"]
S1 --> SS2["subsegment: Invoke estado-pedido<br/>140 ms - 690 ms"]
S1 --> SS3["subsegment: SQL inserir linies<br/>700 ms - 8.110 ms"]
SS3 --> SS3a["39 consultes SELECT preu<br/>190 ms cadascuna, EN SERIE"]
T --> S2["SEGMENT: mercadofresco-estado-pedido<br/>145 ms - 685 ms"]
S2 --> SS4["subsegment: Initialization<br/>145 ms - 460 ms - arrencada en fred"]
S2 --> SS5["subsegment: SNS Publish<br/>500 ms - 660 ms"]
Fixa't en el detall que fa útil aquest model: el segment de la Lambda (145-685 ms) està contingut
dins del subsegment Invoke de la botiga (140-690 ms), i la diferència de 10 ms és la latència de
xarxa i de la mateixa API de Lambda. Aquesta xifra no surt en cap registre de cap dels dos serveis.
Un segment, en la seva representació JSON real —simplificada—, es veu així:
{
"trace_id": "1-68a2f4c1-3b8d9e2a5f7c1b4d8e0a2f6c",
"id": "6b1c2d3e4f5a6b7c",
"name": "mercadofresco-tienda",
"start_time": 1785142692.004,
"end_time": 1785142700.144,
"http": {
"request": {
"method": "POST",
"url": "https://mercadofresco.example/api/pedidos/confirmar",
"client_ip": "203.0.113.45",
"user_agent": "Mozilla/5.0 ..."
},
"response": { "status": 200, "content_length": 412 }
},
"aws": {
"ec2": { "instance_id": "i-0abc123def456", "availability_zone": "eu-west-1a" }
},
"annotations": {
"pedido_id": "48213",
"metodo_pago": "tarjeta",
"num_lineas": 38,
"entorno": "produccion"
},
"metadata": {
"default": {
"carrito": { "productos": ["tomate-rama", "lechuga-batavia", "..."] }
}
},
"subsegments": [
{
"id": "7c2d3e4f5a6b7c8d",
"name": "validar_stock",
"start_time": 1785142692.031,
"end_time": 1785142692.088
}
]
}Els camps error, fault i throttle marquen el resultat, i la seva distinció importa perquè el
mapa de serveis els pinta de colors diferents:
| Camp | Significa | Codi HTTP | Color al mapa |
|---|---|---|---|
error |
Error del client | 4xx | Groc |
fault |
Error del servidor | 5xx | Vermell |
throttle |
Estrangulament | 429 | Morat |
| (cap) | Correcte | 2xx / 3xx | Verd |
L'identificador de traça i la seva propagació
Un ID de traça té aquesta forma:
1-68a2f4c1-3b8d9e2a5f7c1b4d8e0a2f6c │ │ └── 96 bits aleatoris en hexadecimal │ └── marca de temps Unix de l'origen, en hexadecimal └── versio (sempre 1)
Que la marca de temps estigui dins de l'identificador no és decoratiu: permet a X-Ray localitzar la traça sense índex global, i és la raó que no es puguin consultar traces de més de 30 dies.
La propagació es fa amb la capçalera HTTP X-Amzn-Trace-Id:
| Camp | Què és |
|---|---|
Root |
L'ID de la traça. El genera el primer component instrumentat. |
Parent |
L'ID del segment que fa la crida. És el que construeix l'arbre. |
Sampled |
1 = traça-la; 0 = no la tracis. La decisió es pren un cop i es respecta. |
Self |
L'afegeix l'ALB quan ja venia una capçalera del client. |
sequenceDiagram
participant C as Client
participant CF as CloudFront
participant ALB as alb-mercadofresco-tienda
participant T as Botiga EC2
participant L as Lambda estado-pedido
participant D as DynamoDB / RDS
C->>CF: POST /api/pedidos/confirmar
CF->>ALB: (reenvia)
ALB->>T: X-Amzn-Trace-Id: Root=1-68a2...;Sampled=1
Note over ALB: L'ALB GENERA la capcalera<br/>si no venia
T->>L: Invoke + Root=1-68a2...;Parent=6b1c...
L->>D: Query + Root=1-68a2...;Parent=9d4e...
D-->>L: resultat
L-->>T: resposta
T-->>C: 200 OK
Tres regles que cal interioritzar:
- El primer component instrumentat genera el
Root. Si l'ALB té el rastreig actiu, el genera ell. Si no, el genera la teva aplicació. - La decisió de mostreig es pren una sola vegada, a l'origen, i tothom la respecta. Si el
Rootarriba ambSampled=0, la Lambda no enviarà el seu segment ni que tingui el rastreig activat. Això evita traces incompletes i és un comportament que confon molt en depurar: «he activat X-Ray a la Lambda i no veig res» sol voler dir que la decisió de mostreig es va prendre aigües amunt. - Els SDK propaguen la capçalera automàticament a les crides sortints que intercepten. En una crida que no intercepten —un client HTTP exòtic, una cua pròpia— l'has de propagar tu.
Mostreig: per què no es tracen totes les peticions
MercadoFresco rep, un divendres normal, de l'ordre de 2 milions de peticions diàries. Traçar-les totes costaria, a 5 USD per milió de traces registrades, uns 300 USD al mes només de registre, sense comptar les recuperacions. I el 99,9 % d'aquestes traces serien idèntiques i avorrides: peticions de 80 ms que funcionen.
El mostreig decideix quina fracció es traça. Una regla de mostreig té dues parts:
reservoir(dipòsit): un nombre fix de peticions per segon que es tracen sempre. És el terra: garanteix que sempre tinguis exemples, fins i tot amb trànsit baixíssim.fixed_rate: el percentatge de la resta que es traça. És el sostre proporcional: garanteix representativitat quan el trànsit puja.
Amb reservoir: 1 i fixed_rate: 0.05:
| Peticions/segon | Del dipòsit | Del 5 % restant | Total traçat |
|---|---|---|---|
| 1 | 1 | 0 | 1 (100 %) |
| 10 | 1 | 0,45 | ~1,45 (14,5 %) |
| 100 | 1 | 4,95 | ~5,95 (6 %) |
| 1.000 | 1 | 49,95 | ~51 (5,1 %) |
Aquest és el disseny: a volum baix traces gairebé tot (i no et costa res); a volum alt traces un percentatge estable (i el cost creix de manera controlada).
Les regles s'avaluen per prioritat, de menor a major número, i guanya la primera que coincideix.
Els criteris de coincidència són: service_name, service_type, host, http_method, url_path, i
atributs de la petició.
Regles de mostreig de MercadoFresco
L'estratègia de la Marta: traçar poc el que és barat i freqüent, traçar molt el que és car i important.
| Prioritat | Nom | Coincideix amb | Dipòsit | Taxa fixa | Motiu |
|---|---|---|---|---|---|
| 100 | muestreo-mercadofresco-checkout |
POST /api/pedidos/* |
2/s | 100 % | Són els diners |
| 200 | muestreo-mercadofresco-admin |
/admin/* |
1/s | 50 % | Poc trànsit, molt valor |
| 300 | muestreo-mercadofresco-estaticos |
/static/*, /favicon.ico |
0/s | 0 % | Soroll pur |
| 400 | muestreo-mercadofresco-salud |
/salud |
0/s | 0 % | El health check cada 15 s |
| 9000 | muestreo-mercadofresco-defecto |
Tota la resta | 1/s | 5 % | Representativitat |
# La regla que importa: tracar el 100% de les confirmacions de comanda.
aws xray create-sampling-rule --cli-input-json '{
"SamplingRule": {
"RuleName": "muestreo-mercadofresco-checkout",
"Priority": 100,
"FixedRate": 1.0,
"ReservoirSize": 2,
"ServiceName": "mercadofresco-tienda",
"ServiceType": "*",
"Host": "*",
"HTTPMethod": "POST",
"URLPath": "/api/pedidos/*",
"Version": 1,
"ResourceARN": "*",
"Attributes": {}
}
}' --profile mercadofresco-dev --region eu-west-1
# I la que estalvia mes diners: NO tracar el health check.
aws xray create-sampling-rule --cli-input-json '{
"SamplingRule": {
"RuleName": "muestreo-mercadofresco-salud",
"Priority": 400,
"FixedRate": 0.0,
"ReservoirSize": 0,
"ServiceName": "*",
"ServiceType": "*",
"Host": "*",
"HTTPMethod": "GET",
"URLPath": "/salud",
"Version": 1,
"ResourceARN": "*"
}
}' --profile mercadofresco-dev --region eu-west-1Aquest /salud es mereix un càlcul. L'ALB comprova /salud cada 15 segons contra cada instància
(03-03). Amb 4 instàncies al pic: 4 × 4 = 16 peticions per minut, 691.200 al mes. Al 5 % serien
34.560 traces mensuals d'una resposta de 3 ms que diu OK. No aporten absolutament res i
embruten els histogrames de latència amb milers de punts a 3 ms que desplacen els percentils.
Excloure'ls és la primera optimització que cal fer sempre.
Les regles es gestionen de manera centralitzada: el SDK les descarrega cada 10 segons des de l'API d'X-Ray. Canvies una regla a la consola i totes les instàncies de l'ASG l'apliquen en segons, sense desplegar codi. Això permet una cosa molt útil: pujar el mostreig al 100 % durant un incident i abaixar-lo quan s'acaba.
Anotacions enfront de metadades
És la distinció més pràctica d'X-Ray i la que decideix si podràs trobar alguna cosa o no:
| Anotació | Metadada | |
|---|---|---|
| Indexada? | Sí | No |
| S'hi pot filtrar? | Sí | No |
| Límit | 50 per traça | Mida del segment (64 KB) |
| Tipus | Cadena, número, booleà | Qualsevol JSON |
| Ús | pedido_id, metodo_pago, provincia |
La cistella sencera, la resposta de la passarel·la |
La regla és simple: si hi has de cercar, és una anotació; si només ho vols veure quan obris la traça, és una metadada.
I aquí hi ha l'avantatge sobre CloudWatch que convé subratllar. A 05-01 vam veure que posar
PedidoId com a dimensió de mètrica costaria 190.000 USD al mes. Com a anotació d'X-Ray és
gratis i, a més, cercable: annotation.pedido_id = "48213". És exactament el buit que les mètriques
no poden cobrir.
Les anotacions de MercadoFresco, triades per respondre preguntes reals:
| Anotació | Exemple | Pregunta que respon |
|---|---|---|
pedido_id |
"48213" |
«El client diu que la seva comanda va trigar molt» |
cliente_hash |
"a3f8c1e9" |
«Li passa sempre, a aquest client?» |
metodo_pago |
"tarjeta" |
«És lent només amb una passarel·la?» |
num_lineas |
38 |
«La lentitud creix amb la mida de la comanda?» |
provincia |
"Madrid" |
«És un problema d'una zona de repartiment?» |
version_app |
"2.14.3" |
«Va començar amb l'últim desplegament?» |
entorno |
"produccion" |
Separar producció de proves |
Avís de privacitat. Les anotacions i les metadades s'emmagatzemen a X-Ray i són visibles per a qualsevol que tingui permís de lectura. Mai no hi posis correus, noms, adreces, números de targeta ni identificadors directes de persona. Per això MercadoFresco fa servir
cliente_hash, un hash de l'identificador intern, i no el correu. Si manegues dades personals sota el RGPD, aquesta decisió l'ha de revisar qui porti el compliment a la teva organització.
Una traça completa d'una comanda, en un diagrama
gantt
title Traca 1-68a2f4c1 - confirmacio de la comanda 48213 - total 8.140 ms
dateFormat X
axisFormat %L ms
section Botiga EC2
Segment botiga :active, t1, 0, 8140
validar_stock :done, t2, 27, 57
Invoke estado-pedido :done, t3, 140, 550
SQL inserir capcalera :done, t4, 700, 40
SQL 39 SELECT preu :crit, t5, 745, 7410
respondre :done, t6, 8110, 30
section Lambda estado-pedido
Segment lambda :active, l1, 145, 540
Initialization arrencada freda :crit, l2, 145, 315
SNS Publish :done, l3, 500, 160
section RDS pedidos
Consultes PostgreSQL :crit, r1, 745, 7402
Un gràfic així —que a la consola d'X-Ray s'anomena vista de cascada i es genera sol— explica la història sencera en dos segons de lectura: hi ha una barra vermella de 7,4 segons i tota la resta és irrellevant en comparació. Compara-ho amb el full de càlcul que la Marta va muntar a mà a 05-01.
Permisos que necessita X-Ray
El rol rol-mercadofresco-tienda ha de poder enviar segments i descarregar les regles de mostreig.
AWS té una política gestionada per a això:
aws iam attach-role-policy \
--role-name rol-mercadofresco-tienda \
--policy-arn arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess \
--profile mercadofresco-devSi prefereixes el mínim privilegi explícit, que és el que ensenyem a 04-01, el contingut efectiu és aquest:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnviarTrazas",
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords"
],
"Resource": "*"
},
{
"Sid": "DescargarReglasDeMuestreo",
"Effect": "Allow",
"Action": [
"xray:GetSamplingRules",
"xray:GetSamplingTargets",
"xray:GetSamplingStatisticSummaries"
],
"Resource": "*"
}
]
}Cap d'aquestes accions no admet ARN de recurs —X-Ray no té recursos per traça—, de manera que
"Resource": "*" és correcte aquí; és el mateix cas que cloudwatch:PutMetricData a 05-01.
Per al rol de la Lambda, rol-lambda-miniaturas i el de mercadofresco-estado-pedido, n'hi ha prou
amb la política gestionada AWSXRayDaemonWriteAccess, o l'encara més específica
AWSXRayWriteOnlyAccess.
I per llegir traces, el grup de persones que fa guàrdia necessita:
{
"Effect": "Allow",
"Action": [
"xray:GetTraceSummaries",
"xray:BatchGetTraces",
"xray:GetServiceGraph",
"xray:GetTraceGraph",
"xray:GetTimeSeriesServiceStatistics",
"xray:GetGroups",
"xray:GetInsightSummaries",
"xray:GetInsight"
],
"Resource": "*"
}Instrumentar la botiga amb aws_xray_sdk
Instal·lació:
I la instrumentació mínima d'una aplicació Flask, que és el que corre a les instàncies de l'ASG:
"""Instrumentacio d'X-Ray a la botiga de MercadoFresco."""
from flask import Flask, request, jsonify
from aws_xray_sdk.core import xray_recorder, patch_all
from aws_xray_sdk.ext.flask.middleware import XRayMiddleware
# 1. Configurar el gravador ABANS de crear l'aplicacio.
xray_recorder.configure(
service="mercadofresco-tienda", # nom del node al mapa
daemon_address="127.0.0.1:2000", # on escolta el dimoni
context_missing="LOG_ERROR", # NO llancar excepcio si falta context
sampling=True, # fer servir les regles centralitzades
plugins=("EC2Plugin",), # afegeix instance_id i AZ al segment
)
# 2. patch_all instrumenta automaticament les llibreries admeses:
# boto3, botocore, requests, httplib, sqlite3, psycopg2, pymysql, aiohttp...
patch_all()
app = Flask(__name__)
# 3. El middleware crea un segment per cada peticio HTTP entrant,
# llegeix la capcalera X-Amzn-Trace-Id i respecta la decisio de mostreig.
XRayMiddleware(app, xray_recorder)Quatre decisions que cal entendre, perquè cadascuna evita un problema real:
context_missing="LOG_ERROR"és probablement el paràmetre més important del fitxer. Amb el valor per defecte (RUNTIME_ERROR), qualsevol codi instrumentat que s'executi fora d'una petició HTTP —una tasca programada, un script de manteniment, un fil de fons, el publicador de mètriques per lots de 05-01— llança una excepció i tomba la tasca. AmbLOG_ERROR, escriu un error al registre i continua. L'observabilitat no ha de tombar mai l'aplicació.plugins=("EC2Plugin",)fa que cada segment inclogui l'instance_idi la zona de disponibilitat. Quan una sola instància de l'ASG estigui degradada, aquesta dada és la que ho revela. Hi ha equivalentsECSPluginiElasticBeanstalkPlugin.patch_all()incloupsycopg2, el controlador de PostgreSQL. Això vol dir que cada consulta amercadofresco-pedidosgenera un subsegment automàticament, amb el text de la consulta sanejat. És exactament el que delatarà l'N+1.daemon_address: el SDK no parla amb l'API d'X-Ray. Escriu paquets UDP a un dimoni local, que s'encarrega d'agrupar-los i enviar-los. Per això una crida instrumentada afegeix microsegons, no mil·lisegons.
Si prefereixes instrumentar amb més detall,
patch()accepta la llista de mòduls:patch(("boto3", "psycopg2", "requests")). Evita instrumentarsqlite3en producció si el fas servir per a memòries cau locals d'alta freqüència: generaries milers de subsegments inútils.
Subsegments manuals i anotacions útils
La instrumentació automàtica cobreix les crides a serveis externs. Els trams del teu codi els has de marcar tu:
"""Confirmacio de comanda, instrumentada."""
import hashlib
from aws_xray_sdk.core import xray_recorder
@app.route("/api/pedidos/confirmar", methods=["POST"])
def confirmar_comanda():
dades = request.get_json()
pedido_id = dades["pedido_id"]
# ANOTACIONS: indexades i filtrables. Es el primer que es fa,
# perque hi siguin encara que la peticio falli despres.
segment = xray_recorder.current_segment()
segment.put_annotation("pedido_id", str(pedido_id))
segment.put_annotation("metodo_pago", dades["metodo_pago"])
segment.put_annotation("num_lineas", len(dades["lineas"]))
segment.put_annotation("provincia", dades["direccion"]["provincia"])
segment.put_annotation("version_app", app.config["VERSION"])
# Hash, MAI el correu ni l'identificador directe del client.
segment.put_annotation(
"cliente_hash",
hashlib.sha256(dades["cliente_id"].encode()).hexdigest()[:8],
)
# METADADES: no indexades, pero visibles en obrir la traca.
segment.put_metadata("carrito", dades["lineas"], "negocio")
segment.put_metadata("importe_total", dades["total_eur"], "negocio")
# SUBSEGMENT com a gestor de context: es tanca sol, fins i tot si hi ha excepcio.
with xray_recorder.in_subsegment("validar_stock") as sub:
disponible = validar_stock(dades["lineas"])
sub.put_annotation("stock_ok", disponible)
if not disponible:
# error = problema del client (4xx), groc al mapa.
sub.add_error_flag()
return jsonify({"error": "sin stock"}), 409
with xray_recorder.in_subsegment("calcular_portes"):
ports = calcular_portes(dades["direccion"])
# Aquesta crida NO necessita subsegment manual: patch_all ha instrumentat
# boto3, i la invocacio de Lambda apareix sola a la traca.
resposta = cliente_lambda.invoke(
FunctionName="mercadofresco-estado-pedido",
Payload=json.dumps({"pedido_id": pedido_id}),
)
# Aquesta tampoc: psycopg2 esta instrumentat i cada consulta
# genera el seu propi subsegment amb el SQL sanejat.
with xray_recorder.in_subsegment("persistir_pedido"):
desar_comanda(pedido_id, dades["lineas"], ports)
return jsonify({"estado": "confirmado", "pedido_id": pedido_id}), 200I el decorador, per a funcions que es criden des de diversos llocs:
from aws_xray_sdk.core import xray_recorder
@xray_recorder.capture("calcular_portes")
def calcular_portes(direccion):
"""Cada crida a aquesta funcio crea el seu propi subsegment."""
...Feina en fils i en tasques asíncrones
L'error d'instrumentació més habitual en producció. El SDK desa el segment actual en una variable de context per fil. Si engegues un fil nou, aquest fil no té context i tot el que hi executis instrumentat fallarà o es perdrà:
import threading
from aws_xray_sdk.core import xray_recorder
def processar_en_paralel(entitat):
# Capturar el segment al fil pare...
segment_pare = xray_recorder.current_subsegment() or xray_recorder.current_segment()
def treball():
# ...i restaurar-lo al fil fill.
xray_recorder.context.put_segment(segment_pare)
with xray_recorder.in_subsegment("trabajo_paralelo"):
fer_alguna_cosa(entitat)
threading.Thread(target=treball).start()Si això et sembla fràgil, tens raó: ho és. És un dels arguments seriosos a favor d'OpenTelemetry, que resol la propagació de context de manera més neta. Ho veiem al final de la lliçó.
El dimoni d'X-Ray a EC2 i en contenidors
El dimoni d'X-Ray és un procés lleuger que escolta al port 2000/UDP, agrupa els segments que li envien els SDK i els fa arribar a l'API d'X-Ray per lots.
flowchart LR
A["Aplicacio<br/>aws_xray_sdk"] -->|"UDP 2000<br/>microsegons"| D["Dimoni X-Ray<br/>proces local"]
D -->|"HTTPS per lots<br/>cada segon"| X["API d'X-Ray<br/>eu-west-1"]
Per què aquesta arquitectura? Perquè desacobla l'aplicació de la xarxa. Si l'API d'X-Ray va lenta o està caiguda, l'aplicació no se n'assabenta: continua escrivint paquets UDP que, en el pitjor cas, es perden. Una traça perduda no és cap problema; una botiga bloquejada esperant l'API de traces, sí.
Instal·lació a les instàncies de l'ASG, afegida al user data de lt-mercadofresco-tienda:
#!/bin/bash
set -euo pipefail
# Amazon Linux 2023. AWS publica el paquet en un bucket per regio.
curl -fsSL -o /tmp/xray.rpm \
"https://s3.dualstack.eu-west-1.amazonaws.com/aws-xray-assets.eu-west-1/xray-daemon/aws-xray-daemon-3.x.rpm"
dnf install -y /tmp/xray.rpm
cat > /etc/amazon/xray/cfg.yaml <<'YAML'
TotalBufferSizeMB: 16
Concurrency: 8
Region: eu-west-1
Socket:
UDPAddress: 127.0.0.1:2000
TCPAddress: 127.0.0.1:2000
LocalMode: false
LogLevel: warn
YAML
systemctl enable --now xray
systemctl is-active xray || exit 1Notes de camp:
UDPAddress: 127.0.0.1:2000, no0.0.0.0. Només ha d'escoltar a la interfície local; ningú de fora no li ha d'enviar segments.TotalBufferSizeMB: si la memòria intermèdia s'omple, el dimoni descarta segments i ho anota al seu registre. VeureSegmentsRejectedCountcreixent vol dir que cal pujar-lo o abaixar el mostreig.- No cal obrir res a
sg-mercadofresco-tienda: el trànsit és local. La sortida cap a l'API d'X-Ray passa pel NAT (03-01) o, millor encara, per un punt d'enllaç d'interfície de VPC per acom.amazonaws.eu-west-1.xraysi vols que ni tan sols passi per internet.
En contenidors (mòdul 10), el dimoni es desplega com a contenidor sidecar dins de la mateixa
definició de tasca, i l'aplicació l'atansa per localhost a ECS amb xarxa awsvpc. Es detalla a
10-01.
Activar el rastreig a mercadofresco-estado-pedido
A Lambda no hi ha cap dimoni per instal·lar: AWS l'executa per tu dins de l'entorn d'execució. N'hi ha prou amb una casella i un permís.
# 1. El permis
aws iam attach-role-policy \
--role-name rol-mercadofresco-estado-pedido \
--policy-arn arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess \
--profile mercadofresco-dev
# 2. La casella: mode de rastreig actiu
aws lambda update-function-configuration \
--function-name mercadofresco-estado-pedido \
--tracing-config Mode=Active \
--profile mercadofresco-dev --region eu-west-1Els dos modes:
| Mode | Comportament |
|---|---|
PassThrough (per defecte) |
Només traça si la petició ja venia amb Sampled=1 |
Active |
La funció decideix el mostreig si no venia decidit |
Per a mercadofresco-estado-pedido, que la crida la botiga, PassThrough seria suficient i més
barat. Per a mercadofresco-generar-miniaturas, que la dispara un esdeveniment d'S3 i no té origen
instrumentat, cal Active o no es traçarà mai res.
Només amb això ja obtens el segment amb la durada, l'arrencada en fred i els errors. Per veure dins de la funció, cal instrumentar igual que a EC2:
"""mercadofresco-estado-pedido, instrumentada."""
import json
import os
import boto3
from aws_xray_sdk.core import xray_recorder, patch_all
# A Lambda el dimoni es a l'adreca de la variable d'entorn,
# que el mateix entorn d'execucio defineix. No cal configurar res.
patch_all()
sns = boto3.client("sns")
TEMA = os.environ["ARN_TEMA_ALERTAS"]
def handler(esdeveniment, context):
pedido_id = esdeveniment["pedido_id"]
# A Lambda, el segment arrel el crea l'entorn d'execucio i es
# de nomes lectura: les anotacions van en un SUBSEGMENT.
subsegment = xray_recorder.begin_subsegment("procesar_estado")
try:
subsegment.put_annotation("pedido_id", str(pedido_id))
subsegment.put_annotation("origen", esdeveniment.get("origen", "tienda"))
estat = calcular_estat(pedido_id)
subsegment.put_annotation("estado_resultante", estat)
# boto3 esta pedacat: aquesta crida apareix sola com a subsegment
# i a mes dibuixa el node d'SNS al mapa de serveis.
sns.publish(
TopicArn=TEMA,
Subject=f"Pedido {pedido_id}: {estat}",
Message=json.dumps({"pedido_id": pedido_id, "estado": estat}),
)
return {"estado": estat}
except Exception as e:
subsegment.add_exception(e, []) # marca fault: vermell al mapa
raise
finally:
xray_recorder.end_subsegment()El detall que fa perdre temps a tothom la primera vegada: a Lambda no pots anotar el segment
arrel. El crea i el controla l'entorn d'execució. xray_recorder.current_segment() existeix, però
put_annotation sobre ell s'ignora silenciosament. Les anotacions van sempre en un subsegment
propi.
Un altre detall valuós: el subsegment Initialization que veus a les traces de Lambda és l'arrencada
en fred. Si el teu p99 de Duration és dolent però el p50 és bo, mira aquest subsegment: gairebé
sempre és un import pesat que es pot moure dins del handler o reduir. És el diagnòstic que a 02-05
només podíem intuir.
Activar el rastreig a l'ALB, a CloudFront i a API Gateway
| Servei | Què aporta | Com s'activa |
|---|---|---|
| ALB | Genera X-Amzn-Trace-Id si no ve |
Automàtic, sempre actiu |
| API Gateway | Segment propi amb latència d'integració | Casella per etapa |
| CloudFront | No genera segments d'X-Ray | Es correlaciona per x-amz-cf-id |
| SQS / SNS | Propaguen la capçalera | Automàtic (07-01, 07-02) |
| Step Functions | Segment per estat | Casella (07-04) |
L'ALB genera la capçalera automàticament i no hi ha res per activar: és la raó per la qual la
botiga de MercadoFresco rep un Root sense que ningú ho hagi programat. El que l'ALB no fa és
generar un segment propi, així que el seu temps de procés no surt com a node al mapa. Aquesta latència
es continua veient a la mètrica TargetResponseTime de CloudWatch (05-01), i aquest és un bon exemple
que les dues eines es complementen.
Si algun dia MercadoFresco posa una API darrere d'API Gateway:
aws apigateway update-stage \
--rest-api-id abc123def4 --stage-name produccion \
--patch-operations op=replace,path=/tracingEnabled,value=true \
--profile mercadofresco-dev --region eu-west-1CloudFront és la peça que falta, i convé ser honest: CloudFront no participa en les traces
d'X-Ray. El seu identificador propi és x-amz-cf-id, que apareix als seus registres d'accés. Per
correlacionar el temps de la CDN amb la traça, MercadoFresco registra aquest valor com a anotació:
Amb això, donada una traça pots buscar la línia corresponent als registres de CloudFront a S3, i a l'inrevés. No és tan còmode com un node al mapa, però tanca el cercle.
El mapa de serveis: com es llegeix
El mapa de serveis és el gràfic que X-Ray construeix agregant totes les traces de la finestra temporal seleccionada. No és un diagrama d'arquitectura dibuixat per ningú: és el que realment passa, deduït del trànsit.
flowchart LR
C(("Client")) --> T["mercadofresco-tienda<br/>1.240 t/min<br/>lat. mitjana 0,18 s<br/>errors 0,2%"]
T --> L["mercadofresco-estado-pedido<br/>Lambda<br/>lat. mitjana 0,48 s"]
T --> R[("mercadofresco-pedidos<br/>PostgreSQL<br/>lat. mitjana 2,10 s<br/>VERMELL")]
T --> S3[("mercadofresco-catalogo-fotos<br/>S3")]
L --> SN["alertas-mercadofresco<br/>SNS"]
L --> R
Com es llegeix, element a element:
| Element | Significat |
|---|---|
| Cercle | Un node: un servei, un recurs o el client |
| Mida del cercle | Volum de trànsit |
| Anell verd | Peticions correctes |
| Anell groc | error — fallades 4xx (culpa del client) |
| Anell vermell | fault — fallades 5xx (culpa teva) |
| Anell morat | throttle — estrangulament (429) |
| Fletxa | Crida d'un node a un altre |
| Node de client | L'origen: no és un servei teu |
Els tipus de node també importen: X-Ray distingeix els nodes de servei (una cosa que tu instrumentes, amb el seu propi segment) dels nodes de recurs descendent (una base de dades, un bucket d'S3, que no estan instrumentats però el temps dels quals es mesura des del qui crida). Un node d'RDS vermell vol dir que les crides a RDS fallen o triguen, no que el servidor de PostgreSQL informi de res.
Tres patrons que es reconeixen d'un cop d'ull en un mapa real:
- Un node vermell aïllat amb tota la resta verda: el culpable és clar.
- Tot vermell aigües avall d'un node: hi ha una fallada en cascada; el culpable és el més profund.
- Un node que apareix de sobte i no hi hauria de ser: una dependència que algú ha introduït sense dir-ho. És una de les raons per les quals aquest mapa és útil fins i tot sense incidents.
Els grups permeten tenir un mapa filtrat, per exemple només del flux de compra:
aws xray create-group \
--group-name grupo-mercadofresco-pedidos \
--filter-expression 'service("mercadofresco-tienda") AND annotation.entorno = "produccion" AND http.url CONTAINS "/api/pedidos"' \
--insights-configuration InsightsEnabled=true,NotificationsEnabled=true \
--profile mercadofresco-dev --region eu-west-1Un grup amb InsightsEnabled genera, a més, mètriques pròpies a CloudWatch, sobre les quals es poden
crear alarmes.
Filtres de traça: el llenguatge de cerca
Aquí hi ha la potència real d'X-Ray. El llenguatge d'expressions de filtre:
| Expressió | Troba |
|---|---|
service("mercadofresco-tienda") |
Traces que passen per aquest servei |
service("mercadofresco-pedidos") { fault } |
Traces on aquest node ha fallat |
responsetime > 5 |
Traces de més de 5 segons |
duration > 3 AND duration < 10 |
Entre 3 i 10 segons |
http.status = 500 |
Per codi de resposta |
http.url CONTAINS "/api/pedidos" |
Per ruta |
annotation.pedido_id = "48213" |
Una comanda concreta |
annotation.cliente_hash = "a3f8c1e9" |
Totes les peticions d'un client |
annotation.num_lineas > 25 |
Comandes grans |
annotation.version_app = "2.14.3" |
Només la versió nova |
error = true |
Fallades 4xx |
fault = true |
Fallades 5xx |
throttle = true |
Estrangulaments |
service("estado-pedido") { fault } AND responsetime > 2 |
Combinacions amb AND, OR, NOT |
edge("mercadofresco-tienda", "mercadofresco-pedidos") |
Traces que recorren aquesta aresta concreta |
Des de la CLI:
# Totes les confirmacions lentes de les ultimes 3 hores
aws xray get-trace-summaries \
--start-time $(date -d '3 hours ago' +%s) \
--end-time $(date +%s) \
--filter-expression 'http.url CONTAINS "/api/pedidos/confirmar" AND responsetime > 5' \
--query 'TraceSummaries[].[Id,Duration,ResponseTime,Http.HttpStatus]' \
--output table \
--profile mercadofresco-dev --region eu-west-1
# I el detall complet d'una traca concreta
aws xray batch-get-traces \
--trace-ids 1-68a2f4c1-3b8d9e2a5f7c1b4d8e0a2f6c \
--profile mercadofresco-dev --region eu-west-1Un script que la Marta fa servir quan arriba una queixa concreta, i que substitueix les quatre consultes de Logs Insights de 05-01:
"""Buscar la traca d'una comanda concreta i desglossar on se n'ha anat el temps."""
import boto3
import time
xray = boto3.client("xray", region_name="eu-west-1")
def investigar_comanda(pedido_id, hores=24):
ara = int(time.time())
resums = xray.get_trace_summaries(
StartTime=ara - hores * 3600,
EndTime=ara,
FilterExpression=f'annotation.pedido_id = "{pedido_id}"',
)
if not resums["TraceSummaries"]:
print(f"Sense traces per a la comanda {pedido_id}.")
print("Causes possibles: no s'ha mostrejat, o han passat mes de 30 dies.")
return
for resum in resums["TraceSummaries"]:
traca_id = resum["Id"]
print(f"\nTraca {traca_id}: {resum['Duration']:.3f} s")
detall = xray.batch_get_traces(TraceIds=[traca_id])
for traca in detall["Traces"]:
for segment in traca["Segments"]:
import json
doc = json.loads(segment["Document"])
dur = doc["end_time"] - doc["start_time"]
print(f" [{dur*1000:8.1f} ms] {doc['name']}")
for sub in doc.get("subsegments", []):
d = sub["end_time"] - sub["start_time"]
marca = " <-- AQUI" if d > 1.0 else ""
print(f" [{d*1000:8.1f} ms] {sub['name']}{marca}")
investigar_comanda("48213")Anàlisi de latència: histogrames i percentils
La consola d'X-Ray ofereix, per a cada node del mapa i per a cada cerca, un histograma de latència en escala logarítmica. És l'eina que revela el que un percentil resumeix.
Un histograma unimodal —una sola gepa— vol dir que totes les peticions es comporten de manera semblant: si és lent, és lent per a tothom, i el problema és estructural.
Un histograma bimodal —dues gepes— vol dir que hi ha dues poblacions diferents de peticions,
i aquesta és la troballa més valuosa que ofereix X-Ray. El de mercadofresco-tienda al pic del divendres:
| Latència | Peticions | Interpretació |
|---|---|---|
| 60-200 ms | 94 % | Peticions normals |
| 200 ms - 2 s | 4 % | Fitxes de producte amb moltes fotos |
| 5-9 s | 2 % | La segona gepa: el problema |
Un p95 d'1,8 s no hauria mostrat res estrany. El p99 de 8,2 s sí. Però el que de debò convenç és
veure que hi ha dues gepes separades: no és una degradació general, és un subconjunt concret de
peticions que es comporta d'una altra manera. I a X-Ray pots seleccionar la segona gepa a
l'histograma amb el ratolí i veure només aquelles traces. Això porta directament al cas guiat.
Una comparació útil que cal aprendre a fer, entre dues versions de l'aplicació:
# Latencia p95 abans del desplegament de la versio 2.14.3
aws xray get-time-series-service-statistics \
--start-time $(date -d '2026-07-29 00:00' +%s) \
--end-time $(date -d '2026-07-30 00:00' +%s) \
--group-name grupo-mercadofresco-pedidos \
--entity-selector-expression 'service("mercadofresco-tienda")' \
--period 3600 \
--profile mercadofresco-dev --region eu-west-1I el filtre que aïlla exactament l'efecte d'un desplegament, que és la pregunta que més vegades es fa després de publicar una versió:
El cas guiat: la comanda de 8,14 segons
Tornem a la comanda 48213, aquesta vegada amb X-Ray. Compara l'esforç amb les quatre consultes de 05-01.
Pas 1. Trobar la traça. Una sola cerca:
Apareix una traça, 8,14 s. A 05-01 això va costar dues consultes d'Insights i saber el hash del client.
Pas 2. Obrir la vista de cascada. Es veu immediatament:
| Tram | Durada | % del total |
|---|---|---|
validar_stock |
57 ms | 0,7 % |
Invoke mercadofresco-estado-pedido |
550 ms | 6,8 % |
↳ Initialization (arrencada en fred) |
315 ms | 3,9 % |
calcular_portes |
12 ms | 0,1 % |
persistir_pedido |
40 ms | 0,5 % |
SELECT × 39 sobre productos |
7.410 ms | 91,0 % |
| Resta | 71 ms | 0,9 % |
Pas 3. Veure la naturalesa del problema. En desplegar el subsegment de la base de dades apareixen 39 subsegments consecutius, cadascun d'uns 190 ms, amb el mateix SQL sanejat:
Això és el que un registre no pot ensenyar: la forma. Trenta-nou barres idèntiques, una darrere de l'altra, sense solapar-se. El diagnòstic es llegeix al gràfic abans que ningú llegeixi el SQL. És el patró N+1: el codi recorre les línies de la comanda i consulta el preu de cada producte per separat.
Pas 4. Confirmar que és sistemàtic i no un cas aïllat. El filtre:
Retorna 187 traces en 24 hores. I el complementari:
Retorna 2. La lentitud creix amb el nombre de línies de la comanda: confirmat. Aquesta és
exactament la pregunta que les anotacions eren allà per respondre, i per això num_lineas era una
anotació i no una metadada.
Pas 5. Quantificar l'impacte en el negoci. Amb el filtre anterior sobre 30 dies: 5.610 comandes afectades, amb més de 4 segons d'espera a la confirmació. Són les comandes més grans, és a dir, les de major import. Aquesta dada és la que converteix una tasca tècnica en una prioritat.
Pas 6. Arreglar. Una sola consulta en lloc de 39:
Pas 7. Verificar amb dades, no amb impressions. Es desplega com a versió 2.14.4 i es compara:
| Mètrica | Abans (2.14.3) | Després (2.14.4) |
|---|---|---|
| p50 de confirmació | 1,9 s | 0,21 s |
| p95 | 6,8 s | 0,44 s |
| p99 | 8,4 s | 0,61 s |
| Subsegments SQL per comanda | 39-42 | 3 |
DatabaseConnections al pic |
185 | 96 |
Fixa't en l'última fila, que és un efecte secundari que ningú no havia previst: en eliminar l'N+1, la
pressió sobre mercadofresco-pedidos cau a la meitat. L'alarma
mercadofresco-rds-conexiones-altas de 05-01 deixa d'estar al límit tots els divendres. Un problema
de latència era també un problema de capacitat.
I el pas 8, el que tanca el cicle amb 05-01: ara que sabem quin és el problema, es crea una alarma
perquè no torni sense avisar. El grup grupo-mercadofresco-pedidos amb Insights activat publica
mètriques a CloudWatch, i la mètrica TiempoConfirmacionPedido que vam crear a 05-01 ja serveix per
detectar la regressió. X-Ray diagnostica; CloudWatch vigila. Cap dels dos no substitueix l'altre.
X-Ray Insights
Insights analitza contínuament les traces d'un grup, construeix una línia base de comportament normal i detecta anomalies sense que tu defineixis llindars. Quan en troba una, obre un «insight» amb:
- El moment d'inici i l'estat (actiu o resolt).
- La causa arrel probable: el node i el tipus d'error que més hi contribueix.
- L'impacte: nombre de peticions i d'usuaris afectats.
- Un gràfic d'anomalia enfront de la línia base.
aws xray update-group \
--group-name grupo-mercadofresco-pedidos \
--insights-configuration InsightsEnabled=true,NotificationsEnabled=true \
--profile mercadofresco-dev --region eu-west-1
aws xray get-insight-summaries \
--start-time $(date -d '7 days ago' +%s) --end-time $(date +%s) \
--states ACTIVE CLOSED \
--group-name grupo-mercadofresco-pedidos \
--profile mercadofresco-dev --region eu-west-1Amb NotificationsEnabled=true, cada insight genera un esdeveniment d'EventBridge (07-03), que es
pot encaminar a alertas-mercadofresco. Aquest és l'enllaç que converteix X-Ray d'eina de diagnòstic
en eina de detecció.
Dues limitacions honestes:
- Insights detecta anomalies respecte al que és habitual, no problemes. Si el teu servei sempre ha trigat 8 segons, no ho marcarà mai.
- Necessita volum. Amb poc trànsit mostrejat, la línia base és sorollosa i produeix falsos positius. És una raó més per no mostrejar a l'1 %.
Relació amb CloudWatch ServiceLens
ServiceLens és la vista de CloudWatch que uneix els tres senyals en una sola pantalla. És literalment el mapa de serveis d'X-Ray, però amb les mètriques de CloudWatch i un accés directe als registres correlacionats.
| Des de ServiceLens pots… | I així… |
|---|---|
| Veure el mapa amb mètriques de CloudWatch superposades | Latència, taxa d'error i peticions per node |
| Clicar un node i veure'n les mètriques | Sense canviar de consola |
| Clicar un node i veure'n els registres | Filtrats per la finestra temporal del pic |
| Passar d'un pic de latència a les traces d'aquest pic | En dos clics |
| Veure els contribuents principals a l'error | Quina URL, quina instància |
Requisit: X-Ray actiu i, per a la correlació de registres, que els grups estiguin associats. El flux de treball que ServiceLens habilita, i que és el que la Marta acaba fent servir cada dia:
flowchart LR
A["Alarma CloudWatch<br/>latencia p95 alta"] --> B["ServiceLens:<br/>quin node esta vermell?"]
B --> C["X-Ray: traces<br/>d'aquest pic"]
C --> D["Cascada:<br/>quin subsegment?"]
D --> E["Logs Insights:<br/>que deia el registre<br/>en aquell instant"]
E --> F["Diagnostic"]
Mètrica → mapa → traça → subsegment → registre. Aquest és el camí complet, i ara MercadoFresco el té sencer.
OpenTelemetry i ADOT: l'alternativa oberta
Seria deshonest ensenyar X-Ray sense dir que avui l'estàndard del sector és OpenTelemetry (OTel), un projecte de la CNCF que defineix una API, un SDK i un protocol (OTLP) neutrals respecte al proveïdor.
AWS Distro for OpenTelemetry (ADOT) és la distribució d'AWS d'OpenTelemetry, amb suport oficial, que pot enviar traces a X-Ray i a qualsevol altra destinació alhora.
| X-Ray SDK | OpenTelemetry / ADOT | |
|---|---|---|
| Estàndard | Propietari d'AWS | Obert (CNCF) |
| Senyals | Traces | Traces, mètriques i registres |
| Destinacions | X-Ray | X-Ray, Prometheus, Jaeger, Grafana, Datadog… |
| Llenguatges | 6 oficials | Dotzenes |
| Instrumentació automàtica | Bona a AWS | Molt àmplia, amb agent automàtic a Java/Python/Node |
| Propagació de context | Pròpia (X-Amzn-Trace-Id) |
W3C Trace Context (traceparent) + AWS |
| Maduresa a AWS | Molt alta, anys | Alta i creixent |
| Complexitat inicial | Baixa | Mitjana (hi ha un col·lector per configurar) |
| Risc de dependència | Alt | Baix |
| Recomanació d'AWS avui | Amb suport | Preferit per a projectes nous |
Què recomanar, amb criteri:
- Projecte nou, o amb intenció de no lligar-se a AWS: OpenTelemetry amb ADOT. El cost inicial extra —configurar el col·lector— es paga sol la primera vegada que vulguis enviar les mateixes traces a un altre lloc, o migrar.
- Projecte existent ja instrumentat amb el SDK d'X-Ray, tot dins d'AWS: no hi ha pressa. Funciona, té suport i migrar té un cost.
- Cas de MercadoFresco: la Marta ha instrumentat amb el SDK d'X-Ray perquè és el més ràpid de posar en marxa i tot el seu sistema és a AWS. Ha anotat al registre de decisions tècniques que la migració a ADOT és l'evolució natural quan l'aplicació creixi o si algun dia es planteja multinúvol. No és deute tècnic ocult: és una decisió conscient amb data de revisió.
Una nota de compatibilitat que estalvia un mal tràngol: X-Ray propaga X-Amzn-Trace-Id i OTel propaga
traceparent (W3C). Si barreges tots dos en un mateix sistema, cal configurar el propagador
xray a OTel perquè les traces no es trenquin a la frontera.
Un exemple mínim d'ADOT en Python, perquè en vegis la diferència de forma:
pip install aws-opentelemetry-distro
# La instrumentacio automatica no toca el teu codi
OTEL_PYTHON_DISTRO=aws_distro \
OTEL_PYTHON_CONFIGURATOR=aws_configurator \
OTEL_TRACES_EXPORTER=otlp_proto_http \
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces \
OTEL_PROPAGATORS=xray \
OTEL_RESOURCE_ATTRIBUTES="service.name=mercadofresco-tienda" \
opentelemetry-instrument python app.pyFixa't que no hi ha ni una sola línia de codi de l'aplicació: opentelemetry-instrument embolcalla
el procés i instrumenta les llibreries conegudes. Això, per a una aplicació gran i existent, és un
avantatge enorme.
Cost i estratègia de mostreig econòmica
| Concepte | Preu de referència | Capa gratuïta mensual |
|---|---|---|
| Traça registrada | 5,00 USD per milió | 100.000 |
| Traça recuperada o escanejada | 0,50 USD per milió | 1.000.000 |
| X-Ray Insights | 1,00 USD per milió de traces analitzades | — |
| Emmagatzematge | Inclòs (30 dies) | — |
«Traça recuperada» és cada vegada que algú obre una traça o executa un filtre. Consultar és barat; registrar és el que surt car.
Càlcul per a MercadoFresco. Trànsit: uns 2 milions de peticions al dia, 60 milions al mes.
Escenari A: sense regles, mostreig per defecte (1/s + 5 %).
| Concepte | Traces/mes | Cost |
|---|---|---|
| Registrades | ~3,3 milions | 16,50 USD |
| Recuperades | ~200.000 | 0,00 USD (capa gratuïta) |
| Total | ~16,50 USD |
Escenari B: amb les regles de MercadoFresco.
| Regla | Peticions/mes | Mostreig | Traces |
|---|---|---|---|
/salud |
691.200 | 0 % | 0 |
| Estàtics | 34.000.000 | 0 % | 0 |
/api/pedidos/* |
660.000 | 100 % | 660.000 |
/admin/* |
40.000 | 50 % | 20.000 |
| Resta | 24.600.000 | 1/s + 5 % | ~1.400.000 |
| Total registrades | ~2,08 milions |
| Concepte | Cost |
|---|---|
| Registrades: (2.080.000 − 100.000) × 5 / 1.000.000 | 9,90 USD |
| Recuperades: ~500.000 | 0,00 USD |
| Insights sobre el grup de comandes | ~0,70 USD |
| Total | ~10,60 USD/mes |
Menys cost que l'escenari A i amb el 100 % de les confirmacions de comanda traçades, que és l'única cosa que de debò importa. Això és el que fa una bona estratègia de mostreig: no traça menys, traça millor.
Escenari C: l'error, mostreig al 100 % de tot.
60 milions de traces: 300 USD/mes, més l'impacte en el rendiment del dimoni i en la usabilitat de
la consola, que s'omple de traces de /salud. No ho facis mai fora d'una finestra de depuració
acotada.
Les cinc regles de l'estratègia econòmica:
- Excloure sempre
/saludi els estàtics. És el 57 % del trànsit de MercadoFresco i el seu valor diagnòstic és zero. - Traçar al 100 % les rutes crítiques de negoci. Són poques peticions i són les que importen.
- Dipòsit d'almenys 1/s a la regla per defecte, per tenir exemples fins i tot de matinada.
- Pujar el mostreig temporalment durant un incident i abaixar-lo en acabar. Les regles són centralitzades i s'apliquen en 10 segons, sense desplegar.
- Vigilar el cost amb un pressupost filtrat per servei = X-Ray (mòdul 11).
I una advertència de rendiment, no de cost: cada subsegment té un preu en mida. Un segment no pot
passar de 64 KB, i un patch_all() sobre una aplicació que fa milers de consultes petites per petició
genera segments enormes que el dimoni acaba descartant. Si veus SegmentsRejectedCount al registre
del dimoni, revisa què estàs instrumentant.
Neteja
# Desactivar el rastreig a Lambda
aws lambda update-function-configuration \
--function-name mercadofresco-estado-pedido \
--tracing-config Mode=PassThrough \
--profile mercadofresco-dev --region eu-west-1
# Esborrar les regles de mostreig
for R in muestreo-mercadofresco-checkout muestreo-mercadofresco-admin \
muestreo-mercadofresco-estaticos muestreo-mercadofresco-salud; do
aws xray delete-sampling-rule --rule-name "$R" \
--profile mercadofresco-dev --region eu-west-1
done
# Esborrar el grup
aws xray delete-group --group-name grupo-mercadofresco-pedidos \
--profile mercadofresco-dev --region eu-west-1
# Treure els permisos
aws iam detach-role-policy --role-name rol-mercadofresco-tienda \
--policy-arn arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess \
--profile mercadofresco-dev
# I a les instancies, aturar el dimoni
sudo systemctl disable --now xrayImportant: les traces ja registrades no es poden esborrar i caduquen soles als 30 dies. No hi
ha emmagatzematge que continuï costant. El que sí que cal fer és treure la instrumentació del codi
si no la penses fer servir més, o com a mínim deixar context_missing="LOG_ERROR" perquè un SDK sense
dimoni no generi errors al registre.
Errors Habituals i Consells
1. «He activat X-Ray i no veig res». Per ordre de probabilitat: (a) el dimoni no s'està executant
o no escolta al 2000/UDP; (b) falta xray:PutTraceSegments al rol; (c) la petició va arribar amb
Sampled=0 i la decisió es respecta aigües avall; (d) estàs mirant la regió equivocada.
2. Posar anotacions al segment arrel d'una Lambda. S'ignoren en silenci: el segment arrel el controla l'entorn d'execució. Les anotacions van en un subsegment creat per tu.
3. Deixar context_missing amb el seu valor per defecte. Qualsevol codi instrumentat fora d'una
petició HTTP —tasques programades, fils, scripts— llançarà excepcions i tombarà el procés. Posa-hi
sempre LOG_ERROR.
4. Confondre anotació amb metadada. Si no pots filtrar per alguna cosa, és que la vas posar com a metadada. Només les anotacions estan indexades, i n'hi ha un màxim de 50 per traça.
5. Posar dades personals a les anotacions. Correu, nom, adreça, targeta: mai. Fes servir un hash. Les traces les veu tothom qui tingui permís de lectura, i viuen 30 dies.
6. Traçar el health check. El /salud cada 15 segons genera desenes de milers de traces inútils
al mes, embruta els histogrames i desplaça els percentils. És la primera exclusió que cal crear.
7. Mostrejar al 100 % «per no perdre's res». 300 USD al mes en el cas de MercadoFresco, una consola inservible i el dimoni descartant segments. Traçar millor no és traçar més.
8. Perdre el context en fils. El segment viu en una variable per fil. Si engegues un fil nou, li has de passar el segment explícitament. És l'error més freqüent en aplicacions amb paral·lelisme.
9. Buscar traces de fa dos mesos. No existeixen: X-Ray reté 30 dies i punt. Si necessites anàlisi històrica, exporta el que t'interessi a S3 mentre estigui disponible.
10. Esperar que CloudFront aparegui al mapa. No hi apareix. Correlaciona amb l'anotació
cf_id i els registres de la CDN a S3.
11. Instrumentar sense propagar a les crides pròpies. Si el teu codi crida un altre servei amb un
client HTTP que el SDK no pedaça, la capçalera X-Amzn-Trace-Id no viatja i la traça es talla aquí.
Es propaga a mà.
12. Fer servir X-Ray com a substitut dels registres. No ho és. Les traces estan mostrejades: la petició que busques pot no ser-hi. Els registres ho tenen tot. Es complementen: la traça et diu on mirar, el registre et diu què deia.
Consell final: la inversió que més rendiment dona a X-Ray no és la instrumentació, sinó
triar bé cinc o sis anotacions. pedido_id, cliente_hash i num_lineas són les que han
resolt aquest cas. Pensa quines preguntes et faran —«a aquest client li passa sempre», «va començar
amb el desplegament de dimarts», «només amb comandes grans»— i anota exactament el que cal per
respondre-les amb un filtre.
Exercicis
Exercici 1: dissenyar l'estratègia de mostreig d'un servei nou
MercadoFresco llança una API pública perquè les botigues de barri associades consultin estoc i creïn comandes a l'engròs. Trànsit previst:
| Ruta | Peticions/dia | Latència típica | Criticitat |
|---|---|---|---|
GET /api/v1/stock/{sku} |
4.000.000 | 25 ms | Mitjana |
GET /api/v1/catalogo |
120.000 | 300 ms | Mitjana |
POST /api/v1/pedidos-mayoristas |
3.000 | 1,8 s | Màxima |
POST /api/v1/facturas |
800 | 4,0 s | Màxima |
GET /salud |
5.760 | 2 ms | Nul·la |
GET /api/v1/docs |
400 | 40 ms | Nul·la |
Requisits: cada comanda i cada factura han de poder investigar-se individualment; cal poder
diagnosticar la latència d'/stock sense arruïnar-se; el pressupost d'X-Ray és de 25 USD al mes.
Dissenya el conjunt complet de regles de mostreig amb prioritats, dipòsits i taxes. Calcula el nombre de traces mensuals i el cost. Justifica cada regla i digues quines anotacions posaries a l'API.
Exercici 2: llegir una cascada i identificar tres problemes
Aquesta és la cascada d'una traça de 6,45 segons de POST /api/pedidos/confirmar:
[0 ms] mercadofresco-tienda 6.450 ms [12 ms] ├─ subsegment: cargar_configuracion 890 ms [905 ms] ├─ subsegment: SecretsManager GetSecretValue 310 ms [1.220 ms] ├─ subsegment: validar_stock 45 ms [1.270 ms] ├─ subsegment: SELECT ... FROM productos WHERE id=? 95 ms [1.370 ms] ├─ subsegment: SELECT ... FROM productos WHERE id=? 92 ms [1.465 ms] ├─ subsegment: SELECT ... FROM productos WHERE id=? 88 ms [1.560 ms] ├─ (repetit 11 vegades mes, en serie) 1.030 ms [2.595 ms] ├─ subsegment: Invoke mercadofresco-estado-pedido 3.780 ms [2.610 ms] │ └─ SEGMENT Lambda 3.750 ms [2.615 ms] │ ├─ Initialization 2.980 ms [5.600 ms] │ ├─ SNS Publish 95 ms [5.700 ms] │ └─ SELECT ... FROM pedidos 60 ms [6.380 ms] └─ subsegment: respondre 70 ms
Identifica almenys tres problemes diferents, ordena'ls per impacte en mil·lisegons, proposa una solució concreta per a cadascun i estima la latència resultant. Indica també quina anotació o quin subsegment afegiries per poder detectar cadascun d'aquests problemes de manera automàtica en el futur.
Exercici 3: el cas que X-Ray no resol sol
Un dilluns, la Sara avisa que el 3 % de les comandes de l'última setmana no han arribat a generar la
factura. Els clients tenen la comanda confirmada i el repartiment en marxa, però no hi ha factura. No
hi ha errors a CloudWatch, no ha saltat cap alarma, mercadofresco-estado-pedido no registra
Errors, i a X-Ray les traces d'aquestes comandes apareixen completes i en verd.
Arquitectura implicada: la botiga confirma la comanda, publica al tema alertas-mercadofresco, i un
procés de facturació subscrit a aquest tema genera el PDF i el puja a
mercadofresco-informes-analitica. Aquest procés de facturació no està instrumentat amb X-Ray.
Explica: per què X-Ray no ho ha detectat, què li falta a l'observabilitat de MercadoFresco, quina combinació de les eines de 05-01 i 05-02 faries servir per diagnosticar-ho, i quina instrumentació i quina alarma deixaries muntades perquè la propera vegada es detecti en minuts. Sigues concret amb els comandes.
Solucions
Solució 1
Anàlisi prèvia. El trànsit total és de 4.124.960 peticions/dia ≈ 124 milions/mes. Al mostreig per
defecte (5 %) serien 6,2 milions de traces: 30,50 USD. Ens passem del pressupost i, a sobre,
gastaríem gairebé tot en traces d'/stock idèntiques.
Regles proposades:
| Prioritat | Nom | Coincideix | Dipòsit | Taxa | Traces/mes |
|---|---|---|---|---|---|
| 100 | muestreo-api-facturas |
POST /api/v1/facturas |
1/s | 100 % | 24.000 |
| 110 | muestreo-api-pedidos-mayoristas |
POST /api/v1/pedidos-mayoristas |
1/s | 100 % | 90.000 |
| 200 | muestreo-api-catalogo |
GET /api/v1/catalogo |
1/s | 10 % | ~400.000 |
| 300 | muestreo-api-stock |
GET /api/v1/stock/* |
1/s | 0,5 % | ~600.000 |
| 800 | muestreo-api-salud |
GET /salud |
0/s | 0 % | 0 |
| 810 | muestreo-api-docs |
GET /api/v1/docs |
0/s | 0 % | 0 |
| 9000 | muestreo-api-defecto |
Resta | 1/s | 5 % | ~50.000 |
Càlcul detallat d'/stock, que és on hi ha el 97 % del trànsit i on es decideix tot:
- 4.000.000/dia ÷ 86.400 s = 46,3 peticions/segon.
- Dipòsit: 1/s × 2.592.000 s/mes = 2.592.000… que ja és massa. Compte amb el dipòsit quan el volum és alt: el dipòsit és un terra, no un sostre, i amb 46 peticions per segon sempre n'hi ha una per traçar.
Ho refem: amb dipòsit 1/s el terra mensual és de 2,59 milions de traces només d'/stock
(12,95 USD). És massa per al que aporta. Dipòsit 0 i taxa fixa del 0,05 %:
- 124.000.000 × 0,0005 = 62.000 traces/mes. Suficient per a un histograma de latència representatiu d'una ruta que respon en 25 ms.
Taula corregida:
| Prioritat | Nom | Dipòsit | Taxa | Traces/mes |
|---|---|---|---|---|
| 100 | muestreo-api-facturas |
1/s | 100 % | 24.000 |
| 110 | muestreo-api-pedidos-mayoristas |
1/s | 100 % | 90.000 |
| 200 | muestreo-api-catalogo |
1/s | 10 % | ~360.000 |
| 300 | muestreo-api-stock |
0/s | 0,05 % | ~62.000 |
| 800 | muestreo-api-salud |
0/s | 0 % | 0 |
| 810 | muestreo-api-docs |
0/s | 0 % | 0 |
| 9000 | muestreo-api-defecto |
1/s | 5 % | ~50.000 |
| Total | ~586.000 |
Cost: (586.000 − 100.000) × 5 / 1.000.000 = 2,43 USD/mes de registre, més recuperacions dins de la capa gratuïta. Molt per sota dels 25 USD, i amb el 100 % de factures i comandes a l'engròs traçat.
Amb el marge sobrant es pot pujar /catalogo al 25 % i activar Insights sobre el grup de l'API.
La lliçó de l'exercici: el dipòsit és perillós en rutes d'altíssim volum, perquè garanteix un terra d'una traça per segon que en un mes són 2,6 milions. Per al trànsit massiu, dipòsit 0 i taxa molt baixa; per al trànsit escàs i valuós, dipòsit alt i taxa 100 %.
Anotacions proposades per a l'API:
| Anotació | Motiu |
|---|---|
tienda_id |
Cada botiga associada és un client; cal poder aïllar-la |
factura_id / pedido_mayorista_id |
Requisit explícit: investigació individual |
num_lineas |
El mateix que a la botiga: la lentitud creix amb la mida |
version_api |
v1, v2: comparar versions |
plan_tarifa |
Els clients premium tenen millor latència? |
sku |
No: 40.000 valors. Aniria com a metadada o al registre |
Solució 2
Problema 1 — Arrencada en fred de la Lambda: 2.980 ms (46 % del total).
El subsegment Initialization de gairebé 3 segons és una arrencada en fred patològica. El normal en
Python és 200-600 ms; 2.980 ms indica import molt pesats —típicament pandas, numpy, un SDK
sencer— o una connexió establerta a l'àmbit del mòdul.
Solucions, per ordre de cost-benefici:
- Revisar els
importi moure alhandlerels que només es fan servir en alguns camins. - Fer servir
boto3.client()a nivell de mòdul (això està bé: es reutilitza entre invocacions) però no obrir-hi connexions a la base de dades. - Reduir la mida del paquet de desplegament; fer servir capes per a les dependències grans.
- Si després d'això continua sent alt i la latència importa: concurrència provisionada (02-05), que elimina l'arrencada en fred a canvi de pagar per capacitat reservada.
Estalvi estimat: de 2.980 a ~400 ms. −2.580 ms.
Problema 2 — L'N+1, una altra vegada: 1.305 ms (20 %).
Catorze SELECT ... FROM productos WHERE id=? en sèrie, d'uns 93 ms cadascun. El mateix patró del cas
guiat, en una altra ruta.
Solució: WHERE id = ANY(...) en una sola consulta. Estalvi: d'1.305 a ~100 ms. −1.205 ms.
Nota: 93 ms per un SELECT per clau primària també és alt. Val la pena revisar si hi ha un índex
adequat, si la connexió s'estableix a cada consulta (no hi ha agrupació de connexions), o si la
instància d'RDS està saturada. Un subsegment conectar_bd separat ho aclariria.
Problema 3 — Configuració carregada a cada petició: 890 ms (14 %).
cargar_configuracion de 890 ms al principi de cada petició és configuració llegida de disc, de
xarxa o de Parameter Store en calent. Hauria de carregar-se un cop en arrencar el procés i desar-se a
memòria.
Solució: carregar a l'inici del procés i refrescar cada N minuts en un fil de fons. Estalvi: de 890 a ~1 ms. −889 ms.
Problema 4 — Secrets Manager a la ruta crítica: 310 ms (5 %).
GetSecretValue a cada petició. Com vam veure a 04-03, el secret rota cada 30 dies: no hi ha cap raó
per llegir-lo a cada compra. Es desa a la memòria cau amb un TTL de 5-15 minuts i es reintenta en
rebre un error d'autenticació.
Estalvi: de 310 a ~0 ms en el 99,9 % de les peticions. −310 ms.
Problema 5 — La crida a la Lambda és síncrona i bloquejant.
Els 3.780 ms de l'Invoke són enterament a la ruta crítica del client. Però, de debò cal esperar que
es calculi l'estat de la comanda abans de respondre «confirmada»? Gairebé amb tota seguretat no: és un
bon candidat a invocació asíncrona o a una cua (07-01).
Estalvi potencial: els 3.780 ms sencers desapareixen del que el client percep.
Resum ordenat per impacte:
| # | Problema | Estalvi |
|---|---|---|
| 1 | Arrencada en fred de la Lambda | −2.580 ms |
| 2 | N+1 de productes | −1.205 ms |
| 3 | Configuració a cada petició | −889 ms |
| 4 | Secret sense memòria cau | −310 ms |
| 5 | Invocació síncrona innecessària | −3.780 ms (arquitectural) |
Latència resultant aplicant 1-4: 6.450 − 4.984 = ~1.470 ms. Aplicant-hi a més el 5: ~430 ms. De 6,45 s a menys de mig segon.
Instrumentació per detectar-ho automàticament en el futur:
| Què afegir | Detecta |
|---|---|
Subsegment conectar_bd separat de la consulta |
Falta d'agrupació de connexions |
Anotació num_consultas_sql per petició |
L'N+1, amb un filtre annotation.num_consultas_sql > 10 |
Anotació cache_config_hit (booleà) |
Configuració recarregada indegudament |
Anotació arranque_frio a la Lambda |
Traces afectades per arrencada en fred |
Alarma sobre el p99 de Duration de la Lambda |
La regressió d'arrencada en fred |
| X-Ray Insights al grup | Canvis de comportament no previstos |
L'anotació num_consultas_sql es mereix un comentari: és una anotació derivada, calculada per
la mateixa aplicació comptant les consultes de la petició. Amb ella, un filtre
annotation.num_consultas_sql > 10 troba tots els N+1 del sistema, presents i futurs, sense
saber per endavant on són. És el tipus d'anotació que distingeix una instrumentació pensada d'una
copiada.
Solució 3
Per què X-Ray no ho ha detectat, i és important entendre-ho bé.
X-Ray traça el que està instrumentat. El procés de facturació no ho està, així que per a X-Ray
no existeix. Les traces de la botiga acaben a SNS Publish amb estat 200 —el missatge sí que
es va publicar correctament— i tot apareix verd. La fallada és després de l'últim punt
instrumentat, i aquest és el punt cec estructural de qualsevol sistema de traces.
I hi ha un segon motiu, més subtil: encara que instrumentessis el facturador, el patró és asíncron. La botiga publica i se n'oblida. La traça de la botiga acaba en publicar; la del facturador seria una traça diferent, disparada pel missatge. X-Ray les relaciona si el SDK propaga la capçalera a través d'SNS (ho fa, als atributs del missatge), però l'absència d'una traça no genera cap senyal. Ningú no està comptant quants missatges s'haurien d'haver processat.
El que li falta a l'observabilitat de MercadoFresco: no té cap comprovació que les dues meitats d'un procés asíncron quadrin. És el buit clàssic de les arquitectures basades en esdeveniments, i es resol amb reconciliació, no amb traces.
Diagnòstic, pas a pas:
1. Quantificar i acotar en el temps. Consulta directa a la base de dades per saber quants i quan:
SELECT date_trunc('hour', p.confirmado_en) AS hora,
count(*) FILTER (WHERE f.id IS NULL) AS sin_factura,
count(*) AS total
FROM pedidos p
LEFT JOIN facturas f ON f.pedido_id = p.id
WHERE p.confirmado_en > now() - interval '7 days'
GROUP BY 1 ORDER BY 1;Si les fallades es concentren en unes hores concretes, hi ha una causa puntual; si estan repartides uniformement al 3 %, hi ha una fallada probabilística —temps d'espera esgotat, condició de cursa, límit de concurrència—.
2. Comprovar la baula d'SNS. Les mètriques d'SNS a CloudWatch diuen si el missatge va sortir i si el lliurament va fallar:
aws cloudwatch get-metric-statistics \
--namespace AWS/SNS --metric-name NumberOfNotificationsFailed \
--dimensions Name=TopicName,Value=alertas-mercadofresco \
--start-time $(date -d '7 days ago' -u +%FT%TZ) \
--end-time $(date -u +%FT%TZ) \
--period 3600 --statistics Sum \
--profile mercadofresco-dev --region eu-west-1Si NumberOfNotificationsFailed és zero, el missatge va arribar al facturador i el problema és dins
d'ell. Si no és zero, el problema és de lliurament i cal mirar la política de reintents d'SNS (07-02)
i si hi ha cua de missatges fallits.
3. Mirar els registres del facturador amb Logs Insights. Aquí és on 05-01 fa la feina:
fields @timestamp, @message, pedido_id, error | filter ispresent(pedido_id) | stats count() as eventos by pedido_id | filter eventos < 2 | limit 50
És a dir: comandes que van entrar al facturador però no van arribar a registrar la línia de finalització. I la comprovació creuada definitiva, buscant una comanda concreta sense factura a tots els grups alhora:
aws logs start-query \
--log-group-names /mercadofresco/tienda/aplicacion \
/mercadofresco/facturacion/aplicacion \
--start-time $(date -d '3 days ago' +%s) --end-time $(date +%s) \
--query-string 'fields @timestamp, @log, @message
| filter @message like /48213/
| sort @timestamp asc' \
--profile mercadofresco-dev --region eu-west-1Si la comanda apareix a la botiga i no apareix mai a facturació, el missatge s'ha perdut. Si apareix i es talla a mitges, el procés va morir: memòria, temps d'espera esgotat, excepció no capturada.
Diagnòstic probable —i el més freqüent en aquest escenari—: el facturador triga més del que li permet el seu temps d'espera en generar el PDF de comandes grans, mor, i SNS no reintenta indefinidament. Sense cua de missatges fallits, el missatge desapareix en silenci. És un 3 % estable: les comandes més grans.
El que cal deixar muntat, en quatre capes:
a) Instrumentar el facturador amb X-Ray. És el primer i el més obvi:
from aws_xray_sdk.core import xray_recorder, patch_all
xray_recorder.configure(service="mercadofresco-facturacion",
context_missing="LOG_ERROR")
patch_all()Amb anotacions pedido_id i factura_id. A partir d'aquí, la traça d'una comanda inclou la seva
facturació i el filtre service("mercadofresco-facturacion") { fault } troba les fallades.
b) Una cua de missatges fallits. Sense ella no hi ha manera de saber què s'ha perdut. MercadoFresco
ja té el patró muntat amb mercadofresco-miniaturas-fallidas (02-05); aquí cal l'equivalent, i amb
una alarma sobre ApproximateNumberOfMessagesVisible > 0. El patró complet —cues, reintents,
idempotència— és la lliçó 07-05.
c) La mètrica de reconciliació, que és la solució de fons. Un procés que cada 15 minuts compara comandes confirmades amb factures emeses i publica la diferència:
"""Reconciliacio: publica quantes comandes fa mes de 30 minuts que no tenen factura."""
import boto3
cw = boto3.client("cloudwatch", region_name="eu-west-1")
pendents = comptar_comandes_sense_factura(antiguitat_minuts=30)
cw.put_metric_data(
Namespace="MercadoFresco/Tienda",
MetricData=[{
"MetricName": "PedidosSinFactura",
"Dimensions": [{"Name": "Entorno", "Value": "produccion"}],
"Value": pendents,
"Unit": "Count",
}],
)I la seva alarma:
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-pedidos-sin-factura \
--alarm-description "Hi ha comandes confirmades sense factura despres de 30 minuts" \
--namespace MercadoFresco/Tienda --metric-name PedidosSinFactura \
--dimensions Name=Entorno,Value=produccion \
--statistic Maximum --period 900 --evaluation-periods 2 --datapoints-to-alarm 2 \
--threshold 5 --comparison-operator GreaterThanThreshold \
--treat-missing-data breaching \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1Amb --treat-missing-data breaching: si el mateix procés de reconciliació deixa d'executar-se, això
també és un incident.
d) La regla general que cal extreure. En un sistema asíncron, les traces no detecten el que no va passar. Només detecten el que va passar malament. Per al que no va passar cal una mètrica de reconciliació que compari les dues meitats del procés: comandes enfront de factures, missatges enviats enfront de processats, fitxers pujats enfront de miniatures generades.
És exactament el mateix raonament que a 05-01 va portar a posar --treat-missing-data breaching a
mercadofresco-sin-pedidos: l'absència d'un senyal és un senyal, però només si algú l'està
comptant.
Conclusió
MercadoFresco ha passat del «alguna cosa va lenta» al «aquesta crida concreta triga 6,2 segons, i 5,9 són dins d'una consulta». Saps per què les mètriques i els registres no basten: les mètriques agreguen i perden el cas individual, els registres conserven el cas però no la relació causal entre el que va passar a la botiga i el que va passar a la Lambda. Només una traça guarda l'estructura completa d'una petició.
Domines el model: traça, segment per servei i subsegment per tram, amb error (groc,
4xx), fault (vermell, 5xx) i throttle (morat). Coneixes l'ID de traça amb la seva marca de
temps incrustada —i per què això implica els 30 dies de retenció— i la capçalera X-Amzn-Trace-Id
amb els seus camps Root, Parent i Sampled, amb la regla que més despista: la decisió de
mostreig es pren un cop a l'origen i tothom la respecta, així que «he activat X-Ray a la Lambda i no
veig res» gairebé sempre vol dir que va arribar amb Sampled=0.
Saps configurar el mostreig amb el seu dipòsit i la seva taxa fixa, i per què aquesta combinació
traça gairebé tot quan hi ha poc trànsit i un percentatge estable quan n'hi ha molt. Has muntat les
regles de MercadoFresco: 100 % d'/api/pedidos/*, 0 % d'/salud i dels estàtics, 5 % per
defecte. I has après que el dipòsit és traïdor en rutes d'altíssim volum, on 1/s són 2,6 milions de
traces al mes.
Tens clara la distinció que decideix si trobaràs alguna cosa: anotacions indexades i filtrables
(màxim 50, cardinalitat lliure) enfront de metadades que només es veuen en obrir la traça. I saps
que pedido_id com a anotació d'X-Ray és gratis i cercable, mentre que com a dimensió de mètrica a
CloudWatch costaria 190.000 USD al mes: és exactament el buit que X-Ray cobreix. Amb l'advertència de
privacitat corresponent: cliente_hash, mai el correu.
Has instrumentat la botiga amb aws_xray_sdk —xray_recorder.configure amb
context_missing="LOG_ERROR" perquè l'observabilitat no tombi mai l'aplicació, patch_all() que
instrumenta boto3 i psycopg2 de manera que cada consulta genera el seu subsegment, i el
EC2Plugin que afegeix la instància i l'AZ—, has creat subsegments manuals amb in_subsegment, i
saps que en fils cal passar el context a mà. Has desplegat el dimoni a les instàncies de l'ASG,
entenent per què existeix: el SDK escriu UDP local i no espera mai la xarxa. I has activat el
rastreig a mercadofresco-estado-pedido amb una política i Mode=Active, sabent que a Lambda les
anotacions van en un subsegment perquè el segment arrel és de només lectura, i que el subsegment
Initialization és l'arrencada en fred que a 02-05 només podíem intuir.
Saps llegir el mapa de serveis —mida, colors, nodes de client i de recurs, i els tres patrons que
es reconeixen d'un cop d'ull— i cercar amb el llenguatge de filtres: service(), fault,
responsetime > 5, edge(), annotation.pedido_id = "48213". I saps llegir un histograma
bimodal, que és on de debò es veuen les dues poblacions de peticions que un percentil resumeix en un
número.
I has tancat el cas que arrossegàvem des del mòdul 4. La comanda 48213: una sola cerca per
anotació, la cascada, i allà hi era —39 subsegments consecutius de 190 ms, el 91 % del temps
total, un N+1—. Confirmat com a sistemàtic amb annotation.num_lineas > 25 AND responsetime > 4,
quantificat en 5.610 comandes afectades en 30 dies, corregit amb WHERE id = ANY(...), i verificat:
p95 de 6,8 s a 0,44 s, i de regal DatabaseConnections de 185 a 96 al pic del divendres. Compara
això amb les quatre consultes i el full de càlcul de 05-01.
Coneixes X-Ray Insights amb les seves notificacions via EventBridge, ServiceLens com la vista que uneix mètrica → mapa → traça → subsegment → registre, i OpenTelemetry amb ADOT com l'estàndard obert que avui AWS recomana per a projectes nous, amb la decisió de MercadoFresco anotada i amb data de revisió en lloc d'amagada. I saps que tot això costa uns 10,60 USD al mes gràcies a una estratègia de mostreig que traça millor, no més, enfront dels 300 USD de traçar-ho tot.
Queda una pregunta del mòdul 4 sense respondre, i és la que ni les mètriques ni les traces poden
contestar. CloudWatch sap el que l'aplicació diu d'ella mateixa. X-Ray sap per on ha passat una
petició. Cap dels dos no sap qui ha cridat l'API d'AWS. Ningú no sap encara qui va desxifrar la
darrera còpia de la base de dades amb alias/mercadofresco-datos, ni qui va llegir el secret
mercadofresco/produccion/rds/mfadmin, ni des de quina adreça IP, ni si alguna d'aquestes crides va
fallar amb AccessDenied perquè algú estava provant portes.
Aquest registre existeix, es diu AWS CloudTrail i ho porta gravant tot des del primer dia sense
que ningú l'hagi mirat. A la lliçó 05-03, «AWS CloudTrail», veurem la diferència essencial entre
registrar crides a l'API i registrar el que diu la teva aplicació, l'historial gratuït de 90 dies
enfront d'un trail persistent, com crear trail-mercadofresco cap a un bucket xifrat amb validació
d'integritat —i per què aquest bucket ha de ser impossible d'esborrar—, l'anatomia comentada d'un
esdeveniment real de kms:Decrypt, la diferència de cost entre esdeveniments de gestió i
esdeveniments de dades, i com investigar amb Athena i SQL qui va assumir un rol, qui va llegir un
secret i quines crides van fallar amb AccessDenied.
Curs d'AWS
Mòdul 1: Introducció a AWS
- Què és AWS?
- Configuració del teu compte d'AWS
- Infraestructura global d'AWS
- Consola d'administració d'AWS
- AWS CLI i SDK
Mòdul 2: Serveis principals d'AWS
Mòdul 3: Xarxes i lliurament de contingut
- Amazon VPC
- Grups de seguretat i llistes de control d'accés
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Mòdul 4: Seguretat i identitat
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager i Parameter Store
- AWS Shield
- AWS WAF
Mòdul 5: Monitoratge i gestió
- Amazon CloudWatch
- AWS X-Ray i traçabilitat distribuïda
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Mòdul 6: Bases de dades
- Com triar la base de dades adequada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Mòdul 7: Integració d'aplicacions
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrons d'integració: idempotència, reintents i cues de missatges fallits
