Caché de Proyectos
La API de Caché de Proyectos gestiona la carga en memoria de proyectos para operaciones de la API. Entender cuándo es necesario cargar proyectos es esencial para un uso eficiente de la API.
Conceptos Clave
Arquitectura Unificada de Caché
La API y la interfaz de usuario (UI) comparten la misma caché en memoria. Cuando cargas un proyecto vía API, es la misma caché que usa la UI. Esto significa:
- Estado Compartido: Las operaciones de la API ven los mismos datos que los usuarios de la UI
- Resultados Compartidos: Los resultados de ejecución son visibles tanto para API como para UI
- Sin Divergencia: Es imposible que API y UI tengan vistas diferentes de un proyecto
Categorías de Operaciones
Las operaciones API se dividen en tres categorías con diferentes requisitos de caché:
| Categoría | Descripción | ¿Requiere carga del proyecto? | Ejemplos |
|---|---|---|---|
| Direct DB | Operaciones de solo lectura | No | Endpoints GET, gestión de tenant/usuario |
| Auto-Carga | Operaciones de modificación | No (auto-carga) | POST/PUT/DELETE sobre investigaciones, notebooks, bloques |
| Requiere Carga | Operaciones de ejecución | Sí | Ejecutar notebook, obtener resultados de ejecución |
Patrón de Auto-Carga (Flujo Simplificado)
Para la mayoría de operaciones CRUD, no necesitas cargar explícitamente el proyecto. La API carga automáticamente el proyecto cuando es necesario:
# Flujo ANTIGUO (ya no es necesario para CRUD):
# manager.load_project(project_id) # ¡No requerido!
# Flujo NUEVO - solo llama la operación directamente:
response = requests.put(
f"{BASE_URL}/api/{TENANT_ID}/{PROJECT_ID}/notebook/{notebook_id}",
json={"Name": "Nombre Actualizado"},
headers=headers
)
# El proyecto se carga automáticamente si es necesario
Cuándo SÍ se Requiere Carga Explícita
La carga explícita del proyecto sigue siendo requerida para operaciones de ejecución:
POST /execution/notebook/{notebookId}- Ejecutar notebookGET /execution/notebook/{notebookId}/results- Obtener resultados de ejecuciónGET /execution/status/{notebookId}- Consultar estado de ejecución
Cargar Proyecto en Caché
GET /api/{tenantId}/project/{projectId}/load
Carga un proyecto en la caché compartida. Usa esto antes de ejecutar notebooks.
Parámetros de Ruta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenantId |
GUID | Sí | Identificador del tenant |
projectId |
GUID | Sí | Identificador del proyecto |
Respuesta (200 OK)
{
"projectId": "87654321-4321-4321-4321-210987654321",
"projectName": "Análisis de Ordenes de Compra",
"tenantName": "acme-corp",
"investigationCount": 5,
"notebookCount": 12,
"datasetCount": 3,
"loadedFromCache": false,
"message": "Proyecto cargado desde la base de datos"
}
Campos de Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
projectId |
GUID | Identificador del proyecto |
projectName |
cadena | Nombre del proyecto |
tenantName |
cadena | Nombre del tenant |
investigationCount |
entero | Número de investigaciones |
notebookCount |
entero | Número de notebooks |
datasetCount |
entero | Número de conjuntos de datos |
loadedFromCache |
booleano | Verdadero si ya estaba en caché, falso si se cargó desde la base de datos |
message |
cadena | Mensaje de estado legible por humanos |
Comportamiento de la Caché
| Escenario | Respuesta | Rendimiento |
|---|---|---|
| Primera llamada (fallo de caché) | loadedFromCache: false |
~1000ms (consulta a DB) |
| Llamadas siguientes (acierto de caché) | loadedFromCache: true |
~75ms (13x más rápido) |
| Después de 30 min sin actividad | Caché expira | La siguiente llamada recarga |
Propiedades de la Caché
- Duración: 30 minutos después del último acceso
- Auto-refresco: Cualquier llamada API al proyecto reinicia el temporizador de 30 minutos
- Compartida: Misma caché usada por UI y API
- Gestión de Memoria: Limpieza automática al 90% de presión de memoria
Descargar Proyecto de Caché
DELETE /api/{tenantId}/project/{projectId}/unload
Elimina un proyecto de la caché, liberando memoria.
Parámetros de Ruta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenantId |
GUID | Sí | Identificador del tenant |
projectId |
GUID | Sí | Identificador del proyecto |
Respuesta (200 OK)
{
"projectId": "87654321-4321-4321-4321-210987654321",
"wasInCache": true,
"message": "Proyecto descargado de la caché exitosamente"
}
Ejemplos de Flujos de Trabajo
Flujo A: Operaciones CRUD (Auto-Carga)
Para crear, actualizar o eliminar investigaciones, notebooks o bloques:
import requests
headers = {"Authorization": f"Bearer {API_KEY}"}
# Solo llama la operación directamente - no se necesita cargar!
response = requests.post(
f"{BASE_URL}/api/{TENANT_ID}/{PROJECT_ID}/investigation",
json={"name": "Nueva Investigación", "description": "Creada vía API"},
headers=headers
)
# El proyecto se carga automáticamente si es necesario
Flujo B: Ejecución de Notebook (Requiere Carga)
Para ejecutar notebooks y obtener resultados:
import requests
import time
headers = {"Authorization": f"Bearer {API_KEY}"}
# Paso 1: Cargar proyecto (REQUERIDO para ejecución)
response = requests.get(
f"{BASE_URL}/api/{TENANT_ID}/project/{PROJECT_ID}/load",
headers=headers
)
print(f"Proyecto cargado: {response.json()['projectName']}")
# Paso 2: Ejecutar notebook
response = requests.post(
f"{BASE_URL}/api/{TENANT_ID}/{PROJECT_ID}/execution/notebook/{NOTEBOOK_ID}",
headers=headers
)
print(f"Ejecución en cola: {response.json()['status']}")
# Paso 3: Consultar hasta terminar
while True:
response = requests.get(
f"{BASE_URL}/api/{TENANT_ID}/{PROJECT_ID}/execution/status/{NOTEBOOK_ID}",
headers=headers
)
status = response.json()
print(f"Estado: {status['status']} ({status['progress']}%)")
if status['status'] == 'Completed':
break
time.sleep(2)
# Paso 4: Obtener resultados
response = requests.get(
f"{BASE_URL}/api/{TENANT_ID}/{PROJECT_ID}/execution/notebook/{NOTEBOOK_ID}/results",
headers=headers
)
results = response.json()
# Paso 5: Descargar proyecto (limpieza opcional)
requests.delete(
f"{BASE_URL}/api/{TENANT_ID}/project/{PROJECT_ID}/unload",
headers=headers
)
Ejemplos de Implementación
cURL
# Cargar proyecto en caché
curl -X GET "https://your-mindzie-instance.com/api/12345678-1234-1234-1234-123456789012/project/87654321-4321-4321-4321-210987654321/load" \
-H "Authorization: Bearer YOUR_API_KEY"
# Descargar proyecto de caché
curl -X DELETE "https://your-mindzie-instance.com/api/12345678-1234-1234-1234-123456789012/project/87654321-4321-4321-4321-210987654321/unload" \
-H "Authorization: Bearer YOUR_API_KEY"
Python
import requests
TENANT_ID = '12345678-1234-1234-1234-123456789012'
BASE_URL = 'https://your-mindzie-instance.com'
class ProjectCacheManager:
def __init__(self, token):
self.headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
self.loaded_projects = set()
def load_project(self, project_id):
"""Carga un proyecto en caché (requerido para operaciones de ejecución)."""
url = f'{BASE_URL}/api/{TENANT_ID}/project/{project_id}/load'
response = requests.get(url, headers=self.headers)
response.raise_for_status()
result = response.json()
self.loaded_projects.add(project_id)
status = "desde caché" if result['loadedFromCache'] else "desde base de datos"
print(f"Proyecto '{result['projectName']}' cargado {status}")
return result
def unload_project(self, project_id):
"""Descarga un proyecto de la caché."""
url = f'{BASE_URL}/api/{TENANT_ID}/project/{project_id}/unload'
response = requests.delete(url, headers=self.headers)
response.raise_for_status()
self.loaded_projects.discard(project_id)
return response.json()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
for project_id in list(self.loaded_projects):
self.unload_project(project_id)
# Uso con gestor de contexto
with ProjectCacheManager('your-api-key') as cache:
result = cache.load_project('87654321-4321-4321-4321-210987654321')
# Ejecutar notebooks aquí...
# Proyectos se descargan automáticamente al salir
JavaScript/Node.js
const TENANT_ID = '12345678-1234-1234-1234-123456789012';
const BASE_URL = 'https://your-mindzie-instance.com';
class ProjectCacheManager {
constructor(token) {
this.headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
this.loadedProjects = new Set();
}
async loadProject(projectId) {
const url = `${BASE_URL}/api/${TENANT_ID}/project/${projectId}/load`;
const response = await fetch(url, { headers: this.headers });
if (!response.ok) throw new Error(`Falló: ${response.status}`);
const result = await response.json();
this.loadedProjects.add(projectId);
console.log(`Cargado: ${result.projectName} (desde caché: ${result.loadedFromCache})`);
return result;
}
async unloadProject(projectId) {
const url = `${BASE_URL}/api/${TENANT_ID}/project/${projectId}/unload`;
const response = await fetch(url, {
method: 'DELETE',
headers: this.headers
});
this.loadedProjects.delete(projectId);
return response.json();
}
async unloadAll() {
await Promise.all(
Array.from(this.loadedProjects).map(id => this.unloadProject(id))
);
}
}
// Uso
const cache = new ProjectCacheManager('your-api-key');
try {
await cache.loadProject('87654321-4321-4321-4321-210987654321');
// Ejecutar notebooks aquí...
} finally {
await cache.unloadAll();
}
Buenas Prácticas
- Operaciones CRUD: No cargues explícitamente - deja que la auto-carga lo maneje
- Operaciones de Ejecución: Siempre carga el proyecto primero
- Clientes de Larga Duración: Descarga proyectos cuando termines para liberar memoria
- Gestores de Contexto: Usa sentencias
with(Python) o try/finally para limpieza - Conciencia de Memoria: La caché se limpia automáticamente al 90% de presión, pero descargar explícitamente es mejor
- Caché Compartida: Recuerda que los usuarios de UI ven el mismo estado del proyecto que tus operaciones API