Skip to content

Adding reference index to tenant creates the wrong index rule #506

@mike1o1

Description

@mike1o1

Describe the bug
If I have a resource that has both multitenancy (attribute) and an index reference, the generated index is just a duplicate of the tenant_id. For example:

defmodule MyApp.Tasks.Task do
  use Ash.Resource, otp_app: :my_app, domain: MyApp.Tasks, data_layer: AshPostgres.DataLayer

  postgres do
    table "tasks"
    repo MyApp.Repo

    references do
      reference :team, index?: true
    end
  end

  multitenancy do
    strategy :attribute
    attribute :team_id
  end
  
  relationships do
    belongs_to :team, MyApp.Accounts.Team, allow_nil?: false
  end
end

If I run migrations for this, I see the following index being created:

def up do
  create index(:tasks, [:team_id, :team_id])
end

def down do
  drop_if_exists index(:tasks, [:team_id, :team_id])
end

I'm not actually sure how Postgres/Ecto will handle this index behind the scenes, but it still doesn't seem like good behavior.

To Reproduce
Attempt to create a reference with an index to the same attribute as your multitenancy setup.

Click to view a passing test showing the behavior
### Add to `ash_postgres/test/migration_generator_test.exs`

test "index generated by index? true also adds tenant column when using attribute multitenancy" do
  defresource Org, "orgs" do
    attributes do
      uuid_primary_key(:id, writable?: true, public?: true)
      attribute(:name, :string, public?: true)
    end

    multitenancy do
      strategy(:attribute)
      attribute(:id)
    end
  end

  defposts do
    attributes do
      uuid_primary_key(:id)
      attribute(:key_id, :uuid, allow_nil?: false, public?: true)
      attribute(:foobar, :string, public?: true)
    end

    multitenancy do
      strategy(:attribute)
      attribute(:org_id)
    end

    relationships do
      belongs_to(:org, Org) do
        public?(true)
      end
    end

    postgres do
      references do
        reference(:org, index?: true)
      end
    end
  end

  defdomain([Org, Post])

  AshPostgres.MigrationGenerator.generate(Domain,
    snapshot_path: "test_snapshots_path",
    migration_path: "test_migration_path",
    quiet: true,
    format: false
  )

  assert [file] =
            Path.wildcard("test_migration_path/**/*_migrate_resources*.exs")
            |> Enum.reject(&String.contains?(&1, "extensions"))

  assert File.read!(file) =~ ~S{create index(:posts, [:org_id, :org_id])}
end

Expected behavior
Should not create a composite key of attribute and tenant attribute if they are the same.

** Runtime

  • Elixir version: 1.17.2
  • Erlang version: OTP 26
  • OS
  • Ash version: 3.4.67
  • Ash postgres version: 2.5.10

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions