Esquema temporário por conexão?

8

Estou tentando migrar meus testes de unidade do H2 para o Postgresql.

Atualmente, o H2 fornece um esquema na memória, de modo que cada conexão é mapeada para um esquema exclusivo, cria as tabelas, executa o teste e descarta o esquema. A criação e destruição do esquema são tratadas automaticamente pelo H2.

Os testes de unidade são executados simultaneamente.

Qual é a melhor maneira de fazer isso no Postgresql? Especificamente,

  1. Como obtenho um esquema exclusivo por conexão?
    • A estrutura de teste deve gerar nomes exclusivos ou existe um mecanismo interno para fazer isso?
  2. Como garantir que o esquema seja descartado quando a conexão for interrompida?
    • Eu não quero acabar com esquemas pendentes quando os testes de unidade são mortos.
  3. Qual abordagem produzirá o melhor desempenho?
    • Eu preciso criar / descartar dezenas de esquemas por segundo.

ATUALIZAÇÃO : Encontrei uma resposta relacionada aqui, mas ela não descarta os esquemas caso o processo que executa os testes de unidade seja interrompido.

Gili
fonte

Respostas:

13

pg_temp é um alias para o esquema temporário da sessão atual.

Se você fizer um SET search_path TO pg_tempantes de executar seus testes, tudo funcionará (desde que nada faça referência a um esquema explicitamente).

Se você não deseja alterar seu script, defina search_patho usuário no qual os testes efetuam login como:

> ALTER ROLE testuser SET search_path = pg_temp;

Então, tudo o que o usuário criar estará em pg_temp, a menos que seja explicitamente especificado.

Aqui está um exemplo de psql, mostrando o esquema real (para esta conexão) para o qual o alias resolve:

> SET search_path TO pg_temp;
SET
> create table test();
CREATE TABLE
> \dt test
          List of relations
  Schema   | Name | Type  |  Owner
-----------+------+-------+----------
 pg_temp_4 | test | table | postgres
(1 row)

E, como seria de esperar, esse esquema é diferente para todas as conexões simultâneas e desaparece após o fechamento da conexão.

Observe que isso também funciona para funções, embora você precise referenciar explicitamente o esquema pg_temp ao chamá-las.

hbn
fonte
Mas pg_tempé um esquema único, certo? Então, quando eu executo testes de unidade simultâneos, eles não prejudicam as tabelas / dados um do outro?
Gili
11
Não. É um alias para o esquema temporário da sessão atual. Vou atualizar a resposta com um exemplo.
hbn 16/09
Lembre-se de que, se você apenas fechar e abrir uma conexão, poderá acabar com o mesmo esquema temporário, mas ele será esvaziado. Abra 2 simultaneamente para ver os diferentes sendo alocados. Você não pode ver o esquema temporário de outra sessão, a menos que seja um superusuário.
hbn 16/09
Claro que vi um comentário seu perguntando sobre quando definir isso. Enfim - é definido por sessão, se você apenas fizer um SET search_path; use SET LOCAL search_pathpara definir por subtransação ou, se desejar, você pode definir no nível do usuário ALTER USER mytestuser SET search_path = 'pg_temp'ou no banco de dados comALTER DATABASE mytestdb SET search_path = 'pg_temp'
hbn
Por curiosidade, existe alguma maneira de fazer isso funcionar para funções sem uma referência explícita ao esquema? Ou isso é impossível para o pg_tempesquema?
Gili
3

Você pode obter o nome do esquema temporário atual (depois de criar a primeira tabela temporária), conforme descrito no link que você adicionou:

SELECT nspname
FROM   pg_namespace
WHERE  oid = pg_my_temp_schema();

Mas seu plano atual ainda não faria muito sentido. Para criar tabelas no esquema temporário atual, basta criar tabelas temporárias. Isso é tudo. Por padrão, o search_pathé definido para que as tabelas temporárias sejam visíveis primeiro. Um não precisa de tabelas temporárias de esquema-qualificar. Você nunca precisa abordar o esquema temporário atual diretamente de forma alguma - esse é um detalhe da implementação.

Erwin Brandstetter
fonte
Concordou que é um hack, mas pode ser significativamente mais direto do que parametrizar o código de criação para permitir a criação de tabelas temporárias.
hbn 14/09
Bom ponto, exceto como @hbn mencionado, quero que os testes de unidade e o código de produção executem o mesmo script SQL. O primeiro deve executar contra um esquema temporário, enquanto o segundo não.
Gili
@hbn, por curiosidade, como seria o código de criação com parâmetros? Estou usando o flywaydb.org e ele apenas executa arquivos SQL simples (sem variáveis). Provavelmente não quero seguir esse caminho. Só estou curioso sobre o que está envolvido.
Gili
Eu nunca usei o flywaydb. Em um nível muito básico, você pode usar alguma linguagem de modelagem de texto (por exemplo, Jinja2 em Python) para pré-processar seus scripts de criação, adicionando opcionalmente um "temporário" ao criar uma tabela. Se você estiver criando funções explicitamente, o hack do esquema obter-temporário-provavelmente é inevitável, pois (tanto quanto eu sei), você não poderá criar diretamente uma função temporária.
hbn 14/09
@ hbn If you're explicitly sequences ...: Acho que seu último comentário continha um erro de digitação. O que você quis dizer entre explicitlye sequences?
Gili
1

Seus testes envolvem transações? O DDL é transacional no PostgreSQL; portanto, se você criar seu esquema e tabelas, executar seus testes, tudo dentro de uma única transação que será revertida, o esquema nunca será realmente confirmado e visível para outras sessões.

Você ainda precisará usar um nome provavelmente exclusivo para seu esquema (talvez inclua nome do host e PID), pois CREATE SCHEMAfalhará imediatamente se já existir um esquema com nome idêntico e bloqueará se outra sessão tiver criado um esquema com nome idêntico em uma transação não confirmada.

Uma alternativa seria apenas usar tabelas temporárias, se você puder modificar os scripts de criação de banco de dados para fazer isso.

hbn
fonte
Bom truque, mas não funcionaria no meu caso, porque um único teste opera em várias transações. Cada método de teste é um cliente da Web que dispara várias transações do lado do servidor. Por exemplo, ele cria, consulta e exclui um usuário. Cada chamada é uma solicitação HTTP separada e é executada em sua própria transação.
Gili
Justo, minha abordagem foi muito limitada.
hbn 14/09
@ Gili: Observe que essa técnica de nunca se comprometer CREATE SCHEMAé a única que pode garantir que eles desapareçam quando o teste de unidade é morto.
Daniel Vérité
0

Acabei de ter uma ideia.

O Postgresql garante que uma sessão não pode ver as tabelas temporárias de outra. Suponho que isso significa que, quando você cria uma tabela temporária, ele cria um esquema temporário. Então, talvez eu pudesse fazer o seguinte:

  1. Crie uma tabela temporária (fictícia) e procure seu esquema.
  2. Use este esquema para o teste (crie as tabelas, execute o teste).
  3. Quando a conexão é fechada, o Postgresql descarta o esquema.

Não gosto de confiar nos detalhes da implementação, mas neste caso isso parece bastante seguro.

Gili
fonte