Skip to content

one_d4: duplicate motif_occurrences rows on re-index #1072

@aaylward

Description

@aaylward

Bug

When the same game is indexed more than once, game_features is safely upserted (H2 uses MERGE KEY, PostgreSQL uses ON CONFLICT DO UPDATE), but motif_occurrences rows are inserted with a plain INSERT — no conflict clause and no pre-delete. Each re-index of the same game appends a duplicate set of occurrence rows.

When this happens

  • Current-month requests: IndexedPeriodStore.upsertPeriod marks a period as complete only if the current time is past the first day of the next month (isComplete = !fetchedAt.isBefore(firstDayNextMonth)). Any request for the current month will be marked incomplete and therefore will be re-fetched on the next index request, doubling the occurrence rows each time.
  • Concurrent requests: Two simultaneous index requests for the same player+month both pass the period-cache check and both call insertOccurrences.

Root cause

GameFeatureDao.insertOccurrences (called from IndexWorker.indexGame) at line 157 of IndexWorker.java:

gameFeatureStore.insert(row);                              // safe: upserts
gameFeatureStore.insertOccurrences(game.url(), features.occurrences());  // not safe: plain INSERT

deleteOccurrencesByGameUrl exists but is only called from the re-analysis path (AdminController), not from normal game ingestion.

Fix

Before calling insertOccurrences, call deleteOccurrencesByGameUrl(game.url()) in IndexWorker.indexGame, or add an ON CONFLICT DO NOTHING clause to the occurrence INSERT and add a unique constraint on (game_url, motif, ply).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions