btajudeen

btajudeen

Hi Zach, I am building a multi-tenant SaaS application using Ash v3 with a schema/context-based multitenancy strategy.

During tenant onboarding, I execute a synchronous form submission that successfully creates a Workspace (a global public resource). Immediately after, I kick off a background setup worker using Phoenix LiveView’s start_async/3 to provision the tenant.

Inside that async worker thread, before any multi-tenancy prefixing occurs, I need to update a global user record in the public schema to lock down their last_active_workspace_id. However, the query fails instantly.

Here is the code inside my async task process:

Accfarm.Shared.User
|> Ash.get!(workspace.owner_id, domain: Accfarm.SharedDomain, authorize?: false)
|> Ash.Changeset.for_update(:update, %{last_active_workspace_id: workspace.id})
|> Ash.update!(domain: Accfarm.SharedDomain, authorize?: false)

And heres my Debug log output

[debug] CREATE SCHEMA IF NOT EXISTS "org_green-valley-hub" [ ]
[debug] Accfarm.Shared.User.read: skipped query run due to filter being false"

đź”´ SYSTEM MULTI-TENANT PROVISIONING EXCEPTION: %Ash.Error.Invalid{
errors: [
  %Ash.Error.Query.NotFound{
    primary_key: %{id: "019e87e3-884d-7325-8a74-f938a1d373db"},
    resource: Accfarm.Shared.User
  }
]
}

My Resource Configurations:
Both Workspace and User reside in the public schema and are explicitly configured as global resources:

multitenancy do
  global? true
end

Because authorize?: false is specified, I expected the policy engine to be completely bypassed. Why is Ash generating a filter being false condition on a global public resource read, and how do I execute a system-level override inside a background worker process to update this public data row?

Showing Posts 1 to 5

zachdaniel

zachdaniel

Creator of Ash

:thinking: So its not only authorization, its any case whereby the filter is false. Hard to say just from the information there why that would be happening though. Are there preparations on the primary read action that could be filtering the action in some way that produce statically false?

btajudeen

btajudeen OP

"Thanks for the pointer, Zach! That makes total sense. I checked the logs and it is definitely a static filter rewrite preventing the database call entirely.

To help me debug what preparation or base filter is causing this, here is the complete actions do ... end block and attributes layout for my Accfarm.Shared.User resource file:

lib/accfarm/shared/user.ex

multitenancy do
global? true
end

attributes do
uuid_v7_primary_key :id
attribute :email, :ci_string, allow_nil?: false
attribute :last_active_workspace_id, :uuid
attribute :system_role, :atom, default: :user, constraints: [one_of: [:user, :admin]]
timestamps()
end

actions do
defaults [:destroy, :update]

This is the action being invoked by Ash.get!/3

read :read do
primary? true

Are there implicit global preparations or multi-tenancy filters

injected here under the hood by AshPostgres when strategy :context is active elsewhere?

end
end

policies do

policy always(), do: authorize_if(always())
end

Given that this resource has global? true enabled, does the AshPostgres.DataLayer inject implicit tenant routing filters onto primary read actions if other domains in the application use a context/schema multi-tenancy strategy? How can I inspect or trace the exact statically generated expression that is forcing filter being false on this read path?"


zachdaniel

zachdaniel

Creator of Ash

Typically this would include a multitenancy attribute, i.e

multitenancy do
  global? true
  attribute :attribute
end

If you’re just saying “it’s not multi tenant” then you’d leave the block out. Its possible that we have an issue where it doesn’t check that there is an attribute and tries to do something silly like nil == nil (i.e no attribute, no tenant)?

btajudeen

btajudeen OP

"You nailed it perfectly, Zach! That is exactly what was happening.

Both User and Workspace are global public resources that do not have an isolation column attribute because they live exclusively outside the tenant schema boundaries. I had declared a blank multitenancy do global? true end block inside them thinking it was required to declare them as global.

Once that block is evaluated in a background async task without a process tenant set, it triggers that hidden nil == nil state mismatch, which appends a hard where false onto the query builder path [debug].

Removing the multitenancy block entirely from those global public files fixes the query compilation rewrite instantly [debug]! Thanks for the brilliant architectural insight."

zachdaniel

zachdaniel

Creator of Ash

Would you mind opening an issue about this on the ash repo?

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews