Skip to content

fix(comments): legacy id-less comment rows superseded by attributed twins are never reconciled #266

Closed

Description

@ajianaz

Context

#261/#262 added comment dedup: insert_comment matches incoming comments by threads_comment_id, and backfills a legacy id-less row (threads_comment_id IS NULL) only when no exact-id row exists yet.

That leaves a permanent class of orphaned rows: legacy id-less rows fetched before #262 that already have an attributed twin in the same post. Once the attributed row exists, every future fetch takes the exact-match branch and the id-less twin is never revisited — no cleanup, migration, or reconcile path exists for it.

Evidence (production, 2026-09-11)

Post 01a084a5-75e0-7383-a87c-ad00ef90d3cb (account guyonlelucon, published 2026-09-09):

row id threads_comment_id fetched_at
01a08bc0-59d1-74c3-8ea8-c221b9138184 NULL 2026-09-10T14:37:14Z (pre-#262 fetch)
01a08bc0-e33c-74b0-968b-6f51738ea758 18131962096658047 2026-09-11 (backfilled/updated in place)

Same text, byte-identical (verified). Live re-fetch on 2026-09-11 confirmed: rows stay at 2, the attributed row updates in place, the id-less row is ignored forever. Impact: the single real comment is double-counted in sentiment (2× negative) and any per-comment workflow (reply targeting, priority).

Proposed fix

One-time reconciliation at startup (alongside existing migrations) or as a migration step: delete id-less rows that are superseded — where an attributed row with the same (post_id, text) exists:

DELETE FROM comments
WHERE threads_comment_id IS NULL
  AND EXISTS (
    SELECT 1 FROM comments c2
    WHERE c2.post_id = comments.post_id
      AND c2.text = comments.text
      AND c2.threads_comment_id IS NOT NULL
  );

Constraints to preserve (mirror #261's safety rules):

  • Only supersede when the attributed row's author matches or the legacy row is anonymous (author_username IS NULL), same as the existing backfill guard.
  • Never delete an id-less row that has reply_status != 'new' or stored reply_text — it carries workflow state the attributed twin lacks.
  • Log the number purged (tracing::info!) for observability.

Testing

  • Unit test: id-less row + attributed twin → purge removes exactly the id-less row.
  • Unit test: id-less row with reply_status != 'new' → NOT purged.
  • Unit test: id-less row without attributed twin → NOT purged (id-less is still the only row for Meta's standard-access behavior).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions