Identificador: PERF-001
Version: 1.0.0
Fecha: 2025-12-08
Autor: SpecQueen Technical Division
Estado: Aprobado
1. Tabla de Contenidos
- Introduccion
- Arquitectura y Consideraciones
- SLAs por Categoria
- Metricas de Servidor
- Metricas de Cliente
- Metricas de Sincronizacion
- Metricas de Cifrado E2E
- Alerting y Monitoring
- Capacity Planning
- Degradation Strategies
2. Introduccion
2.1. Proposito
Este documento define los Service Level Agreements (SLAs), benchmarks de rendimiento y metricas clave para el ecosistema MedTime. Establece los objetivos de performance que deben cumplirse en todas las capas del sistema.
┌────────────────────────────────────────────────────────────────┐
│ PERFORMANCE PRINCIPLES │
├────────────────────────────────────────────────────────────────┤
│ │
│ 1. CLIENT-FIRST: 95% de operaciones son locales │
│ → Latencia minima, funciona offline │
│ │
│ 2. ZERO-KNOWLEDGE OVERHEAD: Cifrado tiene costo │
│ → Aceptable trade-off por privacidad │
│ │
│ 3. PERCEIVED PERFORMANCE: UX sobre metricas puras │
│ → Optimistic UI, skeleton loading, progressive │
│ │
│ 4. BATTERY-CONSCIOUS: Mobile es battery-sensitive │
│ → Background tasks optimizados, no polling │
│ │
│ 5. NETWORK-RESILIENT: Red es unreliable │
│ → Retry con backoff, offline-first │
│ │
└────────────────────────────────────────────────────────────────┘
2.3. Referencias
| Documento |
Relevancia |
| 02-arquitectura-cliente-servidor.md |
Arquitectura dual 95%/5% |
| 04-seguridad-cliente.md |
Cifrado E2E overhead |
| 06-infraestructura.md |
Monitoring infrastructure |
| 07-testing-strategy.md |
Performance testing |
| MOB-IOS-001, MOB-AND-001 |
Mobile architecture |
3. Arquitectura y Consideraciones
3.1. Distribucion de Carga
┌─────────────────────────────────────────────────────────────────┐
│ CARGA POR UBICACION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CLIENTE (95% de operaciones) │
│ ├── Lectura de datos: 100% local │
│ ├── Escritura de datos: 100% local (sync async) │
│ ├── Cifrado/Descifrado: 100% local │
│ ├── Alertas/Notificaciones: 100% local │
│ ├── Busqueda en catalogo local: 100% local │
│ └── ML/Predictions: 100% local │
│ │
│ SERVIDOR (5% de operaciones) │
│ ├── Autenticacion: Login, refresh, logout │
│ ├── Sincronizacion: Push/Pull blobs cifrados │
│ ├── Catalogos publicos: Busqueda online (>100 items) │
│ ├── Push notifications: Trigger desde servidor │
│ └── OCR processing: Temporal, sin retener │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2. Zero-Knowledge Overhead
El cifrado E2E agrega overhead pero es aceptable por privacidad:
| Operacion |
Sin Cifrado |
Con E2E |
Overhead |
| Guardar medicamento |
5ms |
15ms |
+10ms |
| Leer medicamento |
3ms |
8ms |
+5ms |
| Sync blob (1KB) |
20ms |
50ms |
+30ms |
| Key derivation (Argon2id) |
N/A |
200ms |
One-time |
Nota: El overhead de cifrado es constante y predecible, no escala con el numero de usuarios.
4. SLAs por Categoria
4.1. Autenticacion
| Operacion |
Target P50 |
Target P95 |
Max |
Degraded |
| Login (email/password) |
300ms |
500ms |
1s |
2s |
| Login (biometric) |
100ms |
200ms |
500ms |
1s |
| Token refresh |
100ms |
200ms |
500ms |
1s |
| Logout |
50ms |
100ms |
200ms |
500ms |
| Password reset email |
500ms |
1s |
2s |
5s |
Authentication SLAs:
login_email:
p50: 300ms
p95: 500ms
p99: 800ms
max: 1000ms
error_budget: 0.1% # 99.9% success rate
login_biometric:
p50: 100ms # Local Keychain/Keystore access
p95: 200ms
p99: 300ms
max: 500ms
error_budget: 0.01% # 99.99% success rate
token_refresh:
p50: 100ms
p95: 200ms
p99: 400ms
max: 500ms
error_budget: 0.1%
session_validation:
p50: 5ms # Local token check
p95: 10ms
max: 20ms
note: "100% local, no network"
4.2. Operaciones CRUD
| Operacion |
Target P50 |
Target P95 |
Max |
Notas |
| Crear medicamento |
50ms |
100ms |
200ms |
Local + async sync |
| Leer medicamento |
10ms |
20ms |
50ms |
100% local |
| Actualizar medicamento |
30ms |
60ms |
100ms |
Local + async sync |
| Eliminar medicamento |
20ms |
40ms |
80ms |
Soft delete local |
| Listar medicamentos (50) |
20ms |
50ms |
100ms |
Local query |
| Listar medicamentos (500) |
100ms |
200ms |
400ms |
Con paginacion |
CRUD SLAs:
create_medication:
p50: 50ms
breakdown:
- validation: 5ms
- encryption: 10ms
- local_write: 20ms
- ui_update: 5ms
- queue_sync: 10ms # Async
note: "Sync ocurre en background"
read_medication:
p50: 10ms
breakdown:
- query: 5ms
- decryption: 3ms
- mapping: 2ms
note: "100% local, no network"
list_medications:
p50_50_items: 20ms
p50_100_items: 40ms
p50_500_items: 100ms
virtualization: required_for_100plus
4.3. Sincronizacion (DV2-P3)
PERF-BAJO-005: Perfect tier sync latency sin diferenciacion
Remediacion: Especificar sync latency < 500ms para tier Perfect
| Operacion |
Free/Pro P50 |
Perfect P50 |
Free/Pro P95 |
Perfect P95 |
Max |
Notas |
| Sync pull (initial) |
2s |
2s |
5s |
5s |
10s |
Sin diferenciacion |
| Sync pull (incremental) |
500ms |
300ms |
1s |
500ms |
3s |
Perfect: prioritized |
| Sync push (1 item) |
200ms |
100ms |
500ms |
300ms |
1s |
Perfect: dedicated |
| Sync push (batch 10) |
800ms |
500ms |
1.5s |
1s |
3s |
Perfect: faster |
| Conflict resolution |
50ms |
50ms |
100ms |
100ms |
200ms |
Sin diferenciacion (local) |
Sync SLAs:
initial_sync:
description: "Primera sincronizacion completa"
all_tiers:
p50: 2s
p95: 5s
max: 10s
conditions:
- max_items: 1000
- network: 4G_or_better
degraded_mode:
trigger: ">10s"
action: "Show progress, allow cancel"
note: "Sin diferenciacion por tier (operacion poco frecuente)"
incremental_sync:
description: "Sync de cambios desde ultimo sync"
free_tier:
p50: 500ms
p95: 1s
max: 3s
frequency: "On app foreground + every 30min background"
priority: normal
concurrency: shared_pool
pro_tier:
p50: 500ms
p95: 1s
max: 3s
frequency: "On app foreground + every 15min background"
priority: normal
concurrency: shared_pool
perfect_tier:
p50: 300ms # DV2-P3: 40% faster
p95: 500ms # DV2-P3: 50% faster
max: 2s
frequency: "On app foreground + every 5min background + realtime trigger"
priority: high
concurrency: dedicated_worker
optimizations:
- dedicated_api_worker
- priority_queue_bypass
- connection_keep_alive
- http2_multiplexing
- response_streaming
push_single:
description: "Enviar un blob cifrado"
free_pro_tier:
p50: 200ms
p95: 500ms
max: 1s
queue_strategy: "FIFO shared"
perfect_tier:
p50: 100ms # DV2-P3: 50% faster
p95: 300ms # DV2-P3: 40% faster
max: 800ms
queue_strategy: "Priority dedicated"
optimizations:
- skip_rate_limit_checks
- dedicated_connection_pool
- immediate_processing
retry:
attempts: 3
backoff: exponential
max_delay: 30s
push_batch:
description: "Enviar multiples blobs"
free_pro_tier:
10_items:
p50: 800ms
p95: 1.5s
max: 3s
perfect_tier:
10_items:
p50: 500ms # DV2-P3: 37% faster
p95: 1s # DV2-P3: 33% faster
max: 2s
optimization: "Parallel uploads (up to 5 concurrent)"
conflict_resolution:
description: "Resolver conflicto LWW"
all_tiers:
p50: 50ms
max: 200ms
strategy: "Last Write Wins (client timestamp)"
note: "100% local, no network, sin diferenciacion por tier"
perfect_tier_benefits_summary:
incremental_sync: "40-50% faster"
push_single: "40-50% faster"
push_batch: "30-40% faster"
implementation:
- "Dedicated API worker threads"
- "Priority in server queue"
- "Connection keep-alive (no re-handshake)"
- "HTTP/2 multiplexing enabled"
- "Skip rate limit checks"
- "Monitoring alert if SLAs missed"
4.4. Alertas y Notificaciones (DV2-P3)
PERF-BAJO-004: Push delivery latency sin SLA
Remediacion: Definir P95 target para push notifications
| Operacion |
Target P50 |
Target P95 |
Max |
Notas |
| Programar alerta local |
10ms |
20ms |
50ms |
AlarmManager/UNNotification |
| Mostrar notificacion |
50ms |
100ms |
200ms |
Desde trigger |
| Push notification delivery |
2s |
5s |
30s |
FCM/APNs (DV2-P3) |
| Snooze action |
20ms |
50ms |
100ms |
Reprogramar |
Alert SLAs:
schedule_local_alert:
p50: 10ms
p95: 20ms
max: 50ms
platforms:
ios: UNUserNotificationCenter
android: AlarmManager + WorkManager
reliability: "99.99% delivery"
notification_display:
p50: 50ms
p95: 100ms
max: 200ms
includes:
- rich_content_load
- action_buttons
- sound_playback
push_notification_delivery:
description: "End-to-end push notification delivery"
p50: 2s
p95: 5s # DV2-P3: SLA definido
p99: 15s
max: 30s
timeout: 60s # Considerar fallback despues de 60s
providers:
ios: APNs
android: FCM
measurement:
start: "Server sends push request to FCM/APNs"
end: "Device displays notification"
note: "Best effort by providers, not guaranteed delivery"
factors_affecting_latency:
- device_online_status: "Doze/App Standby on Android"
- network_conditions: "WiFi vs cellular"
- provider_queue: "FCM/APNs backend load"
- device_battery: "Power save mode delays"
fallback_strategy:
60s: "Consider SMS escalation for critical alerts"
300s: "Log delivery failure, retry on next sync"
push_delivery_by_priority:
high_priority:
description: "Critical medication reminders"
p50: 1s
p95: 3s
max: 10s
fcm_priority: "high"
apns_priority: 10
behavior: "Wake device, bypass Doze"
normal_priority:
description: "Non-critical notifications"
p50: 5s
p95: 15s
max: 60s
fcm_priority: "normal"
apns_priority: 5
behavior: "Delivered when convenient"
push_delivery_monitoring:
metrics:
- delivery_success_rate # Target: >98%
- p50_latency
- p95_latency # Target: <5s
- p99_latency
- timeout_rate # Target: <1%
alerts:
warning:
condition: "p95 > 8s for 5min"
action: "Notify on-call"
critical:
condition: "p95 > 15s OR success_rate < 95%"
action: "Page on-call + investigate providers"
escalation_sms:
p50: 10s
p95: 30s
max: 60s
provider: Twilio
reliability: "99.9%"
trigger_conditions:
- push_delivery_timeout_60s
- critical_medication_missed
- user_preference_sms_backup
4.5. Catalogos y Busqueda
| Operacion |
Target P50 |
Target P95 |
Max |
Notas |
| Busqueda catalogo local |
20ms |
50ms |
100ms |
<100 items embebidos |
| Busqueda catalogo online |
200ms |
500ms |
1s |
Medicamentos publicos |
| Autocomplete |
100ms |
200ms |
300ms |
Debounce 300ms |
| Cargar detalle |
50ms |
100ms |
200ms |
Local o cache |
Catalog SLAs:
local_search:
description: "Busqueda en catalogo embebido (<100 items)"
p50: 20ms
p95: 50ms
max: 100ms
catalogs:
- units
- forms
- routes
- frequencies
- categories
online_search:
description: "Busqueda en catalogo de medicamentos (>100k items)"
p50: 200ms
p95: 500ms
max: 1s
requirements:
- min_query_length: 3
- debounce: 300ms
- max_results: 50_per_page
privacy: "INV-015 consent required"
autocomplete:
p50: 100ms
p95: 200ms
max: 300ms
debounce: 300ms
prefetch: top_20_common
5. Metricas de Servidor
5.1. Endpoints Criticos
| Endpoint |
RPS Target |
P50 |
P95 |
P99 |
Max |
POST /auth/login |
100 |
200ms |
400ms |
600ms |
1s |
POST /auth/refresh |
500 |
50ms |
100ms |
200ms |
500ms |
POST /sync/push |
200 |
300ms |
600ms |
1s |
2s |
GET /sync/pull |
200 |
300ms |
800ms |
1.5s |
3s |
GET /catalog/medications |
300 |
100ms |
200ms |
400ms |
500ms |
POST /alerts/emergency |
50 |
100ms |
200ms |
300ms |
500ms |
Server Endpoints Performance:
auth_login:
path: POST /v1/auth/login
rps_target: 100
latency:
p50: 200ms
p95: 400ms
p99: 600ms
max: 1000ms
dependencies:
- Firebase Auth
- PostgreSQL (user lookup)
bottleneck: "Firebase Auth latency"
sync_pull:
path: GET /v1/sync/pull
rps_target: 200
latency:
p50: 300ms
p95: 800ms
p99: 1500ms
max: 3000ms
factors:
- items_count: logarithmic
- blob_sizes: linear
- network_conditions: variable
optimization:
- compression: gzip
- pagination: cursor_based
- delta_sync: since_version
sync_push:
path: POST /v1/sync/push
rps_target: 200
latency:
p50: 300ms
p95: 600ms
p99: 1000ms
max: 2000ms
factors:
- blob_size: linear
- transaction_size: items * 10ms
optimization:
- batching: up_to_10_items
- async_processing: for_non_critical
catalog_search:
path: GET /v1/catalog/medications
rps_target: 300
latency:
p50: 100ms
p95: 200ms
p99: 400ms
max: 500ms
optimization:
- full_text_search: PostgreSQL FTS
- caching: Redis (5min TTL)
- rate_limiting: 100/min per user
emergency_alert:
path: POST /v1/alerts/emergency
rps_target: 50
latency:
p50: 100ms
p95: 200ms
p99: 300ms
max: 500ms
priority: HIGHEST
sla: "99.99% availability"
5.2. Error Rates
| Categoria |
Target |
Degraded |
Critical |
| 2xx Success |
>99.5% |
99-99.5% |
<99% |
| 4xx Client Errors |
<2% |
2-5% |
>5% |
| 5xx Server Errors |
<0.1% |
0.1-0.5% |
>0.5% |
| Timeout Errors |
<0.1% |
0.1-0.5% |
>0.5% |
Error Rate SLAs:
success_rate:
target: 99.5%
warning: 99.0%
critical: 98.0%
measurement: rolling_5min
server_error_rate:
target: 0.1%
warning: 0.3%
critical: 0.5%
codes: [500, 502, 503, 504]
timeout_rate:
target: 0.1%
warning: 0.3%
critical: 0.5%
definition: "Request exceeds max latency"
rate_limit_errors:
acceptable: 1%
warning: 3%
critical: 5%
code: 429
note: "Rate limiting is expected behavior"
5.3. Throughput por Tier
| Tier |
Usuarios Concurrentes |
RPS Total |
Storage/User |
| Free |
10,000 |
500 |
10MB |
| Pro |
5,000 |
1,000 |
100MB |
| Perfect |
1,000 |
500 |
1GB |
| Total |
16,000 |
2,000 |
- |
Throughput by Tier:
free:
concurrent_users: 10000
rps_allocation: 500
storage_per_user: 10MB
rate_limits:
api_calls: 100/min
sync_frequency: every_30min
catalog_searches: 50/day
pro:
concurrent_users: 5000
rps_allocation: 1000
storage_per_user: 100MB
rate_limits:
api_calls: 500/min
sync_frequency: every_5min
catalog_searches: unlimited
perfect:
concurrent_users: 1000
rps_allocation: 500
storage_per_user: 1GB
rate_limits:
api_calls: 1000/min
sync_frequency: realtime
catalog_searches: unlimited
priority_support: true
6. Metricas de Cliente
6.1. App Lifecycle
| Metrica |
iOS Target |
Android Target |
Degraded |
| Cold Start |
<2s |
<2.5s |
<4s |
| Warm Start |
<500ms |
<600ms |
<1s |
| First Contentful Paint |
<1s |
<1.2s |
<2s |
| Time to Interactive |
<2s |
<2.5s |
<4s |
App Lifecycle Performance:
cold_start:
description: "App launch from killed state"
ios:
target: 2s
degraded: 4s
measurement: "Launch to first screen render"
android:
target: 2.5s
degraded: 4s
measurement: "onCreate to first frame"
optimization:
- lazy_initialization
- deferred_crypto_setup
- skeleton_screens
warm_start:
description: "App resume from background"
ios:
target: 500ms
degraded: 1s
android:
target: 600ms
degraded: 1s
note: "State already in memory"
time_to_interactive:
description: "User can interact with app"
ios: 2s
android: 2.5s
includes:
- data_decryption
- ui_rendering
- db_connection
6.2. UI Responsiveness
| Metrica |
Target |
Warning |
Critical |
| Frame Rate |
60 FPS |
45 FPS |
30 FPS |
| Touch Response |
<100ms |
<200ms |
<500ms |
| Scroll Jank |
0% frames dropped |
<5% |
>10% |
| Animation Duration |
300ms |
500ms |
1s |
UI Responsiveness:
frame_rate:
target: 60fps
warning: 45fps
critical: 30fps
measurement: "Rolling 1s average"
platforms:
ios: Core Animation
android: Choreographer
touch_response:
target: 100ms
warning: 200ms
critical: 500ms
definition: "Touch event to visual feedback"
scroll_performance:
target: 0% # frames dropped
warning: 5%
critical: 10%
optimization:
- view_recycling
- image_caching
- offscreen_rendering
list_rendering:
items_50:
render_time: 50ms
scroll_fps: 60
items_500:
render_time: 100ms
scroll_fps: 60
requirement: virtualization
6.3. Memory Usage
| Metrica |
iOS Target |
Android Target |
Max |
| Baseline Memory |
50MB |
80MB |
150MB |
| Peak Memory |
150MB |
200MB |
300MB |
| Memory Growth/Hour |
<10MB |
<15MB |
<30MB |
| Background Memory |
<30MB |
<50MB |
80MB |
Memory Usage:
baseline:
description: "App in foreground, idle"
ios: 50MB
android: 80MB
max: 150MB
peak:
description: "Heavy operation (sync, encryption)"
ios: 150MB
android: 200MB
max: 300MB
duration: "Should return to baseline within 30s"
growth_rate:
description: "Memory increase over time"
target: 10MB/hour
warning: 20MB/hour
critical: 30MB/hour
action: "Investigate memory leaks"
background:
description: "App in background"
ios: 30MB
android: 50MB
max: 80MB
note: "Exceeding may cause system kill"
6.4. Battery Consumption
| Operacion |
Target |
Warning |
Critical |
| Foreground Active |
<5%/hour |
<8%/hour |
>10%/hour |
| Background Sync |
<1%/sync |
<2%/sync |
>3%/sync |
| Idle Background |
<0.5%/hour |
<1%/hour |
>2%/hour |
| Location (if used) |
<2%/hour |
<4%/hour |
>6%/hour |
Battery Consumption:
foreground_active:
target: 5%/hour
warning: 8%/hour
critical: 10%/hour
measurement: "Active use with screen on"
background_sync:
target: 1%/sync
warning: 2%/sync
critical: 3%/sync
frequency: every_15min
optimization:
- batch_operations
- compress_payloads
- avoid_polling
idle_background:
target: 0.5%/hour
warning: 1%/hour
critical: 2%/hour
allowed_activities:
- scheduled_notifications
- periodic_sync
location_tracking:
target: 2%/hour
warning: 4%/hour
critical: 6%/hour
note: "Only if location-based reminders enabled"
optimization:
- significant_location_changes
- geofencing
| Componente |
Target |
Max |
Notas |
| App Bundle (iOS) |
<50MB |
100MB |
Sin assets |
| App Bundle (Android) |
<30MB |
60MB |
AAB con splits |
| Local Database |
<100MB |
500MB |
SQLCipher overhead |
| Cache |
<50MB |
100MB |
LRU eviction |
| Catalogs Embebidos |
<10MB |
20MB |
<100 items each |
Storage Footprint:
app_bundle:
ios:
target: 50MB
max: 100MB
breakdown:
- code: 30MB
- assets: 15MB
- frameworks: 5MB
android:
target: 30MB
max: 60MB
format: AAB with APK splits
breakdown:
- code: 15MB
- assets: 10MB
- native_libs: 5MB
local_database:
target: 100MB
max: 500MB
factors:
- medications: 1KB each
- schedules: 500B each
- dose_logs: 200B each
- sqlcipher_overhead: 10%
cache:
target: 50MB
max: 100MB
eviction: LRU
contents:
- images: 30MB
- api_responses: 10MB
- catalog_cache: 10MB
embedded_catalogs:
target: 10MB
max: 20MB
catalogs:
- units: <1MB
- forms: <1MB
- routes: <1MB
- frequencies: <1MB
- studies_free: ~5MB
7. Metricas de Sincronizacion
| Escenario |
Items |
Target |
Max |
| Initial Sync |
100 |
3s |
10s |
| Initial Sync |
500 |
8s |
20s |
| Initial Sync |
1000 |
15s |
30s |
| Incremental (10 changes) |
10 |
500ms |
2s |
| Incremental (100 changes) |
100 |
2s |
5s |
Sync Performance Matrix:
initial_sync:
100_items:
target: 3s
p95: 6s
max: 10s
500_items:
target: 8s
p95: 15s
max: 20s
1000_items:
target: 15s
p95: 25s
max: 30s
optimization:
- parallel_downloads
- compression
- progress_feedback
incremental_sync:
10_changes:
target: 500ms
max: 2s
100_changes:
target: 2s
max: 5s
strategy:
- delta_only
- cursor_based
- batch_processing
7.2. Conflict Resolution
| Metrica |
Target |
Max |
| Detection Time |
10ms |
50ms |
| Resolution Time |
30ms |
100ms |
| User Notification |
100ms |
500ms |
Conflict Resolution Performance:
detection:
target: 10ms
max: 50ms
method: "Version comparison"
resolution:
target: 30ms
max: 100ms
strategy: "Last Write Wins"
steps:
1. compare_versions
2. select_winner
3. update_local
4. mark_synced
notification:
target: 100ms
max: 500ms
when: "User data was overwritten"
method: "In-app toast"
7.3. Offline Queue
| Metrica |
Target |
Max |
| Queue Write |
5ms |
20ms |
| Queue Read |
3ms |
10ms |
| Queue Size (memory) |
100 ops |
500 ops |
| Reconnection Flush |
1s/100 ops |
5s/100 ops |
Offline Queue Performance:
operations:
write:
target: 5ms
max: 20ms
read:
target: 3ms
max: 10ms
capacity:
memory: 100_operations
disk: 500_operations
per_operation_size: ~2KB
flush_on_reconnect:
100_ops:
target: 1s
max: 5s
500_ops:
target: 5s
max: 15s
strategy:
- priority_ordering
- batch_by_entity
- retry_failed
7.4. Queue Overflow Handling (DV2-P3)
PERF-BAJO-003: Queue overflow para Perfect tier no especificado
Remediacion: Definir limite y comportamiento de queue overflow
7.4.1. Queue Capacity por Tier
| Tier |
Memory Ops |
Disk Ops |
Overflow Behavior |
| Free |
100 |
500 |
Reject oldest non-critical |
| Pro |
200 |
1000 |
Reject oldest non-critical |
| Perfect |
500 |
2000 |
Never overflow, disk expansion |
7.4.2. Overflow Handling Strategy
Queue_Overflow_Handling:
free_tier:
memory_limit: 100_operations
disk_limit: 500_operations
overflow_strategy: "FIFO_DROP_NON_CRITICAL"
behavior:
- "Preserve critical operations (medication logs, alerts)"
- "Drop oldest non-critical (analytics, optional sync)"
- "Warn user if critical ops approaching limit"
- "Suggest upgrade to Pro"
critical_operations:
- dose_log_create
- dose_log_update
- medication_create
- medication_update
- schedule_create
- schedule_update
non_critical_operations:
- analytics_event
- catalog_search_cache
- preference_update
pro_tier:
memory_limit: 200_operations
disk_limit: 1000_operations
overflow_strategy: "FIFO_DROP_NON_CRITICAL"
behavior:
- "Same as Free but higher capacity"
- "Less likely to hit limits"
perfect_tier:
memory_limit: 500_operations
disk_limit: 2000_operations
overflow_strategy: "DISK_EXPANSION"
behavior:
- "Never reject operations"
- "Expand disk queue beyond 2000 if needed"
- "Compress old operations (gzip)"
- "Background cleanup of completed ops"
- "Monitor queue health"
disk_expansion:
max_expanded_limit: 5000_operations
compression_ratio: 0.4 # 60% reduction
cleanup_threshold: 3000_operations
cleanup_strategy: "Remove completed ops >7 days old"
overflow_detection:
warning_threshold:
free: 80% # 400 of 500
pro: 80% # 800 of 1000
perfect: 90% # 1800 of 2000
critical_threshold:
free: 95% # 475 of 500
pro: 95% # 950 of 1000
perfect: 95% # 1900 of 2000
actions:
warning:
- log_metric
- notify_user_subtle
- attempt_flush
critical:
- log_error
- notify_user_prominent
- force_flush
- enable_aggressive_mode
aggressive_mode:
description: "Triggered when queue near capacity"
actions:
- increase_sync_frequency
- reduce_sync_batch_size # More frequent smaller batches
- prioritize_critical_ops
- defer_all_non_critical
- compress_queued_data
exit_condition: "Queue below 60% capacity"
7.4.3. User Notifications
Queue_Overflow_Notifications:
warning_80_percent:
title: "Sync Queue Building Up"
message: "You have {count} pending changes. Connect to sync."
action: "Sync Now"
frequency: once_per_session
critical_95_percent:
title: "Sync Queue Nearly Full"
message: "Connect soon to avoid losing analytics data."
action: "Connect & Sync"
frequency: once_per_hour
severity: medium
perfect_tier_expansion:
title: "Extended Queue Active"
message: "Large sync in progress. All data preserved."
action: "View Progress"
frequency: once
severity: info
8. Metricas de Cifrado E2E
8.1. Operaciones Criptograficas
| Operacion |
Target |
Max |
Notas |
| AES-256-GCM Encrypt (1KB) |
5ms |
20ms |
Por blob |
| AES-256-GCM Decrypt (1KB) |
3ms |
15ms |
Por blob |
| Argon2id Key Derivation |
200ms |
500ms |
One-time per session |
| HMAC-SHA256 (Blind Index) |
1ms |
5ms |
Per field |
| Keystore/Keychain Access |
10ms |
50ms |
Hardware-backed |
Cryptographic Operations:
aes_256_gcm:
encrypt:
1kb:
target: 5ms
max: 20ms
10kb:
target: 15ms
max: 50ms
100kb:
target: 100ms
max: 300ms
decrypt:
1kb:
target: 3ms
max: 15ms
10kb:
target: 10ms
max: 40ms
100kb:
target: 80ms
max: 250ms
platforms:
ios: CryptoKit (hardware accelerated)
android: Cipher (hardware if available)
argon2id:
description: "Master key derivation from password"
target: 200ms
max: 500ms
parameters:
memory: 64MB
iterations: 3
parallelism: 4
output: 32 bytes
frequency: "Once per session (login)"
library:
ios: Swift-Argon2
android: argon2kt
hmac_sha256:
description: "Blind index generation"
target: 1ms
max: 5ms
use_cases:
- email_lookup
- phone_lookup
keystore_access:
description: "Hardware-backed key retrieval"
ios:
target: 10ms
max: 50ms
storage: Secure Enclave (if available)
android:
target: 15ms
max: 50ms
storage: StrongBox (if available)
8.2. Blob Processing
| Blob Size |
Encrypt |
Decrypt |
Total Processing |
| 1KB (typical medication) |
5ms |
3ms |
10ms |
| 10KB (prescription OCR) |
15ms |
10ms |
30ms |
| 100KB (image attachment) |
100ms |
80ms |
200ms |
| 1MB (PDF prescription) |
500ms |
400ms |
1s |
Blob Processing Performance:
typical_medication:
size: ~1KB
encrypt: 5ms
decrypt: 3ms
overhead: 48 bytes (nonce + tag)
prescription_data:
size: ~10KB
encrypt: 15ms
decrypt: 10ms
includes: OCR text + metadata
image_attachment:
size: ~100KB
encrypt: 100ms
decrypt: 80ms
optimization: compress_before_encrypt
pdf_prescription:
size: ~1MB
encrypt: 500ms
decrypt: 400ms
strategy: stream_processing
user_feedback: progress_indicator
8.3. Padding Overhead (IMPORTANTE)
DV2-REMEDIACION: Los benchmarks anteriores NO consideraban el padding de seguridad.
El sistema aplica padding a bloques de 1KB para prevenir ataques de metadata.
paddedSize = ceil(actualSize / 1024) * 1024
Ejemplos:
100 bytes → 1024 bytes (10.24x overhead)
500 bytes → 1024 bytes (2.05x overhead)
1000 bytes → 1024 bytes (1.02x overhead)
1500 bytes → 2048 bytes (1.37x overhead)
5000 bytes → 6144 bytes (1.23x overhead)
8.3.2. Benchmarks Ajustados con Padding
| Actual Size |
Padded Size |
Encrypt Target |
Encrypt Max |
Decrypt Target |
| 100 bytes |
1024 bytes |
10ms |
25ms |
8ms |
| 500 bytes |
1024 bytes |
10ms |
25ms |
8ms |
| 1KB |
1024 bytes |
10ms |
25ms |
8ms |
| 5KB |
6144 bytes |
35ms |
80ms |
28ms |
| 10KB |
11264 bytes |
60ms |
140ms |
48ms |
| 50KB |
51200 bytes |
260ms |
600ms |
210ms |
| 100KB |
102400 bytes |
520ms |
1200ms |
420ms |
Padding-Adjusted Benchmarks:
small_blob_100B:
actual_size: 100 bytes
padded_size: 1024 bytes
overhead_factor: 10.24x
encrypt_target: 10ms
encrypt_max: 25ms
decrypt_target: 8ms
note: "Blobs pequenos tienen mayor overhead relativo"
small_blob_500B:
actual_size: 500 bytes
padded_size: 1024 bytes
overhead_factor: 2.05x
encrypt_target: 10ms
encrypt_max: 25ms
decrypt_target: 8ms
typical_medication_1KB:
actual_size: 1024 bytes
padded_size: 1024 bytes
overhead_factor: 1.0x
encrypt_target: 10ms
encrypt_max: 25ms
decrypt_target: 8ms
medium_blob_5KB:
actual_size: 5000 bytes
padded_size: 6144 bytes (6 bloques)
overhead_factor: 1.23x
encrypt_target: 35ms
encrypt_max: 80ms
decrypt_target: 28ms
prescription_10KB:
actual_size: 10000 bytes
padded_size: 11264 bytes (11 bloques)
overhead_factor: 1.13x
encrypt_target: 60ms
encrypt_max: 140ms
decrypt_target: 48ms
large_blob_50KB:
actual_size: 50000 bytes
padded_size: 51200 bytes (50 bloques)
overhead_factor: 1.02x
encrypt_target: 260ms
encrypt_max: 600ms
decrypt_target: 210ms
image_attachment_100KB:
actual_size: 100000 bytes
padded_size: 102400 bytes (100 bloques)
overhead_factor: 1.02x
encrypt_target: 520ms
encrypt_max: 1200ms
decrypt_target: 420ms
user_feedback: progress_indicator_required
8.3.3. Impacto en Initial Sync
Initial Sync with Padding (50 medications scenario):
worst_case:
medications: 50
schedules_per_med: 5
total_entities: 250
avg_entity_size: 800 bytes
padded_entity_size: 1024 bytes
sequential_encrypt:
time: 250 * 10ms = 2500ms
note: "INACEPTABLE para UX"
batch_encrypt_parallel:
batch_size: 10
batches: 25
time_per_batch: 50ms (paralelo)
total_time: 25 * 50ms = 1250ms
with_ui_yields: ~1500ms
note: "ACEPTABLE con progress indicator"
recommendation:
strategy: batch_parallel_with_progress
batch_size: 10
progress_callback: every_batch
ui_yield: between_batches
target_total: "<2s"
max_total: "<5s"
8.3.4. Mitigaciones
Padding Overhead Mitigations:
1_batch_processing:
description: "Procesar multiples entidades en paralelo"
batch_size: 10
parallelism: 4 (match Argon2id)
benefit: "~4x speedup vs sequential"
2_progress_feedback:
description: "Mostrar progreso al usuario"
threshold: ">500ms total"
ui_component: "IndeterminateProgress or PercentageBar"
message: "Securing your data..."
3_lazy_decryption:
description: "Descifrar solo cuando se necesita"
strategy: "Decrypt on read, cache in memory"
cache_ttl: "Session lifetime"
benefit: "Evita descifrar todo en initial sync"
4_background_preload:
description: "Pre-descifrar en background idle"
trigger: "App en foreground, user idle 5s"
priority: "Low CPU priority"
entities: "Most recently used first"
9. Alerting y Monitoring
9.1. Alert Thresholds
Alerting Configuration:
latency_alerts:
warning:
condition: "P95 > 2x target for 5min"
action: "Notify on-call"
channel: Slack
critical:
condition: "P95 > 3x target for 2min"
action: "Page on-call"
channel: PagerDuty
error_rate_alerts:
warning:
condition: "5xx rate > 0.3% for 5min"
action: "Notify on-call"
critical:
condition: "5xx rate > 0.5% for 2min"
action: "Page on-call"
availability_alerts:
warning:
condition: "Uptime < 99.9% rolling 1h"
action: "Notify on-call"
critical:
condition: "Uptime < 99.5% rolling 15min"
action: "Page on-call + Incident Commander"
sync_alerts:
warning:
condition: "Sync queue > 1000 pending ops"
action: "Notify on-call"
critical:
condition: "Sync failures > 10% for 5min"
action: "Page on-call"
9.2. Zero-Knowledge Compliant Metrics
Zero-Knowledge Metrics:
allowed_metrics:
- request_count (by endpoint)
- latency_percentiles (by endpoint)
- error_rates (by code)
- sync_blob_count (not content)
- sync_blob_size_bytes (aggregate)
- active_users (count only)
- device_types (iOS/Android)
prohibited_metrics:
- user_identifiers in logs
- medication_names
- medication_counts_per_user
- search_terms
- notification_content
- any_phi_data
implementation:
- aggregate_only: true
- no_user_correlation: true
- retention: 30_days for metrics
- encryption_at_rest: true
9.3. Dashboards
Performance Dashboards:
executive:
refresh: 5min
metrics:
- overall_availability
- active_users
- sync_success_rate
- app_store_ratings
engineering:
refresh: 1min
metrics:
- latency_by_endpoint
- error_rates_by_service
- database_connections
- cache_hit_rates
mobile:
refresh: 5min
metrics:
- crash_free_rate
- app_launch_time
- frame_rate_distribution
- memory_usage_p95
security:
refresh: 15min
metrics:
- failed_auth_attempts
- rate_limit_triggers
- suspicious_patterns
- encryption_operations
10. Capacity Planning
10.1. User Projections
| Milestone |
Users |
Concurrent |
Storage |
RPS |
| Launch |
1,000 |
100 |
10GB |
50 |
| 6 months |
10,000 |
1,000 |
100GB |
500 |
| 1 year |
50,000 |
5,000 |
500GB |
2,000 |
| 2 years |
200,000 |
20,000 |
2TB |
8,000 |
Capacity Projections:
launch:
users: 1000
concurrent: 100
storage: 10GB
rps: 50
infrastructure:
supabase: Free tier
firebase: Spark plan
estimated_cost: $0/month
six_months:
users: 10000
concurrent: 1000
storage: 100GB
rps: 500
infrastructure:
supabase: Pro tier
firebase: Blaze plan
estimated_cost: $150/month
one_year:
users: 50000
concurrent: 5000
storage: 500GB
rps: 2000
infrastructure:
supabase: Pro + addons
firebase: Blaze plan
estimated_cost: $500/month
two_years:
users: 200000
concurrent: 20000
storage: 2TB
rps: 8000
infrastructure:
supabase: Team tier
firebase: Blaze plan
custom_infrastructure: possible
estimated_cost: $2000/month
10.2. Storage Growth
Storage Growth Model:
per_user_average:
medications: 10 * 1KB = 10KB
schedules: 30 * 500B = 15KB
dose_logs: 365 * 20 * 200B = 1.4MB/year
prescriptions: 5 * 10KB = 50KB
encrypted_overhead: 15%
growth_rate:
daily: 40KB/user (active)
monthly: 1MB/user (active)
yearly: 12MB/user (active)
retention:
active_data: indefinite
dose_logs: 3_years
deleted_data: 30_days (soft delete)
optimization:
compression: gzip (40% reduction)
archival: after 1 year to cold storage
10.3. Scaling Triggers
Scaling Triggers:
horizontal_scale:
cpu_threshold: 70%
memory_threshold: 80%
rps_threshold: 80% of capacity
latency_threshold: P95 > 2x target
vertical_scale:
database_connections: 80% of limit
storage: 80% of allocated
bandwidth: 70% of limit
auto_scaling:
enabled: true
min_instances: 1
max_instances: 10
cooldown: 5min
manual_intervention:
triggers:
- sustained load > 90% for 1h
- multiple scaling events in 30min
- cost increase > 50% week-over-week
11. Degradation Strategies
11.1. Graceful Degradation
Graceful Degradation:
high_latency:
trigger: "P95 > 2x target"
actions:
- enable_aggressive_caching
- reduce_sync_frequency
- defer_non_critical_operations
partial_outage:
trigger: "Error rate > 1%"
actions:
- circuit_breaker_activation
- fallback_to_cached_data
- queue_failed_operations
full_outage:
trigger: "API unreachable"
actions:
- full_offline_mode
- local_notifications_only
- queue_all_sync_operations
- user_notification
recovery:
detection: health_check_success
actions:
- gradual_traffic_restoration
- flush_queued_operations
- verify_data_consistency
Performance Feature Flags:
reduce_sync_frequency:
default: false
effect: "Sync every 30min instead of 5min"
trigger: high_server_load
disable_rich_notifications:
default: false
effect: "Simple text notifications only"
trigger: notification_system_degraded
limit_catalog_results:
default: false
effect: "Max 20 results instead of 50"
trigger: search_service_slow
defer_analytics:
default: false
effect: "Queue analytics for later"
trigger: bandwidth_constrained
reduce_image_quality:
default: false
effect: "Compress images more aggressively"
trigger: storage_pressure
11.3. Circuit Breakers
Circuit Breakers:
sync_service:
failure_threshold: 5
timeout: 30s
reset_timeout: 60s
half_open_requests: 3
catalog_service:
failure_threshold: 3
timeout: 10s
reset_timeout: 30s
fallback: cached_data
notification_service:
failure_threshold: 5
timeout: 5s
reset_timeout: 30s
fallback: local_only
ocr_service:
failure_threshold: 2
timeout: 60s
reset_timeout: 120s
fallback: manual_entry
12. Apendice
| Plataforma |
Tool |
Metricas |
| Server |
Supabase Analytics |
Latency, RPS, Errors |
| Server |
Sentry |
Errors, Performance |
| iOS |
MetricKit |
App lifecycle, Memory |
| iOS |
Instruments |
CPU, Memory, Energy |
| Android |
Firebase Performance |
App lifecycle, Network |
| Android |
Android Profiler |
CPU, Memory, Battery |
12.2. Testing Matrix
| Dispositivo |
Categoria |
Objetivo |
| iPhone 12 |
Mid-range |
Primary target |
| iPhone SE (2nd) |
Low-end |
Baseline |
| iPhone 15 Pro |
High-end |
Best case |
| Pixel 6 |
Mid-range |
Primary target |
| Samsung A13 |
Low-end |
Baseline |
| Samsung S24 |
High-end |
Best case |
12.3. Network Conditions
| Condicion |
Latency |
Bandwidth |
Packet Loss |
| Excellent (5G) |
20ms |
100Mbps |
0% |
| Good (4G) |
50ms |
20Mbps |
0.1% |
| Average (4G) |
100ms |
5Mbps |
0.5% |
| Poor (3G) |
300ms |
1Mbps |
2% |
| Offline |
N/A |
0 |
100% |
Documento generado por SpecQueen Technical Division - IT-14
"Performance is a feature. Privacy is a right."