23 puntos por GN⁺ 2026-01-23 | 4 comentarios | Compartir por WhatsApp
  • Para la recuperación de datos y el cumplimiento normativo, se usa con frecuencia el borrado lógico basado en una columna archived_at, pero con el tiempo aumentan la complejidad y la ineficiencia
  • Este enfoque vuelve más complejos las consultas, índices, migraciones y la lógica de restauración, y como la mayoría de los datos archivados no se vuelven a leer, termina provocando carga innecesaria en la base de datos
  • Como alternativas, se proponen el archivado basado en eventos de la aplicación, el archivado basado en triggers y el archivado basado en WAL (Change Data Capture)
  • Cada enfoque difiere en complejidad operativa, requisitos de infraestructura y facilidad de restauración; en particular, el basado en WAL requiere integración con sistemas externos como Kafka
  • En un proyecto nuevo, el enfoque basado en triggers es la opción más equilibrada en términos de simplicidad y mantenibilidad

Problemas del borrado lógico

  • Por lo general, los datos se eliminan de forma lógica usando una columna booleana deleted o una columna de marca de tiempo archived_at
    • Permite recuperarlos cuando un cliente borra datos por error
    • En algunos casos también es necesario conservarlos por motivos regulatorios o de auditoría
  • Sin embargo, la columna archived_at genera complejidad en las consultas, la operación y el código de la aplicación
    • La mayoría de los datos archivados no se vuelven a leer
    • Por problemas en el comportamiento de la API o herramientas de automatización como Terraform, pueden acumularse millones de filas innecesarias
  • Si no se configura una tarea de limpieza de datos archivados, se produce una caída de rendimiento en los respaldos y restauraciones de la base de datos
  • Es necesario filtrar los datos archivados en consultas e índices, y existe riesgo de filtración de datos
  • Durante las migraciones, es difícil manejar datos antiguos o modificar valores predeterminados
  • La lógica de restauración se vuelve compleja y, cuando requiere llamadas a sistemas externos, pueden aparecer errores
  • En consecuencia, el enfoque con archived_at parece simple, pero a largo plazo tiene un alto costo de mantenimiento

Archivado a nivel de aplicación

  • Al eliminar, se publica un evento y se envía a SQS para que otro servicio lo archive en S3
  • Ventajas
    • Simplifica la base de datos principal y el código de la aplicación
    • Mejora el rendimiento y la estabilidad al manejar de forma asíncrona la limpieza de recursos externos
    • Puede archivarse serializado en JSON con una estructura amigable para la aplicación
  • Desventajas
    • Por errores en el código de la aplicación, puede haber pérdida de datos archivados
    • Aumenta la complejidad operativa de la infraestructura, como las colas de mensajes
    • Los datos archivados en S3 requieren herramientas de búsqueda y restauración

Archivado basado en triggers

  • Antes de eliminar, un trigger copia la fila en una tabla archive separada en formato JSON
    • Tabla de ejemplo: archive(id, table_name, record_id, data, archived_at, caused_by_table, caused_by_id)
  • En eliminaciones por clave foránea (cascade), se usan variables de sesión (archive.cause_table, archive.cause_id) para rastrear la causa de la eliminación
    • Permite consultar qué registro padre eliminó los datos hijos
  • Ventajas
    • Las tablas activas se mantienen limpias, sin necesidad de la columna archived_at
    • La limpieza de la tabla de archivado (WHERE archived_at < NOW() - INTERVAL '90 days') es sencilla
    • Se mantiene la eficiencia de consultas e índices, y se simplifican las migraciones
    • Reduce el tamaño de los respaldos
  • La tabla de archivado puede administrarse con un tablespace separado o particionado por tiempo

Archivado basado en WAL (Change Data Capture)

  • Se leen los logs WAL de PostgreSQL y se transmiten los eventos de eliminación a un sistema externo
    • Herramienta representativa: Debezium (integrado con Kafka)
    • Ejemplo de ruta: PostgreSQL → Debezium → Kafka → Consumer → Archive Storage
  • Alternativas más livianas
    • pgstream: envía WAL directamente a un webhook o cola de mensajes
    • wal2json: emite el WAL en JSON
    • pg_recvlogical: herramienta integrada de replicación lógica de PostgreSQL
  • Complejidad operativa
    • Un entorno basado en Kafka requiere monitoreo, respuesta ante fallos y tuning
    • Si el consumer se retrasa, puede haber acumulación de archivos WAL → riesgo de falta de espacio en disco
    • Puede limitarse con la configuración max_slot_wal_keep_size de PostgreSQL 13+
    • Es indispensable monitorear el retraso del replication slot y configurar alertas
  • Ventajas
    • Permite capturar todos los cambios sin modificar el código de la aplicación
    • Puede hacer streaming hacia distintos destinos (S3, data warehouse, índice de búsqueda)
    • No añade carga extra a la base de datos principal
  • Desventajas
    • Tiene alta complejidad operativa y costo de infraestructura
    • Si el consumer se retrasa, puede haber pérdida de datos o necesidad de resincronización
    • Los cambios de esquema requieren coordinación entre la fuente y el consumer

Idea de una réplica que no procese eliminaciones

  • Se plantea la idea de mantener una réplica de PostgreSQL que ignore las consultas DELETE
    • Permitirá acumular y conservar todos los datos no eliminados
    • Los datos archivados podrían consultarse directamente
  • Problemas potenciales
    • Puede no ser posible distinguir la información eliminada
    • Existe riesgo de conflicto al aplicar migraciones
    • Aumentan los costos de almacenamiento y operación

Conclusión

  • En proyectos nuevos, el archivado basado en triggers es la opción más práctica
    • Su configuración es simple y mantiene limpias las tablas activas
    • Permite consultar y administrar los datos archivados con facilidad, sin infraestructura adicional
  • Si ya existe una infraestructura compleja o se necesita streaming hacia múltiples destinos, el enfoque basado en WAL es adecuado

4 comentarios

 
love7peace 2026-01-23

Había aprendido que si es basado en triggers genera carga en la BD... ¿y aun así recomiendan triggers?

 
nemorize 2026-01-24

Si la carga generada por un trigger de ese nivel es un problema, entonces incluso sin el trigger la situación ya está llena de problemas.

 
cherrycoder 2026-01-23

Como siempre, la regulación tiene un costo. Bueno, de todos modos es una parte que terminarán pagando los consumidores.

 
GN⁺ 2026-01-23
Opiniones de Hacker News
  • En el dominio bancario donde trabajo, más bien siento que el soft delete sí conviene
    Si tienes una columna deleted_at, escribir consultas es más claro, y tanto las consultas analíticas como el panel de administración pueden trabajar sobre el mismo conjunto de datos
    Borrar suele ser algo poco frecuente en la mayoría de los casos, y casi nunca he visto que las filas con soft delete causen problemas de rendimiento
    Además, como las relaciones se mantienen intactas, también es fácil restaurar (undo)
    Yo incluso prefiero ir más allá y hacer que las filas sean completamente inmutables (immutable), agregando una fila nueva cada vez que hay una actualización
    Si quieres dejar un log, me parece buena idea usar triggers de la DB para registrar en una tabla espejo cada INSERT/UPDATE/DELETE

    • Tienes razón, pero creo que eso solo aplica cuando las eliminaciones son poco frecuentes
      En tablas que he visto donde entre 50 y 70% estaba en soft delete, la degradación de rendimiento sí era clara
      Al final, el soft delete depende del contexto y requiere análisis previo
    • En Postgres también puedes configurar que solo se indexen los datos que no tienen soft delete
      En la mayoría de los casos no hace falta, pero puede ayudar a ahorrar RAM
    • En banca, el soft delete no es más que una solución temporal que disimula la falta de auditabilidad (auditability)
      La solución real es Event Sourcing, registrando todos los cambios como eventos
      El rendimiento empeora, pero se puede compensar con snapshots y sincronización (sync)
    • Si vas a operar la DB con una estructura inmutable, vale la pena considerar sistemas como Datomic
      Con la función de time travel puedes consultar por completo estados pasados
    • Cuando trabajé antes en una aseguradora, también operábamos cada tabla como un log append-only
      El estado más reciente estaba en la fila con el timestamp más alto, y los estados pasados se podían consultar con filtros
      Este enfoque permite una gestión de historial muy potente
  • La mayor trampa del soft delete es la complejidad de las consultas
    Al principio uno piensa que basta con agregar WHERE deleted_at IS NULL, pero unos meses después aparecen datos fantasma en los reportes por filtros omitidos
    Se puede resolver con views, pero al final igual tienes que mantener patrones de acceso paralelos, y cuando necesitas consultar datos borrados terminas saltándote la abstracción
    Event sourcing es más limpio, pero como la carga operativa es alta, la mayoría termina eligiendo un enfoque híbrido

    • Las views son una herramienta suficientemente poderosa
      El problema es que muchos SWE e ingenieros de BI no están familiarizados con SQL ni con el diseño de esquemas
      Un problema todavía más común que el soft delete es manejar Type 2 Slowly Changing Dimension
      La mayoría crea audit tables innecesarias y repite UPDATE/INSERT de forma ineficiente
      La verdad es que las DB son sistemas realmente hermosos, y da pena que no reciban el respeto que merecen
  • Creo que sería bueno que el soft delete viniera como una función integrada de la DB
    Sería ideal poder activarlo por tabla y elegir la estrategia de eliminación

    • De hecho, sistemas como Iceberg, Delta Lake y BigQuery ofrecen funciones de time travel
      Pero muchos equipos terminan implementándolo con enfoque SCD (Slowly Changing Dimension) por requisitos personalizados
  • En mi experiencia, el enfoque basado en triggers ha sido el más estable
    La tabla de archivo debe mantenerse como append-only, y la restauración debe manejarse en la capa de aplicación
    Las actualizaciones se consideran soft delete, y el trigger debe capturar el estado anterior
    El trigger tiene que ejecutarse obligatoriamente en el momento BEFORE, y la lógica debe ser simple
    Las particiones por mes son lo normal, y si hay mucha carga de escritura conviene dividir por día

  • Me gustaría que las DB evolucionaran de stateful → stateless
    Prefiero una estructura donde todos los cambios se registren como eventos append-only, y los datos necesarios se expresen mediante views
    Sería ideal que la DB administrara automáticamente un materialized index
    Algunas DB modernas ya ofrecen algo así, pero todavía falta desarrollo orientado a OLTP

  • En una empresa donde estuve antes aplicaban soft delete en todos los sistemas
    Recuerdo que un profesor decía: “en el mundo de los negocios, los datos nunca se borran

    • Hacer una eliminación definitiva es limitar por cuenta propia tu futura capacidad de análisis de datos
      El almacenamiento es barato, así que no deberíamos borrar datos nunca
    • Pero es interesante que el profesor no dijera nada sobre las modificaciones
  • La base de datos es un lugar para almacenar hechos (facts)
    Crear un registro es un hecho nuevo, y eliminarlo es otro hecho más
    Pero si borras físicamente una fila, ese hecho desaparece
    En la mayoría de los casos, ese tipo de eliminación no es deseable

    • Pero si los datos son un activo con riesgo de filtración, podría ser necesario hacer eliminaciones masivas
      Hay que considerar el costo de mantenimiento y los riesgos de seguridad
    • Si la DB no es inmutable, entonces la propia modificación ya está creando hechos desaparecidos
      La decisión de conservar los datos permanentemente debe tomarse con cuidado
    • Personalmente, creo que un almacén de datos solo debería soportar dos operaciones: consulta e inserción
      Para eso es importante entender el ciclo de vida de los datos
  • En Firezone al principio usaban soft delete para logs de auditoría, pero lo abandonaron por problemas de migración
    En su lugar, cambiaron a un enfoque que usa Postgres CDC (Change Data Capture) para enviar eventos a una tabla separada optimizada para escritura
    Creo que el soft delete sí es útil para la recuperación por parte del usuario, pero no es adecuado para auditoría o cumplimiento normativo

    • En proyectos simples, en vez de auditar cambios en la DB, es más eficiente auditar las propias llamadas a la API
  • Crear una view sobre una tabla con campo de soft delete para ocultar las filas eliminadas es una solución limpia
    Así, la aplicación no tiene que preocuparse por si algo fue eliminado o no

    • Con RLS (Row Level Security) de Postgres puedes ocultar automáticamente las filas con soft delete
      La aplicación sigue haciendo lectura/escritura/eliminación sobre la misma tabla
  • También está la pregunta de cómo manejar el schema drift
    Si luego quieres restaurar datos serializados con el esquema que existía al momento de la eliminación, los cambios de esquema se vuelven un problema

    • En mi experiencia, casi nunca se accede a los objetos archivados
      La mayoría de las restauraciones ocurren dentro de unos días después del borrado, así que el impacto de cambios de esquema suele ser bajo
      Migrar archivos antiguos a un modelo nuevo era una tarea compleja y propensa a errores
      Al final, la estrategia depende de cómo se use realmente el sistema