Schéma temporaire par connexion?

8

J'essaie de migrer mes tests unitaires de H2 vers Postgresql.

Actuellement, H2 me donne un schéma en mémoire tel que chaque connexion mappe à un schéma unique, crée les tables, exécute le test et supprime le schéma. La création et la destruction du schéma sont gérées automatiquement par H2.

Les tests unitaires s'exécutent simultanément.

Quelle est la meilleure façon de procéder à Postgresql? Plus précisément,

  1. Comment obtenir un schéma unique par connexion?
    • Le cadre de test doit-il générer des noms uniques ou existe-t-il un mécanisme intégré pour ce faire?
  2. Comment puis-je m'assurer que le schéma est supprimé lorsque la connexion est interrompue?
    • Je ne veux pas me retrouver avec des schémas pendants lorsque les tests unitaires sont tués.
  3. Quelle approche donnera les performances les plus élevées?
    • J'ai besoin de créer / supprimer des dizaines de schémas par seconde.

MISE À JOUR : J'ai trouvé une réponse connexe ici, mais elle ne parvient pas à supprimer les schémas au cas où le processus exécutant les tests unitaires serait tué.

Gili
la source

Réponses:

13

pg_temp est un alias pour le schéma temporaire de la session en cours.

Si vous faites un SET search_path TO pg_tempavant d'exécuter vos tests, tout devrait fonctionner (tant que rien ne fait explicitement référence à un schéma).

Si vous ne voulez pas du tout changer votre script, définissez le search_pathsur l'utilisateur auquel les tests se connectent en tant que:

> ALTER ROLE testuser SET search_path = pg_temp;

Ensuite, tout ce que l'utilisateur crée sera dans pg_temp, sauf indication contraire explicite.

Voici un exemple de psql, montrant le schéma réel (pour cette connexion) que l'alias résout:

> 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)

Et, comme vous vous en doutez, ce schéma est différent pour chaque connexion simultanée et disparaît après la fermeture de la connexion.

Notez que cela fonctionne également pour les fonctions, mais vous devrez référencer explicitement le schéma pg_temp lors de leur appel.

hbn
la source
Mais pg_tempun seul schéma est-il vrai? Ainsi, lorsque j'exécute des tests unitaires simultanés, ne s'encombrent-ils pas les tables / données les uns des autres?
Gili
1
Non. C'est un alias pour le schéma temporaire de la session en cours. Je mettrai à jour la réponse avec un exemple.
hbn
Gardez à l'esprit que si vous venez de fermer et d'ouvrir une connexion, vous pourriez bien vous retrouver avec le même schéma temporaire, mais il aura été vidé. Ouvrez 2 simultanément pour voir les différentes affectations. Vous ne pouvez pas voir le schéma temporaire d'une autre session à moins que vous ne soyez un superutilisateur.
hbn
Bien sûr, j'ai vu un commentaire de votre part vous demandant quand régler cela. Quoi qu'il en soit - il est défini par session si vous faites juste un SET search_path; utiliser SET LOCAL search_pathpour définir par sous-transaction, ou si vous le souhaitez, vous pouvez définir au niveau de l'utilisateur avec ALTER USER mytestuser SET search_path = 'pg_temp', ou au niveau de la base de données avecALTER DATABASE mytestdb SET search_path = 'pg_temp'
hbn
Par curiosité, existe-t-il un moyen de faire fonctionner cela pour des fonctions sans référence de schéma explicite? Ou est-ce impossible pour le pg_tempschéma?
Gili
3

Vous pouvez obtenir le nom du schéma temporaire actuel (après avoir créé la première table temporaire) comme indiqué dans le lien que vous avez ajouté:

SELECT nspname
FROM   pg_namespace
WHERE  oid = pg_my_temp_schema();

Mais votre plan actuel n'aurait toujours pas beaucoup de sens. Pour créer des tables dans le schéma temporaire actuel, créez simplement des tables temporaires. C'est tout. Par défaut, le search_pathest défini de sorte que les tables temporaires soient visibles en premier. Il n'est jamais nécessaire de qualifier le schéma des tables temporaires. Vous ne devriez jamais avoir à adresser directement le schéma temporaire actuel de quelque manière que ce soit - c'est un détail d'implémentation.

Erwin Brandstetter
la source
D'accord, c'est un hack, mais il peut être beaucoup plus simple que de paramétrer du code de création pour permettre la création de tables temporaires.
hbn
Bon point, sauf que @hbn l'a mentionné, je veux que les tests unitaires et le code de production exécutent le même script SQL. Le premier doit fonctionner contre un schéma temporaire tandis que le second ne doit pas.
Gili
@hbn, par curiosité à quoi ressemblerait le code de création paramétré? J'utilise flywaydb.org et il exécute simplement des fichiers SQL simples (pas de variables). Je ne veux probablement pas emprunter cette voie. Je suis juste curieux de savoir ce que cela implique.
Gili
Je n'ai jamais utilisé flywaydb. À un niveau très basique, vous pouvez utiliser un langage de modélisation de texte (par exemple Jinja2 en Python) pour prétraiter vos scripts de création, en ajoutant éventuellement un "temporaire" lorsque vous créez une table. Si vous créez explicitement des fonctions, le hack get-the-temporaire-schema est probablement inévitable car (pour autant que je sache), vous ne pouvez pas créer directement une fonction temporaire.
hbn
@hbn,: If you're explicitly sequences ...Je pense que votre dernier commentaire contenait une faute de frappe. Que vouliez-vous dire entre explicitlyet sequences?
Gili
1

Vos tests impliquent-ils des transactions? DDL est transactionnel dans PostgreSQL, donc si vous créez votre schéma et vos tables, puis exécutez vos tests, le tout dans une seule transaction qui est ensuite annulée, le schéma n'est jamais réellement validé et visible pour les autres sessions.

Vous devez toujours utiliser un nom probablement unique pour votre schéma (peut-être inclure le nom d'hôte et le PID), car CREATE SCHEMAil échouera immédiatement si un schéma portant le même nom existe déjà et se bloquera si une autre session a créé un schéma portant le même nom dans une transaction non validée.

Une alternative serait peut-être simplement d'utiliser des tables temporaires, si vous êtes en mesure de modifier vos scripts de création de base de données pour ce faire.

hbn
la source
Belle astuce, mais cela ne fonctionnerait pas dans mon cas, car un seul test fonctionne sur plusieurs transactions. Chaque méthode de test est un client Web qui déclenche plusieurs transactions côté serveur. Par exemple, il crée, interroge et supprime un utilisateur. Chaque appel est une demande HTTP distincte et s'exécute dans sa propre transaction.
Gili
Assez juste, mon approche était très limitée.
hbn
@Gili: Notez que cette technique de ne jamais s'engager CREATE SCHEMAest la seule qui puisse garantir leur disparition lorsque le test unitaire sera tué.
Daniel Vérité du
0

Je viens de me faire une idée.

Postgresql garantit qu'une session ne peut pas voir les tables temporaires d'une autre. Je suppose que cela signifie que lorsque vous créez une table temporaire, cela crée un schéma temporaire. Je pourrais peut-être faire ce qui suit:

  1. Créez une table temporaire (factice) et recherchez son schéma.
  2. Utilisez ce schéma pour le test (créez les tables, exécutez le test).
  3. Lorsque la connexion est fermée, Postgresql supprimera le schéma.

Je n'aime pas me fier aux détails de l'implémentation, mais dans ce cas, cela semble assez sûr.

Gili
la source