···401401402402 comment = Atvouch.Tangled.CommentBuilder.build_comment(author_did, author_handle, maintainer_routes, handle_map)
403403404404- # Record the comment first to prevent races between duplicate events
405405- now = DateTime.utc_now() |> DateTime.to_iso8601()
406406-407407- case Atvouch.BotComment.create(%{
408408- repo_at_uri: repo_at_uri,
409409- pull_number: pull_number,
410410- author_did: author_did,
411411- commented_at: now
412412- }) do
413413- {:ok, _} ->
404404+ Mutex.with_lock(Atvouch.Mutex, repo_at_uri, fn ->
405405+ # Re-check after acquiring the lock in case another process already commented
406406+ if Atvouch.BotComment.exists?(repo_at_uri, pull_number) do
407407+ Logger.debug("Comment already exists for #{repo_handle}/#{repo_name}##{pull_number}, skipping")
408408+ :ok
409409+ else
414410 case Atvouch.Tangled.Client.post_comment(repo_handle, repo_name, pull_number, comment) do
415411 :ok ->
412412+ now = DateTime.utc_now() |> DateTime.to_iso8601()
413413+414414+ Atvouch.BotComment.create(%{
415415+ repo_at_uri: repo_at_uri,
416416+ pull_number: pull_number,
417417+ author_did: author_did,
418418+ commented_at: now
419419+ })
420420+416421 Logger.info("Posted vouch comment on #{repo_handle}/#{repo_name}##{pull_number}")
417422 :ok
418423···420425 Logger.warning("Failed to post comment on #{repo_handle}/#{repo_name}##{pull_number}: #{inspect(reason)}")
421426 :ok
422427 end
423423-424424- {:error, %Ecto.Changeset{} = changeset} ->
425425- Logger.debug("Comment already recorded for #{repo_handle}/#{repo_name}##{pull_number}, skipping (#{inspect(changeset.errors)})")
426426- :ok
427427- end
428428+ end
429429+ end)
428430 end
429431430432end