persistSharingPost currently checks for an existing share by the Announce activity IRI before inserting a new sharing post. That is not enough to make share handling idempotent.
The same actor can announce the same original post more than once, either because of duplicate delivery, retries, or different Announce activity IRIs for the same (actor, object) pair. In that case, Hollo may fail on the posts_actor_id_sharing_id_unique constraint after it has already called persistPost() for the original object.
That matters because persistPost() can fetch and walk the remote replies collection. A duplicate Announce should be a cheap no-op, but today it can still trigger remote fetches before the database constraint rejects the insert.
This issue tracks only the idempotency part of #443. The broader question of whether persistPost() should eagerly scrape remote replies should be handled separately.
Expected behavior
When Hollo receives an Announce, persistSharingPost should treat an existing (actor_id, sharing_id) pair as an already persisted share and return that existing post instead of attempting another insert.
Possible approaches
- After resolving the announcing actor and original post, look up an existing post with the same
accountId and sharingId, including the same relations returned by the current existingPost query.
- Alternatively, use
onConflictDoNothing() for the unique constraint and perform a follow-up lookup when the insert is skipped.
Acceptance criteria
- Duplicate Announces for the same actor and original post do not raise a unique-constraint error.
- Duplicate Announces return the existing sharing post.
- The duplicate path does not append another share to timelines.
- The duplicate path does not increment the original post's
sharesCount again.
- There is a regression test covering duplicate Announces with different
Announce IRIs but the same actor and original object.
persistSharingPostcurrently checks for an existing share by theAnnounceactivity IRI before inserting a new sharing post. That is not enough to make share handling idempotent.The same actor can announce the same original post more than once, either because of duplicate delivery, retries, or different
Announceactivity IRIs for the same(actor, object)pair. In that case, Hollo may fail on theposts_actor_id_sharing_id_uniqueconstraint after it has already calledpersistPost()for the original object.That matters because
persistPost()can fetch and walk the remoterepliescollection. A duplicateAnnounceshould be a cheap no-op, but today it can still trigger remote fetches before the database constraint rejects the insert.This issue tracks only the idempotency part of #443. The broader question of whether
persistPost()should eagerly scrape remote replies should be handled separately.Expected behavior
When Hollo receives an
Announce,persistSharingPostshould treat an existing(actor_id, sharing_id)pair as an already persisted share and return that existing post instead of attempting another insert.Possible approaches
accountIdandsharingId, including the same relations returned by the currentexistingPostquery.onConflictDoNothing()for the unique constraint and perform a follow-up lookup when the insert is skipped.Acceptance criteria
sharesCountagain.AnnounceIRIs but the same actor and original object.