Saltar a contenido

Projects Query Stats Report

Tabla: Projects Proyecto: Calmia Nexus Fecha: 2026-02-01 Estado: Análisis inicial


1. Resumen Ejecutivo

Métrica Valor
Sequential Scans 524,856
Index Hit Rate 0.1%
Diagnóstico CRÍTICO: Index hit rate < 1%
Acción Requerida Crear índices para patrones de consulta frecuentes

2. Estructura de la Entidad

Archivo: Shared/Shared.Admin/Entities/Project.cs

Columna Tipo Uso en Consultas
Id Guid PK, FindAsync
Slug string WHERE (único)
Name string ORDER BY
Description string? -
RepoPath string? -
IsActive bool WHERE filter
CreatedAt DateTime -
UpdatedAt DateTime -
OrganizationId Guid? WHERE filter (multi-tenant)

3. Índices Actuales

Basado en NexusDbContext.cs:

Índice Columna(s) Tipo
Projects_pkey Id PRIMARY KEY
IX_Projects_Slug Slug UNIQUE
(FK automático) OrganizationId BTREE

Problema: Solo hay 2-3 índices, pero las consultas usan combinaciones de columnas no indexadas.


4. Patrones de Consulta Identificados

4.1 Alta Frecuencia (cada request)

Patrón Archivo Línea Columnas Índice Usado
Lista proyectos por organización ProjectsController.cs 58-101 OrganizationId, IsActive, Name NINGUNO (Seq Scan)
Multi-tenant filter ProjectsController.cs 71 OrganizationId IS NULL OR IN(...) NINGUNO

SQL Equivalente:

SELECT * FROM "Projects"
WHERE ("OrganizationId" IS NULL OR "OrganizationId" IN (...))
  AND "IsActive" = true
ORDER BY "Name";

4.2 Media Frecuencia

Patrón Archivo Línea Columnas Índice Usado
Buscar por Slug ProjectsController.cs 188 Slug IX_Projects_Slug
Buscar por ID ProjectsController.cs 112, 160, 237 Id PK
Verificar slug único ProjectsController.cs 204 Slug IX_Projects_Slug

4.3 Baja Frecuencia

Patrón Archivo Línea Columnas Índice Usado
Delete con Include ProjectsController.cs 273-276 Id PK
Listar activos ProjectMetricsService.cs 48 IsActive NINGUNO
Verificar proyectos por org OrganizationService.cs 154 OrganizationId Parcial

5. Análisis del Problema

Causa del Bajo Index Hit Rate

  1. Consulta principal (GetAll) ejecuta Seq Scan porque:
  2. Filtro OrganizationId IS NULL OR OrganizationId IN (...) no puede usar índice simple
  3. Combinación IsActive + ORDER BY Name no tiene índice
  4. Sin índice en OrganizationId + Name para ORDER BY

  5. Volumen de llamadas:

  6. 524,856 seq scans sugiere ~500K llamadas sin índice
  7. Cada vista de lista de proyectos genera un seq scan

  8. Tamaño de tabla pequeño:

  9. PostgreSQL puede preferir Seq Scan en tablas pequeñas
  10. Si hay < 100 proyectos, el optimizador puede ignorar índices

6. Consultas Frecuentes Detalladas

Consulta 1: Listado Multi-tenant (CRÍTICA)

Ubicación: ProjectsController.cs:58-101

var query = _context.Projects
    .Include(p => p.Organization)
    .AsQueryable();

// Multi-tenant filtering
query = query.Where(p =>
    p.OrganizationId == null ||
    accessibleOrgIds.Contains(p.OrganizationId.Value));

if (isActive.HasValue)
    query = query.Where(p => p.IsActive == isActive.Value);

if (organizationId.HasValue)
    query = query.Where(p => p.OrganizationId == organizationId.Value);

var projects = await query
    .OrderBy(p => p.Name)
    .Select(...)
    .ToListAsync();

SQL Generado:

SELECT p.*, o.*
FROM "Projects" p
LEFT JOIN "Organizations" o ON p."OrganizationId" = o."Id"
WHERE (p."OrganizationId" IS NULL OR p."OrganizationId" IN (@p0, @p1, ...))
  AND p."IsActive" = @isActive
ORDER BY p."Name"

Problema: OR condition + ORDER BY Name = Seq Scan garantizado.

Consulta 2: Filtro por organización específica

Ubicación: ProjectsController.cs:79-80

SELECT * FROM "Projects"
WHERE "OrganizationId" = @orgId
ORDER BY "Name"

Índice Recomendado:

CREATE INDEX IX_Projects_Org_Name
ON "Projects" ("OrganizationId", "Name");

Consulta 3: Proyectos activos

Ubicación: ProjectMetricsService.cs:48

SELECT * FROM "Projects"
WHERE "IsActive" = true

Índice Recomendado:

CREATE INDEX IX_Projects_Active
ON "Projects" ("IsActive")
WHERE "IsActive" = true;


7. Índices Recomendados

P1 - Alta Prioridad

Índice Definición Consulta Optimizada
IX_Projects_Org_Name ("OrganizationId", "Name") WHERE OrgId ORDER BY Name
IX_Projects_OrgNull_Name ("Name") WHERE "OrganizationId" IS NULL Proyectos sin organización

P2 - Media Prioridad

Índice Definición Consulta Optimizada
IX_Projects_Active_Name ("Name") WHERE "IsActive" = true Proyectos activos ordenados
IX_Projects_Org_Active_Name ("OrganizationId", "IsActive", "Name") Filtro combinado completo

P3 - Baja Prioridad (ya existen)

Índice Estado
IX_Projects_Slug ✓ Existe (UNIQUE)
Projects_pkey ✓ Existe (PK)

8. Script SQL de Estadísticas

Archivo: scripts/database/projects-query-analysis.sql

Ejecutar para obtener métricas actuales:

# En PostgreSQL
psql -d nexus_db -f scripts/database/projects-query-analysis.sql > projects_query_stats.log 2>&1

El script incluye: 1. Estadísticas generales de la tabla 2. Listado de índices y su uso (idx_scan) 3. Definición de índices existentes 4. Índices no utilizados 5. Análisis de columnas indexadas 6. EXPLAIN ANALYZE de consultas típicas 7. Recomendaciones de índices 8. Resumen ejecutivo


9. Próximos Pasos

  1. Ejecutar script de análisis en entorno de desarrollo
  2. Verificar estadísticas reales de idx_scan
  3. Crear índices P1 como primera optimización
  4. Medir impacto después de aplicar índices
  5. Documentar resultados en ADR

10. Referencias

  • Entidad: Shared/Shared.Admin/Entities/Project.cs
  • DbContext: Shared/Shared.Admin/Data/NexusDbContext.cs:109-120
  • Controller: Orchestrator/src/Orchestrator.Api/Controllers/ProjectsController.cs
  • Servicios:
  • ProjectMetricsService.cs
  • OrganizationService.cs
  • UserContextService.cs