Skip to content

Fix definitivo: elimina duplicados de clasificación y bloquea a nivel de BD - #5

Merged
danguz80 merged 1 commit into
mainfrom
claude/libertadores-penalties-admin-s9py1b
Aug 21, 2026
Merged

Fix definitivo: elimina duplicados de clasificación y bloquea a nivel de BD#5
danguz80 merged 1 commit into
mainfrom
claude/libertadores-penalties-admin-s9py1b

Conversation

@danguz80

Copy link
Copy Markdown
Owner

La pista clave

Hay dos botones "Calcular Puntos" (Clasificación y Admin) que pegan al mismo endpoint /api/libertadores-calcular/puntos. Al presionar el del panel Admin, el acumulado volvió exactamente a 178 pts — el mismo valor del bug original, antes de cualquier fix. Que el número regrese exactamente al valor original tras un DELETE-then-INSERT ya corregido en PRs anteriores es la prueba de que esto es una condición de carrera, no un problema de lógica de cálculo: nada impedía que dos ejecuciones del cálculo (dos clics, los dos botones corriendo casi juntos) se intercalaran y cada una reinsertara su propia copia del bonus de clasificación.

La causa raíz: libertadores_puntos_clasificacion se crea con CREATE TABLE IF NOT EXISTS ... UNIQUE(usuario_id, partido_id, jornada_numero). Como esa tabla ya existía en producción (la usa también el cálculo de Jornada 6, anterior a la lógica de octavos), esa sentencia nunca hizo nada y el UNIQUE nunca llegó a crearse de verdad. Sin esa restricción, no hay nada a nivel de base de datos que impida duplicar filas del bonus de "equipo que avanza".

Fix real, a nivel de datos

  1. Al arrancar el cálculo: se borran los duplicados que ya existen en la tabla (dejando una sola fila por usuario+partido+jornada).
  2. Se crea el índice único que debió existir desde el principio (CREATE UNIQUE INDEX IF NOT EXISTS), así que de ahora en más es la propia base de datos la que impide duplicar, sin importar cuántas veces o cuán seguido se dispare el recálculo (desde cualquiera de los dos botones).
  3. El INSERT vuelve a usar ON CONFLICT ... DO UPDATE, ahora seguro porque el índice que necesita queda garantizado en el paso 2 (a diferencia de un intento anterior que asumía el índice sin crearlo, lo que abortaba el cálculo).

Con esto, recalcular puntos para J8 las veces que sea, desde cualquiera de los dos botones, siempre converge al mismo resultado correcto en vez de acumular.


🤖 Generated with Claude Code


Generated by Claude Code

El acumulado de Libertadores volvía a 178pts (el mismo valor del bug
original) cada vez que se presionaba 'Calcular Puntos' -sea desde la
página de Clasificación o desde el panel de Admin, ambos pegan al
mismo endpoint-. Que el número regresara exactamente al valor
original tras un DELETE-then-INSERT ya corregido es evidencia de que
el problema es una condición de carrera: nada impide que dos cálculos
(dos clics, los dos botones corriendo casi al mismo tiempo, etc.) se
intercalen y cada uno reinserte su propia copia del bonus de
clasificación, porque la tabla libertadores_puntos_clasificacion,
creada con CREATE TABLE IF NOT EXISTS, nunca llegó a tener en
producción el UNIQUE(usuario_id, partido_id, jornada_numero) que ahí
se declara (esa sentencia es un no-op sobre una tabla que ya existía).

Fix real, a nivel de datos:
1. Al arrancar el cálculo, se borran los duplicados que ya existen en
   la tabla (dejando una sola fila por usuario+partido+jornada).
2. Se crea el índice único que debió existir desde el principio
   (CREATE UNIQUE INDEX IF NOT EXISTS), así que de ahora en más es la
   propia base de datos la que impide duplicar, sin importar cuántas
   veces o cuán seguido se dispare el recálculo.
3. El INSERT vuelve a usar ON CONFLICT ... DO UPDATE, ahora seguro
   porque el índice que necesita está garantizado en el paso 2.

Con esto, recalcular puntos para J8 las veces que sea, desde
cualquiera de los dos botones, siempre converge al mismo resultado
correcto en vez de acumular.
Copilot AI lite review requested due to automatic review settings August 21, 2026 02:29
@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for pollafutbol canceled.

Name Link
🔨 Latest commit 4e65f8d
🔍 Latest deploy log https://app.netlify.com/projects/pollafutbol/deploys/6a87b827f2f5720008ef512a

@danguz80
danguz80 merged commit fe08b17 into main Aug 21, 2026
1 of 2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Este PR busca eliminar definitivamente los duplicados de puntos de clasificación en libertadores_puntos_clasificacion y hacer el recálculo idempotente bajo concurrencia (dos ejecuciones del cálculo solapadas desde distintos botones), apoyándose en una restricción única a nivel de base de datos.

Changes:

  • Se agrega un saneamiento inicial de libertadores_puntos_clasificacion para borrar duplicados existentes.
  • Se crea un índice único (CREATE UNIQUE INDEX IF NOT EXISTS) para impedir duplicados por (usuario_id, partido_id, jornada_numero).
  • El INSERT de puntos de clasificación vuelve a usar ON CONFLICT ... DO UPDATE para garantizar upsert seguro.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +31 to +42
await pool.query(`
DELETE FROM libertadores_puntos_clasificacion a
USING libertadores_puntos_clasificacion b
WHERE a.ctid < b.ctid
AND a.usuario_id = b.usuario_id
AND a.partido_id = b.partido_id
AND a.jornada_numero = b.jornada_numero
`);
await pool.query(`
CREATE UNIQUE INDEX IF NOT EXISTS ux_lpc_usuario_partido_jornada
ON libertadores_puntos_clasificacion (usuario_id, partido_id, jornada_numero)
`);
Comment on lines +39 to +42
await pool.query(`
CREATE UNIQUE INDEX IF NOT EXISTS ux_lpc_usuario_partido_jornada
ON libertadores_puntos_clasificacion (usuario_id, partido_id, jornada_numero)
`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants