# 8. Mnesia (DBMS)

### 📋 ÍNDICE

1. Visão Geral do Mnesia
2. Arquitetura e Conceitos Fundamentais
3. Definição de Tabelas
4. Tipos de Tabelas e Estratégias de Armazenamento
5. Operações Básicas: CRUD
6. Consultas com QLC (Query List Comprehensions)
7. Transações no Mnesia
8. Transações Sujas (Dirty Operations)
9. Índices e Otimização
10. Distribuição e Replicação
11. Tolerância a Falhas e Recuperação
12. Mnesia vs Outros Bancos de Dados
13. Boas Práticas e Performance
14. Resumo Técnico
15. Referência Rápida

***

### 1. VISÃO GERAL DO MNESIA

O **Mnesia** é um sistema de gerenciamento de banco de dados (DBMS) distribuído, embutido e de tempo real que faz parte do ecossistema Erlang/OTP. Ele foi projetado especificamente para aplicações que rodam na BEAM, combinando características de bancos relacionais com a flexibilidade de bancos NoSQL, tudo com forte integração com a linguagem e a VM.

#### Características Fundamentais do Mnesia

Diferente de bancos de dados tradicionais (como PostgreSQL ou MySQL) que rodam como processos separados, o Mnesia é **embutido** na mesma VM que sua aplicação. Isso significa que não há latência de rede entre sua aplicação e o banco de dados, resultando em performance excepcional para operações de baixa latência.

```yaml
CARACTERÍSTICAS PRINCIPAIS DO MNESIA:

  🎯 PROPÓSITO:
    - Banco de dados embutido na BEAM
    - Projetado para sistemas de telecomunicações (Ericsson)
    - Ideal para dados de configuração, roteamento, sessões
    - Operações em tempo real com latência previsível
  
  🔑 DIFERENCIAIS ÚNICOS:
    - Distribuído nativamente: tabelas podem ser fragmentadas e replicadas
    - Tolerância a falhas: replicação entre nós
    - Transações ACID com isolamento configurável
    - Sem separação processo/SQL - integração direta com Elixir/Erlang
    - Suporte a mudanças de schema em runtime (hot code swapping)
    - Operações "sujas" (dirty) para máxima performance
  
  📊 CASOS DE USO TÍPICOS:
    - Configuração de roteamento em sistemas de telefonia
    - Gerenciamento de sessões distribuídas
    - Cache de dados com replicação
    - Banco de dados embarcado (IoT, Nerves)
    - Dados de configuração de sistemas de alta disponibilidade
```

#### Por que usar Mnesia no Elixir?

O Mnesia é frequentemente subestimado no ecossistema Elixir, onde muitos desenvolvedores recorrem diretamente ao PostgreSQL ou Redis. No entanto, o Mnesia oferece vantagens únicas quando usado apropriadamente:

1. **Zero Latência de Rede:** O banco roda na mesma VM, eliminando round-trips
2. **Transações ACID com Performance:** Transações com isolamento real e alta velocidade
3. **Distribuição Transparente:** Dados podem ser replicados sem trabalho extra
4. **Schema Dinâmico:** Você pode alterar a estrutura de tabelas em runtime, sem migrações complexas

```elixir
# Exemplo: Usando Mnesia diretamente no Elixir
# O banco roda na mesma BEAM, sem processos externos!

:application.start(:mnesia)  # Inicia o Mnesia na VM

# Define uma tabela
:ok = :mnesia.create_table(:usuario, [
  {:attributes, [:id, :nome, :email, :idade]},
  {:type, :set},
  {:disc_copies, [node()]}
])

# Operação transacional (segura e ACID)
usuario = {:usuario, 1, "João Silva", "joao@email.com", 30}
{:atomic, :ok} = :mnesia.transaction(fn ->
  :mnesia.write(usuario)
end)

# Leitura com transação
{:atomic, result} = :mnesia.transaction(fn ->
  :mnesia.read({:usuario, 1})
end)
# Result: [{:usuario, 1, "João Silva", "joao@email.com", 30}]
```

***

### 2. ARQUITETURA E CONCEITOS FUNDAMENTAIS

#### 2.1 Estrutura do Mnesia

O Mnesia opera como um conjunto de tabelas gerenciadas pela VM, com cada tabela podendo ser armazenada em memória RAM, disco ou uma combinação de ambos. A comunicação entre nós é feita através do protocolo de distribuição nativo da BEAM.

```
ARQUITETURA DO MNESIA

┌─────────────────────────────────────────────────────────────┐
│                       NÓ A (BEAM)                           │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐   ┌──────────────┐   ┌─────────────────┐  │
│  │   Tabela A   │   │   Tabela B   │   │    Tabela C     │  │
│  │ (ram_copies) │   │(disc_copies) │   │(disc_only_copies│  │
│  └──────────────┘   └──────────────┘   └─────────────────┘  │
│          │                 │                    │           │
│  ┌───────▼─────────────────▼────────────────────▼────────┐  │
│  │          MNESIA TM (Transaction Manager)              │  │
│  ├────────────────────────────────────────────────────────┤  │
│  │          Schema (Local Definition & Locks)             │  │
│  └────────────────────────────────────────────────────────┘  │
└─────────────────────────────┬───────────────────────────────┘
                              │
             REPLICAÇÃO (Erlang Distribution)
                              │
┌─────────────────────────────▼───────────────────────────────┐
│                       NÓ B (BEAM)                           │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐   ┌──────────────┐   ┌─────────────────┐  │
│  │ Tabela A (R) │   │ Tabela C (R) │   │  Locker/Log     │  │
│  │ (ram_copies) │   │(disc_copies) │   │  (Mnesia Core)  │  │
│  └──────────────┘   └──────────────┘   └─────────────────┘  │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │          Schema (Global Synchronization)              │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 Conceitos Fundamentais

```yaml
CONCEITOS CHAVE DO MNESIA:

  📦 TABELAS:
    - Conjuntos de registros com estrutura definida
    - Tipos: set (chave única), ordered_set (ordenado), bag (chaves duplicadas)
    - Localização: ram_copies, disc_copies, disc_only_copies
  
  📝 REGISTROS:
    - Tuplas Elixir/Erlang: {nome_tabela, chave, atributo1, atributo2, ...}
    - Primeiro elemento: nome da tabela (átomo)
    - Segundo elemento: chave primária
    - Demais elementos: atributos na ordem definida no schema
  
  🔑 CHAVE PRIMÁRIA:
    - Primeiro campo após o nome da tabela
    - Deve ser única (exceto para tabelas :bag)
    - Usada para acesso rápido e indexação
  
  📋 SCHEMA:
    - Metadados das tabelas (estrutura, localização, índices)
    - Pode ser alterado em runtime
    - Deve ser consistente entre nós do cluster
  
  🔄 TRANSAÇÕES:
    - Blocos de código com isolamento ACID
    - Locking otimista (padrão) ou pessimista
    - Retornam {:atomic, result} ou {:aborted, reason}
  
  🏃 DIRTY OPERATIONS:
    - Operações sem transação (alta performance)
    - Não fornecem isolamento ACID
    - Úteis para dados não críticos ou alta performance
```

#### 2.3 Inicialização e Configuração

Antes de usar o Mnesia, é necessário inicializá-lo e configurar o diretório de dados. Em sistemas distribuídos, todos os nós devem compartilhar o mesmo schema ou ter schemas compatíveis.

```elixir
defmodule MeuApp.MnesiaSetup do
  @moduledoc """
  Configuração e inicialização do Mnesia para a aplicação.
  """
  
  # Inicializa o Mnesia no nó atual
  def setup do
    # Define o diretório onde os dados serão persistidos
    :application.set_env(:mnesia, :dir, './mnesia_data')
    
    # Inicia a aplicação Mnesia
    :application.start(:mnesia)
    
    # Aguarda o Mnesia ficar pronto
    wait_for_mnesia()
  end
  
  defp wait_for_mnesia do
    case :mnesia.system_info(:is_running) do
      :yes -> :ok
      _ -> 
        Process.sleep(100)
        wait_for_mnesia()
    end
  end
  
  # Para sistemas distribuídos, você precisa definir os nós
  def join_cluster(nodes) do
    # Define os nós do cluster
    :mnesia.start()
    
    # Configura o schema para ser distribuído
    case :mnesia.create_schema(nodes) do
      :ok -> IO.puts("Schema criado com sucesso")
      {:error, reason} -> IO.puts("Erro: #{reason}")
    end
    
    # Inicia o Mnesia em todos os nós
    Enum.each(nodes, fn node ->
      :rpc.call(node, :mnesia, :start, [])
    end)
  end
end
```

***

### 3. DEFINIÇÃO DE TABELAS

#### 3.1 Estrutura Básica de uma Tabela

No Mnesia, as tabelas são definidas através do schema, especificando seu nome, atributos, tipo e estratégia de armazenamento. Os atributos são posicionais, e o primeiro campo após a chave é o primeiro atributo.

```elixir
# Definição básica de tabela
:ok = :mnesia.create_table(:usuario, [
  # Lista de atributos (posicional)
  {:attributes, [:id, :nome, :email, :idade]},
  
  # Tipo de tabela: :set (chave única), :ordered_set, :bag
  {:type, :set},
  
  # Onde os dados serão armazenados
  {:ram_copies, [node()]},        # Em memória neste nó
  # {:disc_copies, [node()]},     # Em disco (RAM + persistent)
  # {:disc_only_copies, [node()]}, # Apenas disco (mais lento)
  
  # Chave primária (padrão é o primeiro atributo)
  {:keypos, 2},  # Posição da chave (default: 2)
])

# Registro na tabela: {nome_tabela, chave, atributo1, atributo2, ...}
registro = {:usuario, 1, "João", "joao@email.com", 30}
```

#### 3.2 Tipos de Tabelas

Cada tipo de tabela tem características específicas que afetam performance, uso de memória e comportamento de consultas.

| Tipo              | Descrição                                   | Uso Típico                                 | Performance                                |
| ----------------- | ------------------------------------------- | ------------------------------------------ | ------------------------------------------ |
| **:set**          | Chaves únicas, sem ordenação                | Dados com chave única (usuários, produtos) | Leitura O(1), escrita O(1)                 |
| **:ordered\_set** | Chaves únicas, ordenadas                    | Rang queries, dados com ordenação natural  | Leitura O(log n), range queries eficientes |
| **:bag**          | Permite múltiplos registros com mesma chave | Histórico, logs, versões                   | Leitura O(n) para múltiplos registros      |

```elixir
# Exemplo de cada tipo

# :set - caso mais comum (chave única)
:ok = :mnesia.create_table(:usuario_set, [
  {:attributes, [:id, :nome]},
  {:type, :set},
  {:ram_copies, [node()]}
])

# :ordered_set - útil para queries de range
:ok = :mnesia.create_table(:usuario_ordenado, [
  {:attributes, [:id, :nome]},
  {:type, :ordered_set},  # Mantém registros ordenados por chave
  {:ram_copies, [node()]}
])

# :bag - múltiplos registros com mesma chave
:ok = :mnesia.create_table(:eventos_bag, [
  {:attributes, [:usuario_id, :evento, :timestamp]},
  {:type, :bag},  # Permite múltiplos eventos para o mesmo usuário
  {:ram_copies, [node()]}
])
```

#### 3.3 Storage Types (Onde os dados vivem)

Uma das características mais poderosas do Mnesia é a capacidade de escolher onde cada tabela será armazenada, por nó e por tabela.

```yaml
TIPOS DE ARMAZENAMENTO:

  💾 RAM_COPIES (APENAS MEMÓRIA):
    - Dados residem apenas em RAM
    - Mais rápido (acesso direto à memória)
    - Volátil: dados são perdidos após reinicialização
    - Ótimo para: cache, sessões temporárias, dados não críticos
  
  💿 DISC_COPIES (RAM + DISCO):
    - Cópia em RAM para leitura/escrita
    - Persistido em disco (Dets)
    - Dados sobrevivem a reinicializações
    - Levemente mais lento que RAM only
    - Ótimo para: dados críticos que precisam persistência
  
  🪨 DISC_ONLY_COPIES (APENAS DISCO):
    - Dados residem apenas em disco (Dets)
    - Mais lento (acesso a disco)
    - Baixo uso de RAM
    - Ótimo para: logs, histórico, dados grandes pouco acessados
```

```elixir
# Controlando storage type por nó e por tabela
defmodule StorageConfig do
  def setup_tables do
    # Tabela de configuração: persistente, mas rápido acesso
    :mnesia.create_table(:config, [
      {:attributes, [:key, :value]},
      {:disc_copies, [node()]},  # RAM + Disco
      {:storage_properties, [
        {:ets, [compressed: true]},  # Compressão em RAM
        {:dets, [auto_save: 5000]}   # Auto-save a cada 5s
      ]}
    ])
    
    # Tabela de sessões: volátil, máxima performance
    :mnesia.create_table(:sessao, [
      {:attributes, [:token, :usuario_id, :criado_em]},
      {:ram_copies, [node()]},  # Apenas memória
      {:record_name, :sessao}
    ])
    
    # Tabela de logs: apenas disco, baixo uso de RAM
    :mnesia.create_table(:log, [
      {:attributes, [:timestamp, :nivel, :mensagem]},
      {:disc_only_copies, [node()]},  # Apenas disco
      {:type, :bag}  # Múltiplos logs por timestamp
    ])
  end
end
```

***

### 4. OPERAÇÕES BÁSICAS: CRUD

#### 4.1 Escrevendo Registros (Create/Update)

A operação `:mnesia.write/1` insere ou atualiza um registro na tabela. Dentro de uma transação, ela é segura e atômica.

```elixir
defmodule CrudOperations do
  # Inserir um novo registro
  def criar_usuario(id, nome, email, idade) do
    # Executa dentro de uma transação
    :mnesia.transaction(fn ->
      # Registro: {tabela, chave, atributos...}
      registro = {:usuario, id, nome, email, idade}
      
      # Write insere ou atualiza
      :mnesia.write(registro)
      {:ok, registro}
    end)
  end
  
  # Inserir múltiplos registros na mesma transação
  def criar_varios_usuarios(usuarios) do
    :mnesia.transaction(fn ->
      Enum.each(usuarios, fn {id, nome, email, idade} ->
        :mnesia.write({:usuario, id, nome, email, idade})
      end)
      {:ok, Enum.count(usuarios)}
    end)
  end
  
  # Atualizar um campo específico
  def atualizar_email(id, novo_email) do
    :mnesia.transaction(fn ->
      case :mnesia.read({:usuario, id}) do
        [] -> {:error, :nao_encontrado}
        [usuario] ->
          # Atualiza o email (posição 4 na tupla)
          usuario_atualizado = put_elem(usuario, 4, novo_email)
          :mnesia.write(usuario_atualizado)
          {:ok, usuario_atualizado}
      end
    end)
  end
  
  # Atualização condicional
  def incrementar_idade(id) do
    :mnesia.transaction(fn ->
      case :mnesia.read({:usuario, id}) do
        [] -> {:error, :nao_encontrado}
        [usuario] ->
          idade_atual = elem(usuario, 5)
          usuario_atualizado = put_elem(usuario, 5, idade_atual + 1)
          :mnesia.write(usuario_atualizado)
          {:ok, usuario_atualizado}
      end
    end)
  end
end
```

#### 4.2 Lendo Registros (Read)

A leitura no Mnesia pode ser feita por chave primária (mais rápida) ou através de consultas QLC.

```elixir
defmodule ReadOperations do
  # Leitura por chave primária (mais rápida)
  def buscar_usuario_por_id(id) do
    :mnesia.transaction(fn ->
      case :mnesia.read({:usuario, id}) do
        [] -> nil
        [usuario] -> usuario
      end
    end)
  end
  
  # Leitura com pattern matching
  def buscar_por_email(email) do
    :mnesia.transaction(fn ->
      # Usa QLC para busca por email
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:sort([{:email, email}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Leitura de múltiplas chaves
  def buscar_multiplos_usuarios(ids) do
    :mnesia.transaction(fn ->
      Enum.map(ids, fn id ->
        :mnesia.read({:usuario, id})
      end)
      |> List.flatten()
    end)
  end
  
  # Leitura de toda tabela (CUIDADO! grande)
  def listar_todos_usuarios do
    :mnesia.transaction(fn ->
      :mnesia.foldl(fn usuario, acc ->
        [usuario | acc]
      end, [], :usuario)
    end)
  end
  
  # Melhor: leitura com paginação
  def listar_usuarios_paginado(offset, limit) do
    :mnesia.transaction(fn ->
      :mnesia.table(:usuario)
      |> Stream.drop(offset)
      |> Enum.take(limit)
    end)
  end
end
```

#### 4.3 Deletando Registros (Delete)

```elixir
defmodule DeleteOperations do
  # Deletar por chave primária
  def deletar_usuario(id) do
    :mnesia.transaction(fn ->
      :mnesia.delete({:usuario, id})
      :ok
    end)
  end
  
  # Deletar vários registros em uma transação
  def deletar_usuarios_em_lote(ids) do
    :mnesia.transaction(fn ->
      Enum.each(ids, fn id ->
        :mnesia.delete({:usuario, id})
      end)
      {:ok, length(ids)}
    end)
  end
  
  # Deletar todos os registros da tabela (CUIDADO!)
  def limpar_tabela do
    :mnesia.transaction(fn ->
      :mnesia.clear_table(:usuario)
      :ok
    end)
  end
  
  # Deletar condicional (ex: usuários com idade < 18)
  def deletar_menores_de_idade do
    :mnesia.transaction(fn ->
      usuarios = :mnesia.foldl(fn usuario, acc ->
        case elemento_idade = elem(usuario, 5) do
          idade when idade < 18 -> [usuario | acc]
          _ -> acc
        end
      end, [], :usuario)
      
      Enum.each(usuarios, fn {_, id, _, _, _} ->
        :mnesia.delete({:usuario, id})
      end)
      
      {:ok, length(usuarios)}
    end)
  end
end
```

***

### 5. CONSULTAS COM QLC (QUERY LIST COMPREHENSIONS)

O QLC (Query List Comprehensions) é o mecanismo de consulta do Mnesia, permitindo buscas complexas usando sintaxe similar a list comprehensions.

#### 5.1 QLC Básico

```elixir
defmodule QlcQueries do
  # Busca simples por condição
  def buscar_por_idade(idade_minima) do
    :mnesia.transaction(fn ->
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:sort([{:idade, idade_minima}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Busca com múltiplas condições
  def buscar_por_idade_e_nome(idade_min, nome_contem) do
    :mnesia.transaction(fn ->
      # Define a query usando list comprehension
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:sort([{:cond, fn {_, _, nome, _, idade} ->
          idade >= idade_min and String.contains?(nome, nome_contem)
        end}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Projeção: retornar apenas alguns campos
  def buscar_nomes_e_emails do
    :mnesia.transaction(fn ->
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:sort([{:projection, fn {_, _, nome, email, _} -> {nome, email} end}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Busca com ordenação
  def buscar_ordenado_por_idade do
    :mnesia.transaction(fn ->
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:sort([{:order, fn {_, _, _, _, idade} -> idade end}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Paginação com QLC
  def buscar_com_paginacao(offset, limit) do
    :mnesia.transaction(fn ->
      query = :qlc.q([:mnesia.table(:usuario)])
      
      query
      |> :qlc.cursor()
      |> :qlc.next_answers(limit, offset)
    end)
  end
end
```

#### 5.2 Queries Complexas com Joins

O QLC suporta joins entre tabelas, semelhante a SQL, mas usando tuplas e pattern matching.

```elixir
defmodule ComplexQueries do
  # Join entre tabela de usuários e pedidos
  def buscar_pedidos_do_usuario(usuario_id) do
    :mnesia.transaction(fn ->
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:iterator([:mnesia.table(:pedido)]),
        :qlc:sort([{:cond, fn usuario, pedido ->
          elem(usuario, 2) == usuario_id and elem(pedido, 3) == usuario_id
        end}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Join com projeção
  def buscar_detalhes_pedidos do
    :mnesia.transaction(fn ->
      query = :qlc.q([
        :qlc:iterator([:mnesia.table(:usuario)]),
        :qlc:iterator([:mnesia.table(:pedido)]),
        :qlc:sort([{:cond, fn {_, uid, nome, _, _}, {_, pid, uid, total, _} ->
          true  # Join natural
        end},
        {:projection, fn usuario, pedido ->
          {elem(usuario, 3), elem(pedido, 2), elem(pedido, 4)}
        end}])
      ])
      :qlc.e(query)
    end)
  end
  
  # Left join (manualmente)
  def buscar_usuarios_com_pedidos do
    :mnesia.transaction(fn ->
      usuarios = :mnesia.foldl(fn usuario, acc -> [usuario | acc] end, [], :usuario)
      
      Enum.map(usuarios, fn usuario ->
        pedidos = :qlc.e(
          :qlc.q([:mnesia.table(:pedido), 
                  :qlc:sort([{:cond, fn pedido -> elem(pedido, 3) == elem(usuario, 2) end}])])
        )
        {usuario, pedidos}
      end)
    end)
  end
end
```

***

### 6. TRANSAÇÕES NO MNESIA

#### 6.1 Transações ACID Básicas

As transações no Mnesia fornecem isolamento, atomicidade, consistência e durabilidade, com locking otimista por padrão.

```elixir
defmodule Transactions do
  # Transação simples
  def transferir_saldo(from_id, to_id, valor) do
    :mnesia.transaction(fn ->
      # Lê as contas
      [from] = :mnesia.read({:conta, from_id})
      [to] = :mnesia.read({:conta, to_id})
      
      # Verifica saldo suficiente
      saldo_from = elem(from, 3)
      if saldo_from >= valor do
        # Atualiza contas
        from_atualizada = put_elem(from, 3, saldo_from - valor)
        to_atualizada = put_elem(to, 3, elem(to, 3) + valor)
        
        :mnesia.write(from_atualizada)
        :mnesia.write(to_atualizada)
        
        {:ok, :transferencia_realizada}
      else
        # Aborta a transação (rollback automático)
        :mnesia.abort(:saldo_insuficiente)
      end
    end)
  end
  
  # Transação com múltiplas operações
  def processar_pedido(usuario_id, itens) do
    :mnesia.transaction(fn ->
      # 1. Verifica usuário
      [usuario] = :mnesia.read({:usuario, usuario_id})
      
      # 2. Verifica estoque dos itens
      Enum.each(itens, fn {produto_id, quantidade} ->
        [produto] = :mnesia.read({:produto, produto_id})
        estoque = elem(produto, 3)
        
        if estoque < quantidade do
          :mnesia.abort({:estoque_insuficiente, produto_id})
        end
      end)
      
      # 3. Atualiza estoque e cria pedido
      Enum.each(itens, fn {produto_id, quantidade} ->
        [produto] = :mnesia.read({:produto, produto_id})
        produto_atualizado = put_elem(produto, 3, elem(produto, 3) - quantidade)
        :mnesia.write(produto_atualizado)
      end)
      
      # 4. Cria registro do pedido
      pedido_id = :erlang.unique_integer([:positive])
      pedido = {:pedido, pedido_id, usuario_id, itens, :os.system_time(:second)}
      :mnesia.write(pedido)
      
      {:ok, pedido_id}
    end)
  end
  
  # Sleeping transactions (wait para locks)
  def with_timeout do
    :mnesia.transaction(
      fn -> 
        # Operações que podem esperar por locks
        :mnesia.read({:usuario, 1})
      end,
      [{:timeout, 5000}]  # Timeout de 5 segundos
    )
  end
end
```

#### 6.2 Locking Estratégias

O Mnesia permite diferentes níveis de locking para controlar isolamento e concorrência.

```elixir
defmodule LockingStrategies do
  # Locking otimista (padrão) - melhor para baixa contenção
  def optimistic_lock_example do
    :mnesia.transaction(fn ->
      # Mnesia assume que não há conflitos
      usuario = :mnesia.read({:usuario, 1})
      :timer.sleep(100)  # Simula processamento
      :mnesia.write(usuario)
    end)
  end
  
  # Locking pessimista - força lock de escrita
  def pessimistic_lock_example do
    :mnesia.transaction(fn ->
      # Lock de escrita imediato
      :mnesia.write_lock_table(:usuario)
      
      usuario = :mnesia.read({:usuario, 1})
      :timer.sleep(100)
      :mnesia.write(usuario)
    end)
  end
  
  # Lock de tabela inteira (cuidado!)
  def table_lock_example do
    :mnesia.transaction(fn ->
      # Bloqueia tabela inteira para escrita
      :mnesia.write_lock_table(:usuario)
      
      # Operações seguras mas bloqueantes
      Enum.each(1..1000, fn id ->
        :mnesia.delete({:usuario, id})
      end)
    end)
  end
  
  # Lock de chave específica
  def key_lock_example do
    :mnesia.transaction(fn ->
      # Lock apenas no registro específico
      # Outros registros permanecem acessíveis
      :mnesia.read({:usuario, 1})
      :mnesia.write({:usuario, 1, "Novo Nome", "email@x.com", 30})
    end)
  end
end
```

***

### 7. TRANSAÇÕES SUJAS (DIRTY OPERATIONS)

Operações "sujas" bypassam a camada de transação, oferecendo máxima performance mas sem isolamento ACID.

```elixir
defmodule DirtyOperations do
  # Dirty write - não usa transação
  def dirty_create_usuario(id, nome, email, idade) do
    # Sem locking, sem rollback, sem isolamento!
    :mnesia.dirty_write({:usuario, id, nome, email, idade})
    :ok
  end
  
  # Dirty read - pode ler dados inconsistentes
  def dirty_read_usuario(id) do
    case :mnesia.dirty_read({:usuario, id}) do
      [] -> nil
      [usuario] -> usuario
    end
  end
  
  # Dirty delete
  def dirty_delete_usuario(id) do
    :mnesia.dirty_delete({:usuario, id})
    :ok
  end
  
  # Dirty update (sem atomicidade!)
  def dirty_increment_counter(id) do
    # NÃO FAÇA ISSO! Race condition!
    # Dois processos podem ler o mesmo valor
    [counter] = :mnesia.dirty_read({:contador, id})
    new_value = elem(counter, 2) + 1
    :mnesia.dirty_write(put_elem(counter, 2, new_value))
  end
  
  # Solução: Use update_counter para operações atômicas
  def atomic_increment_counter(id) do
    # Incrementa atomicamente sem transação
    :mnesia.dirty_update_counter({:contador, id}, 2, 1)
  end
  
  # Casos de uso para dirty operations:
  # ✅ Contadores de alta performance
  # ✅ Cache de dados não críticos
  # ✅ Logs/sensor data (pode perder alguns registros)
  # ❌ Dados que precisam consistência
  # ❌ Operações com múltiplos registros relacionados
  # ❌ Quando rollback é necessário
  
  # Exemplo: Contador de acessos (dados não críticos)
  def increment_acessos(pagina) do
    :mnesia.dirty_update_counter({:pagina_contador, pagina}, 2, 1)
  end
end
```

***

### 8. ÍNDICES E OTIMIZAÇÃO

#### 8.1 Criando Índices Secundários

Índices aceleram consultas por campos não-chave.

```elixir
defmodule Indexes do
  # Criar tabela com múltiplos índices
  def create_table_with_indexes do
    # Primeiro cria a tabela
    :mnesia.create_table(:funcionario, [
      {:attributes, [:id, :nome, :departamento, :salario, :email]},
      {:type, :set},
      {:ram_copies, [node()]},
      {:index, [:departamento, :email]}  # Índices secundários
    ])
  end
  
  # Adicionar índice depois da tabela criada
  def add_index after_create do
    :ok = :mnesia.add_table_index(:funcionario, :departamento)
    :ok = :mnesia.add_table_index(:funcionario, :salario)
  end
  
  # Remover índice
  def remove_index do
    :ok = :mnesia.del_table_index(:funcionario, :departamento)
  end
  
  # Usar índice para busca rápida
  def buscar_por_departamento(dept) do
    :mnesia.transaction(fn ->
      # Usa o índice para busca O(1)
      :mnesia.index_read(:funcionario, dept, :departamento)
    end)
  end
  
  # Busca por índice com condição adicional
  def buscar_salario_por_departamento(dept, salario_minimo) do
    :mnesia.transaction(fn ->
      funcionarios = :mnesia.index_read(:funcionario, dept, :departamento)
      
      Enum.filter(funcionarios, fn {_, _, _, salario, _} ->
        salario >= salario_minimo
      end)
    end)
  end
end
```

#### 8.2 Otimizações de Performance

```elixir
defmodule PerformanceOptimizations do
  # Usar select em vez de foldl para melhor performance
  def otimized_select do
    :mnesia.transaction(fn ->
      # Select permite projeção e condições
      :mnesia.select(:usuario, [
        {# Pattern match
         {:usuario, :"$1", :"$2", :"$3", :"$4"},
         [# Guard conditions
          [{:>=, :"$4", 18}],
          [:"$_"]  # Return value
         }
      ])
    end)
  end
  
  # Usar ets:select para tabelas ram_copies
  def ets_select_optimization do
    :mnesia.transaction(fn ->
      # Para tabelas ram_copies, use ets diretamente
      :ets.select(:usuario, [
        {{:usuario, :"$1", :"$2", :"$", :"$4"},
         [{:>=, :"$4", 18}],
         [:"$_"]}
      ])
    end)
  end
  
  # Batch operations
  def batch_write(registros) do
    :mnesia.transaction(fn ->
      # Escreve em lote dentro da mesma transação
      Enum.each(registros, fn registro ->
        :mnesia.write(registro)
      end)
    end)
  end
  
  # Usar dirty operations para counters (como visto)
  def optimized_counter do
    # 10x mais rápido que transação
    :mnesia.dirty_update_counter({:contador, :page_views}, 2, 1)
  end
end
```

***

### 9. DISTRIBUIÇÃO E REPLICAÇÃO

#### 9.1 Configuração de Cluster

```elixir
defmodule DistributedMnesia do
  @moduledoc """
  Configuração de Mnesia distribuído entre múltiplos nós.
  """
  
  # Iniciar cluster com 3 nós
  def setup_cluster do
    nodes = [:"app@host1", :"app@host2", :"app@host3"]
    
    # Conecta os nós
    Enum.each(nodes, &Node.connect/1)
    
    # Cria schema distribuído
    :mnesia.create_schema(nodes)
    
    # Inicia Mnesia em todos os nós
    Enum.each(nodes, fn node ->
      :rpc.call(node, :mnesia, :start, [])
    end)
    
    # Aguarda sincronização
    :mnesia.wait_for_tables([:usuario], 5000)
  end
  
  # Tabela replicada em todos os nós
  def create_replicated_table do
    all_nodes = [node() | Node.list()]
    
    :mnesia.create_table(:usuario_global, [
      {:attributes, [:id, :nome, :email]},
      {:type, :set},
      {:ram_copies, all_nodes},  # Réplica em todos os nós
      {:load_order, 999}  # Carrega primeiro (dados críticos)
    ])
  end
  
  # Tabela fragmentada (sharding)
  def create_fragmented_table do
    # Define fragmentação por hash do ID
    :mnesia.create_table(:usuario_fragmentado, [
      {:attributes, [:id, :nome, :email]},
      {:type, :set},
      {:ram_copies, [node()]},
      {:frag_properties, [
        {:n_fragments, 4},  # 4 fragmentos
        {:node_pool, [node() | Node.list()]},
        {:hash_module, :mnesia_frag_hash}
      ]}
    ])
  end
end
```

#### 9.2 Estratégias de Replicação

```yaml
ESTRATÉGIAS DE REPLICAÇÃO NO MNESIA:

  🔄 REPLICAÇÃO TOTAL:
    - Todos os nós têm cópia completa
    - Leitura: extremamente rápida (local)
    - Escrita: broadcast para todos os nós (custo)
    - Uso: dados de configuração, lookup tables

  📊 REPLICAÇÃO PARCIAL:
    - Apenas alguns nós têm cópia
    - Leitura: pode precisar de acesso remoto
    - Escrita: apenas nós específicos
    - Uso: dados regionais, sharding manual

  🧩 FRAGMENTAÇÃO (SHARDING):
    - Dados distribuídos entre nós
    - Cada fragmento pode ter réplicas
    - Balanceamento automático
    - Uso: grandes volumes de dados
```

```elixir
defmodule ReplicationStrategies do
  # Tabela com replicação em 2 nós (tolerância)
  def create_duplicated_table do
    [node1, node2 | _] = [node() | Node.list()]
    
    :mnesia.create_table(:produto, [
      {:attributes, [:id, :nome, :preco, :estoque]},
      {:disc_copies, [node1, node2]},  # Duas cópias em disco
      {:type, :set}
    ])
  end
  
  # Tabela com replicação assíncrona (eventual consistency)
  def create_async_replicated_table do
    :mnesia.create_table(:log_acessos, [
      {:attributes, [:timestamp, :user_id, :page]},
      {:ram_copies, Node.list()},
      {:type, :bag},
      {:storage_properties, [
        {:ets, [write_concurrency: true]}  # Escrita concorrente
      ]}
    ])
  end
  
  # Adicionar nó ao cluster existente
  def add_node_to_cluster(new_node) do
    # Conecta ao nó existente
    Node.connect(new_node)
    
    # Adiciona ao schema
    :mnesia.add_table_copy(:usuario, new_node, :ram_copies)
    :mnesia.add_table_copy(:produto, new_node, :disc_copies)
    
    # Aguarda replicação
    :mnesia.wait_for_tables([:usuario, :produto], 10000)
  end
  
  # Remover nó do cluster
  def remove_node_from_cluster(node_to_remove) do
    # Remove cópias das tabelas
    :mnesia.del_table_copy(:usuario, node_to_remove)
    :mnesia.del_table_copy(:produto, node_to_remove)
    
    # Remove do schema
    :mnesia.del_table_copy(:schema, node_to_remove)
  end
end
```

***

### 10. TOLERÂNCIA A FALHAS E RECUPERAÇÃO

#### 10.1 Backup e Restore

```elixir
defmodule BackupRecovery do
  # Backup completo do Mnesia
  def backup_database(backup_dir) do
    # Define nome do arquivo com timestamp
    timestamp = :calendar.local_time() |> :calendar.datetime_to_gregorian_seconds()
    backup_file = "#{backup_dir}/mnesia_backup_#{timestamp}"
    
    # Executa backup
    case :mnesia.backup(backup_file) do
      :ok -> {:ok, backup_file}
      {:error, reason} -> {:error, reason}
    end
  end
  
  # Restore de backup
  def restore_database(backup_file) do
    # Para o Mnesia (opcional)
    :mnesia.stop()
    
    # Restaura do backup
    case :mnesia.restore(backup_file, []) do
      {:ok, tables_restored} -> {:ok, tables_restored}
      {:error, reason} -> {:error, reason}
    end
    
    # Reinicia Mnesia
    :mnesia.start()
  end
  
  # Backup apenas de tabela específica
  def backup_table(table_name, backup_file) do
    :mnesia.backup(table_name, backup_file, [])
  end
  
  # Restore de tabela específica
  def restore_table(table_name, backup_file) do
    :mnesia.restore(backup_file, [{:skip_tables, [table_name]}])
  end
end
```

#### 10.2 Recuperação Automática

```elixir
defmodule AutomaticRecovery do
  # Configurar Mnesia para recuperação automática
  def configure_auto_recovery do
    # Ativa recuperação automática em caso de corrupção
    :application.set_env(:mnesia, :auto_repair, true)
    
    # Define comportamento em caso de divergência
    :application.set_env(:mnesia, :dc_replicas, :ignore)
    
    # Tempo de espera para sync (segundos)
    :application.set_env(:mnesia, :dump_log_timeout, 30000)
    
    # Tamanho máximo do log (bytes)
    :application.set_env(:mnesia, :dump_log_write_threshold, 100000)
  end
  
  # Monitoramento de falhas
  def monitor_failures do
    # Registra evento de falha de tabela
    :mnesia.subscribe(:system, :table_loaded)
    
    receive do
      {:mnesia_system_event, {:table_loaded, table, node}} ->
        IO.puts("Tabela #{table} carregada em #{node}")
      {:mnesia_system_event, {:table_dumped, table, node}} ->
        IO.puts("Tabela #{table} dumpada em #{node}")
      {:mnesia_system_event, {:error, reason}} ->
        IO.puts("Erro no Mnesia: #{reason}")
    end
  end
  
  # Verificar integridade das tabelas
  def verify_table_integrity(table_name) do
    case :mnesia.verify_table(table_name, []) do
      {:ok, record_count} ->
        IO.puts("Tabela #{table_name} íntegra, #{record_count} registros")
        :ok
      {:error, reason} ->
        IO.puts("Tabela #{table_name} corrompida: #{reason}")
        :error
    end
  end
  
  # Reparar tabela corrompida
  def repair_table(table_name) do
    case :mnesia.verify_table(table_name, []) do
      {:error, _reason} ->
        # Tenta reparar a tabela
        :mnesia.force_load_table(table_name, [])
        IO.puts("Tentativa de repair na tabela #{table_name}")
      _ -> :ok
    end
  end
end
```

***

### 11. BOAS PRÁTICAS E PERFORMANCE

#### 11.1 Guia de Otimização

```yaml
BOAS PRÁTICAS DO MNESIA:

  📊 ESTRUTURA DE DADOS:
    ✅ Use tuplas pequenas e planas
    ✅ Evite estruturas aninhadas muito profundas
    ✅ Coloque campos mais acessados no início da tupla
    ✅ Use átomos para campos que se repetem

  🔍 ÍNDICES:
    ✅ Crie índices para campos frequentemente filtrados
    ✅ Limite o número de índices (custo de escrita)
    ✅ Use índices apenas quando necessário

  🏃 TRANSAÇÕES:
    ✅ Mantenha transações curtas e rápidas
    ✅ Evite I/O dentro de transações
    ✅ Use dirty operations para contadores e logs
    ✅ Monitore deadlocks (use logs)

  🌐 DISTRIBUIÇÃO:
    ✅ Mantenha dados relacionados no mesmo nó
    ✅ Use replicação para dados críticos
    ✅ Teste falhas de rede e de nó
    ✅ Monitore latência entre nós

  💾 PERFORMANCE:
    ✅ Use select/qlc em vez de foldl para consultas
    ✅ Prefira ram_copies para alta performance
    ✅ Use disc_only_copies para dados grandes
    ✅ Balanceie fragmentos uniformemente
```

#### 11.2 Exemplos de Código Otimizado

```elixir
defmodule BestPractices do
  # ✅ BOA: Tupla plana com posições importantes
  def good_user_struct do
    # Posição 2: ID (chave), 3: Nome (acessado), 4: Email
    {:usuario, 123, "João", "joao@x.com", 30, "ativo", ~N[2024-01-01]}
  end
  
  # ❌ RUIM: Deep nesting (custo de acesso)
  def bad_user_struct do
    {:usuario, 123, %{nome: "João", contato: %{email: "..."}}}
  end
  
  # ✅ BOA: Transação curta e específica
  def good_transaction do
    :mnesia.transaction(fn ->
      # Apenas operações Mnesia aqui
      usuario = :mnesia.read({:usuario, 123})
      :mnesia.write(update_usuario(usuario))
    end)
  end
  
  # ❌ RUIM: Transação longa com I/O pesado
  def bad_transaction do
    :mnesia.transaction(fn ->
      usuario = :mnesia.read({:usuario, 123})
      
      # I/O pesado dentro da transação!
      arquivo = File.read!("dados.txt")
      {:ok, api_response} = HTTPoison.get("https://api.com/dados")
      
      :mnesia.write(update_usuario(usuario, api_response))
    end)
  end
  
  # ✅ BOA: Uso de select em vez de foldl
  def good_query do
    :mnesia.transaction(fn ->
      # Select é otimizado internamente
      :mnesia.select(:usuario, [
        {{:usuario, :"$1", :"$2", :"$3", :"$4", :"$5", :"$6"},
         [{:>=, :"$5", 18}],
         [:"$_"]}
      ])
    end)
  end
  
  # ❌ RUIM: foldl para consultas (mais lento)
  def bad_query do
    :mnesia.transaction(fn ->
      :mnesia.foldl(fn usuario, acc ->
        if elem(usuario, 5) >= 18 do
          [usuario | acc]
        else
          acc
        end
      end, [], :usuario)
    end)
  end
end
```

***

### 12. RESUMO TÉCNICO

```yaml
RESUMO DO MNESIA:

  🎯 QUANDO USAR:
    ✅ Banco de dados distribuído embutido
    ✅ Dados que precisam de transações ACID
    ✅ Configuração de sistemas de alta disponibilidade
    ✅ Cache com replicação entre nós
    ✅ Dados de baixa latência (tempo real)
    ✅ IoT/Embedded systems (Nerves)

  ❌ QUANDO NÃO USAR:
    ❌ Dados muito grandes (>100GB)
    ❌ Queries analíticas complexas (use SQL)
    ❌ Quando você precisa de drivers/suporte da comunidade
    ❌ Integração com sistemas não-Erlang

  🔄 ALTERNATIVAS NO ECOSSISTEMA ELIXIR:
    • Ecto + PostgreSQL: relacional completo
    • Redis: cache de alta performance
    • GenStage/Flow: processamento de streams
    • DETS: apenas tabelas em disco (sem distribuição)
```

***

### 13. REFERÊNCIA RÁPIDA

#### 13.1 Comandos Essenciais

| Operação          | Comando                     |
| ----------------- | --------------------------- |
| Criar tabela      | `:mnesia.create_table/2`    |
| Inserir/Atualizar | `:mnesia.write/1`           |
| Ler por chave     | `:mnesia.read/1`            |
| Deletar           | `:mnesia.delete/1`          |
| Transação         | `:mnesia.transaction/1`     |
| Dirty write       | `:mnesia.dirty_write/1`     |
| Query QLC         | `:qlc.q/1` + `:qlc.e/1`     |
| Índice            | `:mnesia.add_table_index/2` |
| Backup            | `:mnesia.backup/1`          |

#### 13.2 Configurações Comuns

```elixir
# Configuração básica no config/config.exs
config :mnesia,
  dir: './mnesia_data',
  dump_log_timeout: 30000,
  dump_log_write_threshold: 100000

# Inicialização
:application.start(:mnesia)

# Verificar status
:mnesia.system_info(:is_running)
:mnesia.table_info(:usuario, :size)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/conceitos/programacao-e-linguagens/elixir/8.-mnesia-dbms.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
