Servidor disponible solo los jueves de 10:30 a 12:30 y los viernes de 10:00 a 13:00 (hora de Madrid); fuera de ese horario está apagado. Fuera del horario, usa la documentación local del material que te descargaste (el README.md de cada carpeta).
G214 · CUNEF
Inicio / tema3 / 03.10-estacionesdynamodb

Tema 3 · 10 · Ejercicio adicional C: estaciones de bicicleta en DynamoDB (clave compuesta)

Presentación del Tema 3: «Ejercicio C» de «Ejercicios adicionales» (DynamoDB con clave de partición + clave de ordenación). Duración: unos 20 minutos, más ~9 minutos de espera si creas el índice secundario global. Coste: céntimos (tabla on-demand de 600 ítems pequeños).

¿Dudas con un script? Todos los scripts de esta carpeta traen su propia ayuda: python3 genera-estaciones.py --ayuda (también vale --help o -h) explica los pasos y las opciones. Este README.md es el paso a paso de la actividad: tenlo a mano y consúltalo antes de preguntar.

Etiquetas de lo que creas. Todo recurso que crees a mano (máquina, volumen, bucket, base de datos, tabla, función...) ponle estas etiquetas, al crearlo (apartado Tags / Etiquetas) o después desde su pestaña de etiquetas: curso = G214, universidad = CUNEF y actividad = 03.10-estacionesdynamodb. Si algún día hay que borrar recursos a mano, se localizan todos de golpe con aws resourcegroupstaggingapi get-resources --region eu-north-1 --tag-filters Key=actividad,Values=03.10-estacionesdynamodb. Más detalle (cómo etiquetar con la CLI, cómo buscar y qué hacer en una emergencia): README principal, sección «Etiquetas».

Qué vas a hacer

Vas a generar 600 lecturas simuladas de estaciones de bicicleta pública, cargarlas en una tabla de DynamoDB con clave compuesta (estacion_id + ts) y comprobar con --return-consumed-capacity la diferencia de coste entre query (lee solo lo que pides) y scan (lee toda la tabla). Al final creas un índice secundario global para resolver la pregunta «¿qué estaciones no tienen bicis ahora mismo?».

genera-estaciones.py ──> batch_estaciones_00.json … _23.json (24 ficheros de 25 ítems)
aws dynamodb batch-write-item ──> tabla Estaciones (estacion_id HASH, ts RANGE) ──> query / scan / índice estado-ts-index

Qué se crea en AWS: la tabla Estaciones (modo PAY_PER_REQUEST) y, si haces la última parte, el índice secundario global estado-ts-index. Todo se hace en CloudShell (región eu-north-1). Las lecturas reales de estaciones en tiempo real no son de descarga libre (Barcelona exige un token y Madrid solo publica información de las estaciones), por eso se generan simuladas.

Qué contiene esta carpeta

Fichero Para qué sirve En qué paso se usa
genera-estaciones.py Genera 600 lecturas de estaciones de bicicleta (20 estaciones E01..E20 × 30 lecturas, una cada 5 minutos desde las 17:00 del 2026-03-14) con bicis_libres, anclajes_libres y estado (NORMAL, SIN_BICIS o LLENA), en ficheros batch_estaciones_00.json... de 25 ítems (el máximo de batch-write-item; con 30 ítems en una llamada da Member must have length less than or equal to 25). Solo usa la librería estándar Paso 2
README.md Esta guía (incluye los comandos de la AWS CLI) Siempre

Antes de empezar

Comprobación previa en CloudShell: aws sts get-caller-identity y echo "$AWS_REGION" (debe imprimir eu-north-1; si no, cierra CloudShell, cambia la región de la consola y ábrelo de nuevo).

Cómo llevar los ficheros a donde toque

Se hace en CloudShell. Comprime esta carpeta (03.10-estacionesdynamodb) en un zip, abre CloudShell en eu-north-1, Actions > Upload file y sube el zip. Después:

unzip -o 03.10-estacionesdynamodb.zip && cd 03.10-estacionesdynamodb && chmod -R u+rwX .
sed -i 's/\r$//' *.py
chmod +x *.py
ls

Si ya subiste g214-materiales.zip (README principal), no necesitas este zip: entra con cd ~/g214-materiales/tema3/03.10-estacionesdynamodb y, si algún fichero da Permission denied, ejecuta allí chmod -R u+rwX . && chmod +x *.py.

Qué verás: ls muestra README.md y genera-estaciones.py. (sed quita los finales de línea de Windows y chmod +x da permiso de ejecución.) Ejecuta todo desde dentro de esta carpeta. También puedes ejecutarlo en tu PC con la AWS CLI configurada; en PowerShell, ver el paso 3.

Paso a paso

Paso 1. Crear la tabla Estaciones. Con el comando de la presentación: create-table con estacion_id como clave de partición (HASH), ts como clave de ordenación (RANGE) y PAY_PER_REQUEST. Equivalente en CLI (sin probar tal cual es la referencia):

aws dynamodb create-table --table-name Estaciones \
  --attribute-definitions AttributeName=estacion_id,AttributeType=S AttributeName=ts,AttributeType=S \
  --key-schema AttributeName=estacion_id,KeyType=HASH AttributeName=ts,KeyType=RANGE \
  --billing-mode PAY_PER_REQUEST --region eu-north-1
aws dynamodb wait table-exists --table-name Estaciones --region eu-north-1

Paso 2. Generar los datos.

python3 genera-estaciones.py            # 600 ítems en 24 ficheros (o: genera-estaciones.py 6 5 -> 30 ítems en 2 ficheros)
ls batch_estaciones_*.json

Qué verás: 24 ficheros batch_estaciones_00.json ... batch_estaciones_23.json (cada uno con 25 ítems).

Paso 3. Cargar los lotes y comprobar.

for f in batch_estaciones_*.json; do
  aws dynamodb batch-write-item --request-items file://$f --region eu-north-1
done
aws dynamodb scan --table-name Estaciones --select COUNT --region eu-north-1

Qué verás: cada batch-write-item imprime un JSON de tres líneas ({, "UnprocessedItems": {} y }; si UnprocessedItems no estuviera vacío, reenvía esos ítems) y el scan --select COUNT da "Count": 600. En PowerShell sustituye el bucle por Get-ChildItem batch_estaciones_*.json | ForEach-Object { aws dynamodb batch-write-item --request-items file://$($_.Name) --region eu-north-1 } (o usa CloudShell: las comillas simples de los JSON dan problemas en Windows PowerShell 5.1).

Paso 4. query frente a scan. Con los comandos de la presentación (con --return-consumed-capacity TOTAL). Resultados para contrastar (medidos): el query de E12 desde las 18:00 devuelve 18 ítems (Count 18, ScannedCount 18) y consume 0.5; todas las lecturas de E12, 30 ítems, también 0.5; el scan con --filter-expression "estacion_id = :e" devuelve 30 pero lee 600 (ScannedCount 600) y consume 6.5 (13.0 con --consistent-read). Con solo 30 ítems en la tabla ya se ve la diferencia (query 0.5 frente a scan 2.0).

Paso 5. Índice secundario global para «estaciones sin bicis ahora mismo». Hace falta un índice (estado + ts): crearlo con 600 ítems tarda unos 9 minutos hasta IndexStatus ACTIVE, y después una query con --index-name estado-ts-index lo resuelve con 0.5. Comandos (los mismos que se midieron; en una tabla On-demand el índice no necesita capacidad aparte). Acota ts a una ventana reciente: sin esa condición salen todas las lecturas históricas con SIN_BICIS (28 en total), no las de «ahora»:

aws dynamodb update-table --table-name Estaciones --region eu-north-1   --attribute-definitions AttributeName=estado,AttributeType=S AttributeName=ts,AttributeType=S   --global-secondary-index-updates '[{"Create":{"IndexName":"estado-ts-index","KeySchema":[{"AttributeName":"estado","KeyType":"HASH"},{"AttributeName":"ts","KeyType":"RANGE"}],"Projection":{"ProjectionType":"ALL"}}}]'
aws dynamodb describe-table --table-name Estaciones --region eu-north-1   --query "Table.GlobalSecondaryIndexes[0].IndexStatus" --output text      # espera a ACTIVE (~9 min)
aws dynamodb query --table-name Estaciones --index-name estado-ts-index --region eu-north-1   --key-condition-expression "estado = :s AND ts >= :d"   --expression-attribute-values '{":s":{"S":"SIN_BICIS"},":d":{"S":"2026-03-14T19:25:00"}}'   --return-consumed-capacity TOTAL

Qué verás: con ts >= 19:25 sale 1 ítem y 0.5 RCU (el scan equivalente lee los 600 y cuesta 6.5).

Errores frecuentes

Síntoma Causa Solución
Member must have length less than or equal to 25 Un batch-write-item con más de 25 ítems Usa los ficheros que genera el script (25 ítems cada uno)
ResourceNotFoundException al cargar o consultar La tabla no existe todavía, o está en otra región aws dynamodb wait table-exists --table-name Estaciones --region eu-north-1 y comprueba --region
UnprocessedItems no está vacío DynamoDB no procesó algún ítem Reenvía esos ítems
Permission denied al lanzar ./genera-estaciones.py Sin permiso de ejecución chmod +x genera-estaciones.py (o python3 genera-estaciones.py)
bad interpreter o $'\r': command not found Finales de línea de Windows sed -i 's/\r$//' *.py y repite
En PowerShell 5.1 los JSON con comillas simples dan error Las comillas simples se interpretan distinto Usa CloudShell
El scan --select COUNT da menos de 600 Faltan lotes por cargar Repite el bucle (los put repetidos no duplican: misma clave)
La query al índice no devuelve nada El índice aún no está ACTIVE, o la ventana de ts no contiene ninguna lectura SIN_BICIS Espera a ACTIVE (~9 min) y usa ts >= 2026-03-14T19:25:00

Las filas de esta tabla distintas de la primera y de la de PowerShell son deducciones, sin probar.

Limpieza

aws dynamodb delete-table --table-name Estaciones --region eu-north-1
rm batch_estaciones_*.json

El borrado de la tabla se lleva también el índice. Comprueba que ya no está:

aws dynamodb list-tables --region eu-north-1

Qué debes ver: la lista sin Estaciones. Esta actividad no comparte recursos con las demás (la tabla Usuarios es de 03.07); la última carpeta que borra la RDS y Adminer es la 03.09.

Cómo sabes que has terminado

Anterior: 03.09 · Ejercicios adicionales A y B · Siguiente: 03.11 · Ejercicio D (CSV a Parquet)