How to update a structure and do queries on a mnesia table with Ash?

can someone pls help me to understand the baselines for Ash?
I want to be able to update a structure and do querys on a mnesia table, but i’m having trouble to understand how to do that
Contract resource

defmodule Contract do
  use Ash.Resource,
    data_layer: EtsTableContracts,
    domain: nil, # Configuração explícita do domínio
    notifiers: []

  attributes do
    attribute :c_identifier, :string, allow_nil?: false, primary_key?: true
    attribute :status, :atom, default: :pending
    attribute :reason, :map, allow_nil?: true
  end

  actions do
    create :create do
      accept [:c_identifier]
      change set_attribute(:status, :processing) # Define status ao criar
    end

    update :update do
      accept [:c_identifier, :status, :reason]
      change fn changeset ->
        changeset
        |> Ash.Changeset.change_attribute(:c_identifier,:status, :processing)
      end
    end
  end
end

my data_layer

defmodule  EtsTableContracts do
  @moduledoc """
  Módulo responsável por inicializar e gerenciar o esquema e a tabela Mnesia para o estado do jogo.
  """

  def init do
    # Cria o esquema Mnesia (apenas se não existir)
    :mnesia.create_schema([node()])

    # Inicia o Mnesia
    :mnesia.start()

    # Cria a tabela GameState, se ainda não existir
    case :mnesia.create_table(:contract, [
      {:attributes, [:c_identifier, :status, :reason]},
      {:type, :set},
      {:ram_copies, [node()]}
         ]) do
      {:atomic, :ok} ->
        IO.puts("Tabela GameState criada com sucesso!")
      {:aborted, {:already_exists, _}} ->
        IO.puts("Tabela GameState já existe.")
      {:error, reason} ->
        IO.puts("Erro ao criar tabela GameState: #{inspect(reason)}")
    end
  end
end

and how does the flow of my code should be I expecto to have?
on the rubish way I was doing before trying to use Ashe, I interacted with my resouces in that way

defmodule Testespay.Genservers.ContractProcessor do

    @moduledoc """
    Gen server responsavel por processamento assincrono da segunda parte de verificação create_contract
    a validação depende de api externa, então quando o endpoint é hitado e antes do processamento ocorrer é registrado no ets
    payload == processing
    o processamento é então passado pra uma task assincrona nao supervisionada (por enquanto nao supervisionada)
    o estado é então atualizado pela task assim que o processamento termina
    """
    @state_processing :processing
    @state_approved :approved
    @state_error :error
    @state_error_dblimit :error_dblimit
    @time_toprocessqueue 2_000
    use GenServer
    require Logger

    def start_link(_opts) do
      GenServer.start_link(__MODULE__, %{queue: :queue.new()}, name: __MODULE__)
    end


    def init(_) do
      {:ok, %{queue: nil}} # Inicializa sem fila
    end

    def validate_and_registry_payload(request_map, payload) do
      GenServer.cast(__MODULE__, {:validate_and_registry_payload, request_map, payload})
    end

    def retry_payloadqueue(payload) do
      GenServer.cast(__MODULE__, {:retry_payload, payload})
    end

    def handle_cast({:validate_and_registry_payload, request_map, payload}, state) do
      Logger.debug("payload cast post")
      Logger.debug(payload)
      Utils.Ets.Acess.insert_payload(payload, @state_processing, :chubby)
      Task.start(fn ->
        Logger.debug("executing task")
        Logger.debug("payload cast post")
        Logger.debug(payload)
        with {:ok, payload_value} <- MyApp.Pix.PixMod.get_qrcode_value_from_payload(payload),
              {:ok, resp} <- GenServerUtils.populate_utils(request_map, payload_value) do
                Logger.debug("Atualizando payload no ETS para estado aprovado")
                Utils.Ets.Acess.update_payload(payload, @state_approved, resp)
        else
          {:error, reason} ->
            Logger.debug("contrato não aprovado: #{inspect(reason)}")
            if reason == :max_db_limit do
              GenServer.cast(__MODULE__, {:enqueue_payload, request_map})
              Utils.Ets.Acess.update_payload(payload, @state_error_dblimit, :chubby)
            else
              Utils.Ets.Acess.update_payload(payload, @state_error, reason)
            end
        end

      end)

      {:noreply, state}
    end

    @impl true
    def handle_cast({:enqueue_payload, valid_changeset_map}, %{queue: nil} = state) do
      new_queue = [valid_changeset_map]
      {:noreply, %{state | queue: new_queue}}
    end

    def handle_cast({:enqueue_payload, valid_changeset_map}, %{queue: queue} = state) do
      new_queue = queue ++ [valid_changeset_map]
      {:noreply, %{state | queue: new_queue}}
    end

    def handle_info(:process_queue, %{queue: queue} = state) do
      MyApp.GenServer.ProcessQueue.process(queue)
      Logger.warn("implementar DIREITO error handling no processamento de queue e processamento de queue")

      Task.start(fn ->
        process_queue_items(:queue.to_list(queue))
      end)

      {:noreply, %{state | queue: nil}} # Esse 'end' agora fecha a função corretamente
    end

    defp process_queue_items(queue_items) do
      Enum.each(queue_items, fn item ->
        case GenServerUtils.populate_utils_without_get_value(item) do
          {:ok, _result} ->
            IO.puts("Processado com sucesso: #{inspect(item)}")

          {:error, reason} ->
            IO.puts("Erro ao processar #{inspect(item)}: #{inspect(reason)}")
        end
      end)
    end
  end

im not into system design neither elixir, so trying to get bot at the same has being overwhelming, hope no one think it’s a lazy question :slight_smile:

:thinking: Are you trying to map an Ash resource to an existing Mnesia table? It’s very rare that people will start off by writing their own data layers.

It’s hard to look over all of your code and suggest a “right” way, perhaps if I had more time. But I can definitely say you’re into a relatively complex aspect of Ash that I don’t think you really need. At least not when starting out with Ash.

I suspect that an LLM or two helped you with this code and unfortunately you just aren’t going to have great luck getting help from LLMs for Ash. Its too new :slight_smile:

defmodule YourDomain do
  use Ash.Domain
  
  resources do
    resource Contract
  end
end

# in a different file
defmodule Contract do
  use Ash.Resource,
    data_layer: Ash.DataLayer.Mnesia, # use our built in Mnesia data layer
    domain: YourDomain # you need a domain

  attributes do
    attribute :c_identifier, :string, allow_nil?: false, primary_key?: true
    attribute :status, :atom, default: :pending
    attribute :reason, :map, allow_nil?: true
  end

  actions do
    create :create do
      primary? true
      accept [:c_identifier]
      change set_attribute(:status, :processing)
    end

    update :update do
      primary? true
      accept [:c_identifier, :status, :reason]
      change set_attribute(:status, :processing)
    end
  end
end

That resource would be sufficient to store data in an Mnesia table.

Make sure to add:

Mnesia.create_schema([node()])

in your applications start function.

If you just want to use ETS, then you can use Ash.DataLayer.Ets and no need to add anything to your application start function.