diff --git a/.woodpecker/docker-combine.yaml b/.woodpecker/docker-combine.yaml index 9b9f89fdb..fc69430fe 100644 --- a/.woodpecker/docker-combine.yaml +++ b/.woodpecker/docker-combine.yaml @@ -18,7 +18,7 @@ labels: steps: docker-develop-combine: - image: git.fluffytail.org/phnt/wpc-docker-tagger:latest + image: &docker_image git.pleroma.social/phnt/wpc-docker-tagger:0.9.1 when: - event: push branch: ${CI_REPO_DEFAULT_BRANCH} @@ -31,14 +31,14 @@ steps: tags: - latest - develop - - ${CI_COMMIT_SHA:0:8} + - "${CI_COMMIT_SHA:0:8}" username: from_secret: pleroma-ci-user password: from_secret: pleroma-ci-password docker-stable-combine: - image: git.fluffytail.org/phnt/wpc-docker-tagger:latest + image: *docker_image when: - evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""' settings: @@ -46,10 +46,10 @@ steps: tags: &stable_docker_tags - latest - stable - - ${CI_COMMIT_SHA:0:8} + - "${CI_COMMIT_SHA:0:8}" docker-stable-tag-combine: - image: git.fluffytail.org/phnt/wpc-docker-tagger:latest + image: *docker_image when: - event: tag - evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""' @@ -57,4 +57,4 @@ steps: <<: *docker_settings tags: - <<: *stable_docker_tags - - ${CI_COMMIT_TAG} + - "${CI_COMMIT_TAG}" diff --git a/.woodpecker/otp-musl.yaml b/.woodpecker/otp-musl.yaml index 60558b893..4443d992a 100644 --- a/.woodpecker/otp-musl.yaml +++ b/.woodpecker/otp-musl.yaml @@ -33,10 +33,10 @@ variables: - mkdir release - export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH} - mix release --path release - build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 + build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-alpine-3.22.5 build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine - build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 - artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 + build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-alpine-3.22.5 + artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.1 artifacts_uploader_settings: &artifacts_uploader_settings user: from_secret: pleroma-ci-user diff --git a/.woodpecker/otp.yaml b/.woodpecker/otp.yaml index 9a33c228e..6b7a8124e 100644 --- a/.woodpecker/otp.yaml +++ b/.woodpecker/otp.yaml @@ -33,10 +33,10 @@ variables: - mkdir release - export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH} - mix release --path release - build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 + build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-ubuntu-noble-20260810 build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3 - build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 - artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 + build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-ubuntu-noble-20260810 + artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.1 artifacts_uploader_settings: &artifacts_uploader_settings user: from_secret: pleroma-ci-user diff --git a/.woodpecker/paradedb-integration.yaml b/.woodpecker/paradedb-integration.yaml index b91222837..40994662f 100644 --- a/.woodpecker/paradedb-integration.yaml +++ b/.woodpecker/paradedb-integration.yaml @@ -52,12 +52,14 @@ steps: services: postgres: image: docker.io/postgres:13-alpine + failure: ignore environment: POSTGRES_DB: pleroma_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres paradedb: image: docker.io/paradedb/paradedb:0.24.3-pg15@sha256:997d62f6c2605becb7c45878af3f8c8c698f355006b675b3ce212d4eb2d14f2e + failure: ignore environment: POSTGRES_DB: paradedb POSTGRES_USER: postgres diff --git a/.woodpecker/unit-testing-elixir-1.15.yaml b/.woodpecker/unit-testing-elixir-1.15.yaml index 0f8eac566..13eb40244 100644 --- a/.woodpecker/unit-testing-elixir-1.15.yaml +++ b/.woodpecker/unit-testing-elixir-1.15.yaml @@ -40,6 +40,7 @@ steps: services: postgres: image: postgres:13-alpine + failure: ignore environment: POSTGRES_DB: pleroma_test POSTGRES_USER: postgres diff --git a/.woodpecker/unit-testing-elixir-1.19.yaml b/.woodpecker/unit-testing-elixir-1.19.yaml index 60b0826a7..156fc350b 100644 --- a/.woodpecker/unit-testing-elixir-1.19.yaml +++ b/.woodpecker/unit-testing-elixir-1.19.yaml @@ -40,6 +40,7 @@ steps: services: postgres: image: postgres:13-alpine + failure: ignore environment: POSTGRES_DB: pleroma_test POSTGRES_USER: postgres diff --git a/changelog.d/domain-block-subplan-cost.fix b/changelog.d/domain-block-subplan-cost.fix new file mode 100644 index 000000000..7ae866ad1 --- /dev/null +++ b/changelog.d/domain-block-subplan-cost.fix @@ -0,0 +1 @@ +Fix notification and timeline queries for users with domain blocks choosing a plan that scans every active user per row; the followed-accounts exception is now an `ARRAY(SELECT ...)` InitPlan that the planner costs once. diff --git a/changelog.d/idempotency-plug-func-typo.skip b/changelog.d/idempotency-plug-func-typo.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/wpc-tagger-mirror.skip b/changelog.d/wpc-tagger-mirror.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index e27f78d33..b803c07b4 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -258,10 +258,16 @@ defmodule Pleroma.FollowingRelationship do [_, user_actor: user_actor], fragment( # "(actor's domain NOT in domain_blocks) OR (actor IS in followed AP IDs)" + # + # ARRAY(SELECT ...) makes the followed-ids lookup an InitPlan that is + # evaluated and costed once. As a plain subselect, the planner charges + # its startup cost on every rescan of the actor lookup, which inflates + # the cost of the index plan enough for it to lose to a plan that + # materialises every active user for every candidate row. """ NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR - ? = ANY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr - ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = ?) + ? = ANY(ARRAY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr + ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = ?)) """, user_actor.ap_id, ^user.domain_blocks, diff --git a/lib/pleroma/web/plugs/idempotency_plug.ex b/lib/pleroma/web/plugs/idempotency_plug.ex index 81983efba..1cb328622 100644 --- a/lib/pleroma/web/plugs/idempotency_plug.ex +++ b/lib/pleroma/web/plugs/idempotency_plug.ex @@ -33,7 +33,7 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlug do case @cachex.get(:idempotency_cache, cache_key) do {:ok, nil} -> - cache_resposnse(conn, key, cache_key) + cache_response(conn, key, cache_key) {:ok, record} -> send_cached(conn, key, record) @@ -43,7 +43,7 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlug do end end - defp cache_resposnse(conn, key, cache_key) do + defp cache_response(conn, key, cache_key) do register_before_send(conn, &maybe_cache_response(&1, key, cache_key)) end diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs index a2be2ae49..992b15f8b 100644 --- a/test/pleroma/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -1028,6 +1028,21 @@ defmodule Pleroma.NotificationTest do assert Notification.for_user(user) == [] end + test "it doesn't return notifications from a blocked domain unless the actor is followed", + %{user: user} do + blocked_domain = "blocked.example" + stranger = insert(:user, local: false, domain: blocked_domain) + followed = insert(:user, local: false, domain: blocked_domain) + {:ok, user, followed} = User.follow(user, followed) + {:ok, user} = User.block_domain(user, blocked_domain) + + {:ok, _} = CommonAPI.post(stranger, %{status: "hey @#{user.nickname}"}) + {:ok, followed_activity} = CommonAPI.post(followed, %{status: "hey @#{user.nickname}"}) + + assert [%{activity: %{id: followed_id}}] = Notification.for_user(user) + assert followed_id == followed_activity.id + end + test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com")