Fix definitivo: elimina duplicados de clasificación y bloquea a nivel de BD - #5
Merged
Merged
Conversation
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.
✅ Deploy Preview for pollafutbol canceled.
|
There was a problem hiding this comment.
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_clasificacionpara 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
INSERTde puntos de clasificación vuelve a usarON CONFLICT ... DO UPDATEpara 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) | ||
| `); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 unDELETE-then-INSERTya 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_clasificacionse crea conCREATE 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 elUNIQUEnunca 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
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).INSERTvuelve a usarON 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