web-dev-qa-db-fra.com

Exécuter PostgreSQL en mémoire uniquement

Je souhaite exécuter une petite base de données PostgreSQL qui s'exécute uniquement en mémoire, pour chaque test d'unité que j'écris. Par exemple:

@Before
void setUp() {
    String port = runPostgresOnRandomPort();
    connectTo("postgres://localhost:"+port+"/in_memory_db");
    // ...
}

Idéalement, un seul exécutable postgres doit être vérifié dans le contrôle de version, que le test unitaire utilisera.

Quelque chose comme HSQL, mais pour postgres. Comment puis je faire ça?

Où puis-je obtenir une telle version de Postgres? Comment puis-je lui demander de ne pas utiliser le disque?

84
Chi-Lan

Ce n'est pas possible avec Postgres. Il ne propose pas de moteur in-process/in-memory comme HSQLDB ou MySQL.

Si vous voulez créer un environnement autonome, vous pouvez placez les fichiers binaires Postgres dans SVN (mais cela ne se limite pas à un seul exécutable).

Vous devrez exécuter initdb pour configurer votre base de données de test avant de pouvoir faire quoi que ce soit avec cela. Cela peut être fait à partir d'un fichier de commandes ou en utilisant Runtime.exec (). Mais notez que initdb n’est pas quelque chose de rapide. Vous ne voudrez certainement pas exécuter cela pour chaque test. Cependant, vous pourriez vous en tirer avant votre suite de tests.

Cependant, bien que cela puisse être fait, je vous conseillerais d’avoir une installation Postgres dédiée dans laquelle vous devrez simplement recréer votre base de données de tests avant d’exécuter vos tests.

Vous pouvez recréer la base de données de test en utilisant une base de données de modèles, ce qui la rend assez rapide (un lot plus rapide que l'exécution de initdb pour chaque exécution de test)

38

(Déplacement de ma réponse de tilisation de PostgreSQL en mémoire et généralisation):

Vous ne pouvez pas exécuter Pg in-process, in-memory

Je n'arrive pas à comprendre comment exécuter une base de données Postgres en mémoire à des fins de test. C'est possible?

Non ce n'est pas possible. PostgreSQL est implémenté en C et compilé en code plate-forme. Contrairement à H2 ou à Derby, vous ne pouvez pas simplement charger jar et le lancer en tant que base de données en mémoire jetable.

Contrairement à SQLite, qui est également écrit en C et compilé pour le code de la plate-forme, PostgreSQL ne peut pas non plus être chargé en cours de traitement. Il nécessite plusieurs processus (un par connexion) car il s’agit d’une architecture multitraitement, et non multithreading. L'exigence de multitraitement signifie que vous devez lancer le postmaster en tant que processus autonome.

Au lieu de cela: préconfigurer une connexion

Je suggère simplement d’écrire vos tests de façon à ce qu’un nom d’hôte/nom d’utilisateur/mot de passe particulier fonctionne, et que le test harness CREATE DATABASE une base de données jetable, puis DROP DATABASE à la fin de la course. Obtenez les informations de connexion à la base de données à partir d'un fichier de propriétés, de propriétés de cible, de variable d'environnement, etc.

Vous pouvez utiliser une instance PostgreSQL existante dans laquelle vous avez déjà une base de données qui vous tient à cœur, à condition que l'utilisateur que vous fournissez à vos tests unitaires soit et non un superutilisateur. , seulement un utilisateur avec CREATEDB droits. Au pire, vous créez des problèmes de performances dans les autres bases de données. Pour cette raison, je préfère exécuter une installation PostgreSQL complètement isolée.

Au lieu de cela: Lancez une instance PostgreSQL jetable pour le tester

Alternativement, si vous êtes vif , vous pourriez demandez à votre harnais de test de localiser le initdb et le postgres binaires, exécutez initdb pour créer une base de données, modifiez pg_hba.conf à trust, exécutez postgres pour le démarrer sur un port aléatoire, créez un utilisateur, créez une base de données et exécutez les tests . Vous pouvez même regrouper les fichiers binaires PostgreSQL pour plusieurs architectures dans un fichier jar et décompresser ceux de l'architecture actuelle dans un répertoire temporaire avant d'exécuter les tests.

Personnellement, je pense que c'est une douleur majeure qui devrait être évitée; Il est beaucoup plus simple de configurer une base de test. Cependant, c'est devenu un peu plus facile avec l'avènement de include_dir support dans postgresql.conf; Maintenant, vous pouvez simplement ajouter une ligne, puis écrire un fichier de configuration généré pour tout le reste.

Tests plus rapides avec PostgreSQL

Pour plus d'informations sur la manière d'améliorer en toute sécurité les performances de PostgreSQL à des fins de test, reportez-vous à la réponse détaillée que j'ai écrite à ce sujet: Optimiser PostgreSQL pour des tests rapides

Le dialecte H2 de PostgreSQL n'est pas un vrai substitut

Certaines personnes utilisent plutôt la base de données H2 en mode dialecte PostgreSQL pour exécuter des tests. Je pense que c'est presque aussi mauvais que les Rails personnes utilisant SQLite pour les tests et PostgreSQL pour le déploiement en production.

H2 prend en charge certaines extensions PostgreSQL et émule le dialecte PostgreSQL. Cependant, c'est juste que - une émulation. Vous trouverez des zones où H2 accepte une requête, mais pas PostgreSQL, où le comportement est différent, etc. . Vous trouverez également de nombreux endroits où PostgreSQL ™ permet de faire quelque chose que H2 ne peut tout simplement pas - comme les fonctions de fenêtre, au moment de l'écriture.

Si vous comprenez les limites de cette approche et que votre accès à la base de données est simple, H2 pourrait fonctionner correctement. Mais dans ce cas, vous êtes probablement un meilleur candidat pour un ORM qui abstrait la base de données, car vous n'utilisez pas ses fonctionnalités intéressantes. Dans ce cas, vous n'avez plus à vous soucier de la compatibilité de la base de données.

Les tablespaces ne sont pas la solution!

Ne pas utilisez un tablespace pour créer une base de données "en mémoire". Non seulement c'est inutile car cela n'améliorera pas les performances de toute façon, mais c'est aussi un excellent moyen de perturber l'accès à tout autre élément qui pourrait vous intéresser dans la même installation PostgreSQL. La documentation 9.4 contient maintenant l'avertissement suivant :

[~ # ~] avertissement [~ # ~]

Même s'ils sont situés en dehors du répertoire de données principal PostgreSQL, les espaces de table font partie intégrante du cluster de bases de données et ne peuvent pas être traités comme une collection autonome de fichiers de données. Ils dépendent des métadonnées contenues dans le répertoire de données principal et ne peuvent donc pas être attachés à un autre cluster de base de données ni sauvegardés individuellement. De même, si vous perdez un espace de table (suppression de fichier, défaillance du disque, etc.), le cluster de base de données peut devenir illisible ou ne plus pouvoir démarrer. Placer un espace de table sur un système de fichiers temporaire, tel qu'un disque virtuel, met en péril la fiabilité de l'ensemble du cluster.

parce que j'ai remarqué que trop de gens le faisaient et avaient des problèmes.

(Si vous avez fait cela, vous pouvez mkdir le répertoire de l'espace de table manquant pour que PostgreSQL redémarre, puis DROP les bases de données, tables, etc. manquants. Il vaut mieux ne pas le faire.)

67
Craig Ringer

Ou vous pouvez créer un ESPACE DE TABLE dans un ramfs/tempfs et créer tous vos objets là-bas.
On m'a récemment signalé un article expliquant exactement comment faire cela sous Linux .

Attention

Cela peut mettre en péril l'intégrité de votre cluster de base de données complet.
Lisez l'avertissement ajouté dans le manuel.
Il ne s’agit donc que d’une option pour les données consomptibles.

Pour les tests unitaires , cela devrait fonctionner correctement. Si vous exécutez d'autres bases de données sur le même ordinateur, veillez à utiliser un cluster de bases de données séparé (qui possède son propre port) pour plus de sécurité.

66
Erwin Brandstetter

Il est désormais possible d'exécuter une instance en mémoire de PostgreSQL dans vos tests JUnit via le composant PostgreSQL incorporé à partir d'OpenTable: https://github.com/opentable/otj-pg-embedded .

En ajoutant la dépendance à la bibliothèque otj-pg-embedded ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ), vous pouvez démarrer et arrêter votre propre instance. de PostgreSQL dans vos hooks @Before et @Afer:

EmbeddedPostgres pg = EmbeddedPostgres.start();

Ils proposent même une règle JUnit pour que JUnit démarre et arrête automatiquement votre serveur de base de données PostgreSQL pour vous:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();
23
Rubms

Vous pouvez utiliser TestContainers pour créer un conteneur docker PosgreSQL pour les tests: http://testcontainers.viewdocs.io/testcontainers-Java/usage/database_containers/

Les TestContainers fournissent un JUnit @ Rule/@ ClassRule : ce mode démarre une base de données à l'intérieur d'un conteneur avant vos tests et le supprime par la suite.

Exemple:

public class SimplePostgreSQLTest {

    @Rule
    public PostgreSQLContainer postgres = new PostgreSQLContainer();

    @Test
    public void testSimple() throws SQLException {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(postgres.getJdbcUrl());
        hikariConfig.setUsername(postgres.getUsername());
        hikariConfig.setPassword(postgres.getPassword());

        HikariDataSource ds = new HikariDataSource(hikariConfig);
        Statement statement = ds.getConnection().createStatement();
        statement.execute("SELECT 1");
        ResultSet resultSet = statement.getResultSet();

        resultSet.next();
        int resultSetInt = resultSet.getInt(1);
        assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
    }
}
11
Andrejs

Il existe maintenant une version en mémoire de PostgreSQL de la société de recherche russe nommée Yandex: https://github.com/yandex-qatools/postgresql-embedded

Il est basé sur le processus d'intégration de Flapdoodle OSS.

Exemple d'utilisation (depuis la page github):

// starting Postgres
final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6);
// predefined data directory
// final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6, "/path/to/predefined/data/directory");
final String url = postgres.start("localhost", 5432, "dbName", "userName", "password");

// connecting to a running Postgres and feeding up the database
final Connection conn = DriverManager.getConnection(url);
conn.createStatement().execute("CREATE TABLE films (code char(5));");

Je l'utilise quelque temps. Ça marche bien.

[~ # ~] mis à jour [~ # ~] : ce projet n'est plus activement maintenu

Please be adviced that the main maintainer of this project has successfuly 
migrated to the use of Test Containers project. This is the best possible 
alternative nowadays.
4
akvyalkov

Vous pouvez également utiliser les paramètres de configuration PostgreSQL (tels que ceux détaillés dans la question et la réponse acceptée ici ) pour obtenir des performances sans avoir nécessairement recours à une base de données en mémoire.

2
Dan