Skip to content

one_d4: duplicate motif_occurrences rows on re-index #1072

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

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions