Skip to content

Conversation

@barnabasJ
Copy link
Contributor

This would allow using a multitenancy :allow_global read action
for the scheduler and a regular read for the worker_read_action,
by setting the tenant from the attribute.

I could also add a flag for it if we want to keep it optional?

Contributor checklist

Leave anything that you believe does not apply unchecked.

  • I accept the AI Policy, or AI was not used in the creation of this PR.
  • Bug fixes include regression tests
  • Chores
  • Documentation changes
  • Features include unit/acceptance tests
  • Refactoring
  • Update dependencies

@zachdaniel
Copy link
Contributor

Makes sense to me, but we would need to do this:

https://hexdocs.pm/ash/dsl-ash-resource.html#multitenancy-parse_attribute

and parse the attribute. I'm not 100% sure on if it should be opt-in or not. We currently set the tenant only from the query which is done via list_tenants (which is TBH itself unideal how we do that). This change is theoretically breaking existing usage of resources that may be used that support bypassing multi tenancy?

Copy link
Contributor

@jimsynz jimsynz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tenant

@barnabasJ
Copy link
Contributor Author

Makes sense to me, but we would need to do this:

https://hexdocs.pm/ash/dsl-ash-resource.html#multitenancy-parse_attribute

and parse the attribute. I'm not 100% sure on if it should be opt-in or not. We currently set the tenant only from the query which is done via list_tenants (which is TBH itself unideal how we do that). This change is theoretically breaking existing usage of resources that may be used that support bypassing multi tenancy?

The changing behaviour is why I suggested maybe adding a flag. but I'm not sure it was actually possible to bypass multitenancy like this, because the multitenancy :allow_global option is only available for reads.
Ok, it might have been for cases where global was set for the whole resource.

@barnabasJ
Copy link
Contributor Author

Makes sense to me, but we would need to do this:

https://hexdocs.pm/ash/dsl-ash-resource.html#multitenancy-parse_attribute

and parse the attribute.

Is that doing anything? The attribute of the record should already have the parsed value?

@zachdaniel
Copy link
Contributor

If the tenant is something like org_1234, and the attribute value is parsed to 1234, then setting tenant to 1234 would be wrong. Actually...thats a problem 😄 parse_attribute doesn't work in reverse. So that kind of scuttles this plan unless we add a function for going the other way.

@barnabasJ
Copy link
Contributor Author

If the tenant is something like org_1234, and the attribute value is parsed to 1234, then setting tenant to 1234 would be wrong. Actually...thats a problem 😄 parse_attribute doesn't work in reverse. So that kind of scuttles this plan unless we add a function for going the other way.

I can PR that if you are open to it. I do think this would be pretty useful in ash oban

@zachdaniel
Copy link
Contributor

Open to it, yes 👍 Must be opt in for this case, but otherwise sounds great.

This would allow using a multitenancy `:allow_global` read action
for the scheduler and a regular read for the worker_read_action,
by setting the tenant from the attribute.
Add a DSL option `use_tenant_from_record?` (default: false) to gate
the tenant extraction feature from records. When set to true, the
tenant will be extracted from the record's tenant attribute when
building trigger jobs.

This allows using a multitenancy :allow_global read action for the
scheduler and a regular read action for the worker_read_action.

Changes:
- Add use_tenant_from_record? to Trigger schema with documentation
- Update Trigger typespec and defstruct
- Gate tenant extraction logic in build_trigger function
- Register new verifier in extension configuration

When false (default), only explicit :tenant options are used,
maintaining backward compatibility.
Add a verifier that ensures when use_tenant_from_record? is enabled,
the multitenancy parse_attribute and tenant_from_attribute options
are consistently configured - either both at defaults or both customized.

These options are inverses of each other, so if one is customized,
the other must also be customized to maintain correct tenant handling.

The verifier provides clear error messages explaining the requirement
and showing the current configuration state.
Update the test trigger to use the new use_tenant_from_record? flag,
ensuring the tenant extraction feature is explicitly enabled for testing.
Add use_tenant_from_record? to locals_without_parens in formatter
configuration to ensure consistent DSL formatting without parentheses.

Update test to use the correct format: `use_tenant_from_record? true`
instead of `use_tenant_from_record?(true)`.
Add use_tenant_from_record? option at the oban DSL level, allowing
it to be set as a default for all triggers in a resource.

The trigger-level option overrides the oban-level setting, following
the same pattern as shared_context?.

Changes:
- Add use_tenant_from_record? to oban schema with documentation
- Update SetDefaults transformer to inherit oban-level setting
- Maintain backward compatibility with default: false
@barnabasJ barnabasJ force-pushed the feat/set-tenant-from-record branch from 1092e46 to a48ce8d Compare October 29, 2025 12:52
Update documentation to be more user-focused, avoiding internal
implementation details like build_trigger.

Changes:
- Trigger-level: Focus on scheduler using :allow_global while worker
  gets correct tenant context per record
- Oban-level: Clarify it's a default value that can be overridden
- Remove references to internal build_trigger function
- Emphasize the user-visible behavior and use case
@barnabasJ
Copy link
Contributor Author

ash-project/ash#2412

Comment on lines 178 to 179
Oban.drain_queue(queue: :default)
Oban.drain_queue(queue: :custom)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the goal to clear the queues out before tests, or to execute whichever jobs are left in there? If the goal is to clear them out I would reach for Repo.delete_all(Job, prefix: "private") instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes more sense. Thanks for the tip.

Point ash dependency to GitHub main branch to access the new
multitenancy_tenant_from_attribute API function that is not yet
available in the released version.
Spark verifier errors are emitted as compile-time warnings to stderr,
not as Logger messages. This caused tests using ExUnit.CaptureLog to
fail because the error output was never captured.

Changes:
- Import ExUnit.CaptureIO instead of using CaptureLog
- Use capture_io(:stderr, fn -> ... end) to capture compiler warnings
- Improve assertions with regex patterns to match specific error format
- Use ~r/parse_attribute:.*\(customized\)/ to verify attribute state
- Assert output == "" for success cases (no errors emitted)

The regex patterns verify that both the attribute name and its state
(customized/default) appear on the same line, providing more precise
validation than loose string matching.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants