Saltar al contenido principal

Almacén de Sesiones con Redis

Vali-Blob.Redis proporciona un almacén de sesiones reanudables respaldado por Redis. Es la opción recomendada para producción: ofrece rendimiento de lectura/escritura sub-milisegundo, soporte nativo de TTL (expiración automática) y compatibilidad con arquitecturas multi-instancia.

Instalación

dotnet add package Vali-Blob.Redis

Configuración básica

using Vali-Blob.Core.Extensions;
using Vali-Blob.Redis;

builder.Services
.AddVali-Blob(o => o.DefaultProvider = "aws")
.AddProvider<AWSS3Provider>("aws", opts => { /* ... */ })
.UseRedisResumableStore(redis =>
{
redis.ConnectionString = builder.Configuration["Redis:ConnectionString"]!;
});

RedisResumableStoreOptions

public class RedisResumableStoreOptions
{
/// <summary>Cadena de conexión de Redis (formato StackExchange.Redis). Requerida.</summary>
public required string ConnectionString { get; set; }

/// <summary>Índice de la base de datos Redis (0-15). Por defecto: 0.</summary>
public int DatabaseIndex { get; set; } = 0;

/// <summary>Prefijo para todas las claves de sesión. Por defecto: "valiblob:sessions".</summary>
public string KeyPrefix { get; set; } = "valiblob:sessions";

/// <summary>TTL predeterminado para las sesiones en Redis. Por defecto: 24 horas.</summary>
public TimeSpan DefaultSessionTtl { get; set; } = TimeSpan.FromHours(24);

/// <summary>Si true, comprimir los datos JSON de sesión antes de almacenar.</summary>
public bool CompressSessionData { get; set; } = false;
}

Tabla de opciones

OpciónPor defectoDescripción
ConnectionStringCadena de conexión de Redis. Requerida.
DatabaseIndex0Base de datos Redis (0–15). Usa una dedicada para Vali-Blob
KeyPrefixvaliblob:sessionsPrefijo de todas las claves en Redis
DefaultSessionTtl24 horasTTL aplicado a cada sesión al crearla
CompressSessionDatafalseComprimir el JSON de sesión con GZip antes de guardar

Formatos de ConnectionString

# Instancia local
localhost:6379

# Con autenticación
redis.empresa.com:6379,password=miContraseña

# Con TLS (Redis en la nube)
redis.empresa.com:6380,ssl=true,password=miContraseña

# Azure Cache for Redis
mi-cache.redis.cache.windows.net:6380,password=CLAVE_PRIMARIA,ssl=True,abortConnect=False

# Redis Sentinel (alta disponibilidad)
sentinel1:26379,sentinel2:26379,sentinel3:26379,serviceName=mymaster

# Redis Cluster
redis-node1:6379,redis-node2:6379,redis-node3:6379

Reutilizar IConnectionMultiplexer existente

Si tu aplicación ya tiene un IConnectionMultiplexer registrado (por ejemplo, para caché), Vali-Blob lo reutiliza automáticamente:

// Registro compartido del multiplexer
builder.Services.AddSingleton<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(builder.Configuration["Redis:ConnectionString"]!));

// Vali-Blob detecta y reutiliza el multiplexer existente
builder.Services
.AddVali-Blob(o => o.DefaultProvider = "s3")
.AddProvider<AWSS3Provider>("s3", opts => { /* ... */ })
.UseRedisResumableStore(redis =>
{
redis.DatabaseIndex = 2; // Base de datos separada para sesiones Vali-Blob
redis.KeyPrefix = "storage:sessions";
// Sin ConnectionString: usa el IConnectionMultiplexer del contenedor DI
});

Configuración desde appsettings.json

{
"Redis": {
"ConnectionString": "localhost:6379",
"DatabaseIndex": 1,
"KeyPrefix": "valiblob",
"DefaultSessionTtlHours": 48
}
}
builder.Services
.AddVali-Blob(o => o.DefaultProvider = "s3")
.AddProvider<AWSS3Provider>("s3", opts => { /* ... */ })
.UseRedisResumableStore(
builder.Configuration.GetSection("Redis").Bind);

Estructura de datos en Redis

Las sesiones se almacenan como hashes Redis:

valiblob:sessions:{sessionId}:meta   → Hash con todos los campos de la sesión
valiblob:sessions:{sessionId}:chunks → Set con índices de chunks recibidos

Inspección desde redis-cli:

# Ver todos los campos de una sesión
HGETALL "valiblob:sessions:abc123:meta"
# path → "uploads/mi-archivo.zip"
# totalBytes → "104857600"
# uploadedBytes → "52428800"
# status → "Active"
# createdAt → "2024-03-15T10:00:00Z"
# expiresAt → "2024-03-17T10:00:00Z"

# Ver TTL restante de la sesión
TTL "valiblob:sessions:abc123:meta"
# Resultado: 172800 (segundos = 48 horas restantes)

# Contar sesiones activas
KEYS "valiblob:sessions:*:meta"

TTL y expiración automática

Una de las principales ventajas de Redis es el TTL nativo. No se necesita un job de limpieza manual:

.UseRedisResumableStore(redis =>
{
redis.DefaultSessionTtl = TimeSpan.FromHours(48);
// Redis eliminará automáticamente las sesiones expiradas
})

Cuando un cliente intenta usar una sesión expirada:

var resultado = await storage.UploadChunkAsync(new UploadChunkRequest
{
SessionId = sessionIdExpirado,
// ...
}, ct);

if (resultado.ErrorCode == StorageErrorCode.ResumableSessionExpired)
{
return Results.StatusCode(410); // 410 Gone — la sesión ya no existe
}

if (resultado.ErrorCode == StorageErrorCode.ResumableSessionNotFound)
{
return Results.NotFound("Sesión no encontrada.");
}

Configuración completa en producción

// Program.cs — AWS S3 + Azure Cache for Redis
var builder = WebApplication.CreateBuilder(args);

builder.Services
.AddVali-Blob(o => o.DefaultProvider = "s3")
.AddProvider<AWSS3Provider>("s3", opts =>
{
opts.BucketName = builder.Configuration["AWS:BucketName"]!;
opts.Region = builder.Configuration["AWS:Region"]!;
})
.WithPipeline(p => p
.UseValidation(v => v.MaxFileSizeBytes = 5_000_000_000L) // 5 GB
.UseContentTypeDetection()
.UseConflictResolution(ConflictResolution.ReplaceExisting)
)
.UseRedisResumableStore(redis =>
{
redis.ConnectionString = builder.Configuration["Redis:ConnectionString"]!;
redis.DatabaseIndex = 1;
redis.KeyPrefix = $"{builder.Configuration["App:Name"]}:sessions";
redis.DefaultSessionTtl = TimeSpan.FromHours(72); // 3 días
redis.CompressSessionData = false;
});
Consejo

Usa un DatabaseIndex dedicado (ej. DatabaseIndex = 1) para las sesiones de Vali-Blob, separado de otros usos de Redis como caché de aplicación o colas. Esto facilita la inspección con redis-cli, el monitoreo de uso y el flush selectivo de datos sin afectar otros sistemas.

Advertencia

Configura Redis con la política de evicción noeviction o volatile-lru para las sesiones de Vali-Blob. Si Redis expulsa claves activas por presión de memoria antes de que expiren por TTL, la sesión de subida se perderá prematuramente y el cliente recibirá ResumableSessionNotFound. Monitorea el uso de memoria de Redis en producción.