diff -pruN 1.3.16-1/Build.PL 1.3.18-1/Build.PL
--- 1.3.16-1/Build.PL	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/Build.PL	2025-09-23 10:45:12.000000000 +0000
@@ -14,7 +14,7 @@ Module::Build->new(
         'Redis::Fast'    => 0,
     },
     test_requires      => { DBI => 0, 'DBD::SQLite' => 0, },
-    dist_version       => '1.3.16',
+    dist_version       => '1.3.18',
     autosplit          => [qw(lib/Apache/Session/Browseable/_common.pm)],
     configure_requires => { 'Module::Build' => 0, },
     meta_merge         => {
diff -pruN 1.3.16-1/Changes 1.3.18-1/Changes
--- 1.3.16-1/Changes	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/Changes	2025-09-23 10:45:12.000000000 +0000
@@ -1,5 +1,11 @@
 Revision history for Perl extension Apache::Session::Browseable.
 
+1.3.18
+    - Add persistence option for Redis
+
+1.3.17
+    - Add Patroni support
+
 1.3.16
     - Optimize deleteIfLowerThan for Redis
 
diff -pruN 1.3.16-1/MANIFEST 1.3.18-1/MANIFEST
--- 1.3.16-1/MANIFEST	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/MANIFEST	2025-09-23 10:45:12.000000000 +0000
@@ -11,6 +11,7 @@ lib/Apache/Session/Browseable/LDAP.pm
 lib/Apache/Session/Browseable/MySQL.pm
 lib/Apache/Session/Browseable/MySQLJSON.pm
 lib/Apache/Session/Browseable/Oracle.pm
+lib/Apache/Session/Browseable/Patroni.pm
 lib/Apache/Session/Browseable/PgHstore.pm
 lib/Apache/Session/Browseable/PgJSON.pm
 lib/Apache/Session/Browseable/Postgres.pm
@@ -23,6 +24,7 @@ lib/Apache/Session/Browseable/Store/Info
 lib/Apache/Session/Browseable/Store/LDAP.pm
 lib/Apache/Session/Browseable/Store/MySQL.pm
 lib/Apache/Session/Browseable/Store/Oracle.pm
+lib/Apache/Session/Browseable/Store/Patroni.pm
 lib/Apache/Session/Browseable/Store/Postgres.pm
 lib/Apache/Session/Browseable/Store/Redis.pm
 lib/Apache/Session/Browseable/Store/SQLite.pm
@@ -36,12 +38,14 @@ MANIFEST
 META.json
 META.yml			Module meta-data (added by MakeMaker)
 README.md
+RELEASE.md
 rpm/Apache-Session-Browseable.spec
 t/Apache-Session-Browseable-Cassandra.t
 t/Apache-Session-Browseable-common.t
 t/Apache-Session-Browseable-DBI.t
 t/Apache-Session-Browseable-File.t
 t/Apache-Session-Browseable-LDAP.t
+t/Apache-Session-Browseable-Patroni.t
 t/Apache-Session-Browseable-Redis.t
 t/Apache-Session-Browseable-SQLite.t
 t/Apache-Session-Browseable-Store-DBI.t
diff -pruN 1.3.16-1/META.json 1.3.18-1/META.json
--- 1.3.16-1/META.json	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/META.json	2025-09-23 10:45:12.000000000 +0000
@@ -41,7 +41,7 @@
    "provides" : {
       "Apache::Session::Browseable" : {
          "file" : "lib/Apache/Session/Browseable.pm",
-         "version" : "v1.3.16"
+         "version" : "v1.3.18"
       },
       "Apache::Session::Browseable::Cassandra" : {
          "file" : "lib/Apache/Session/Browseable/Cassandra.pm",
@@ -75,6 +75,10 @@
          "file" : "lib/Apache/Session/Browseable/Oracle.pm",
          "version" : "v1.2.2"
       },
+      "Apache::Session::Browseable::Patroni" : {
+         "file" : "lib/Apache/Session/Browseable/Patroni.pm",
+         "version" : "v1.3.17"
+      },
       "Apache::Session::Browseable::PgHstore" : {
          "file" : "lib/Apache/Session/Browseable/PgHstore.pm",
          "version" : "v1.3.9"
@@ -89,7 +93,7 @@
       },
       "Apache::Session::Browseable::Redis" : {
          "file" : "lib/Apache/Session/Browseable/Redis.pm",
-         "version" : "v1.3.16"
+         "version" : "v1.3.18"
       },
       "Apache::Session::Browseable::SQLite" : {
          "file" : "lib/Apache/Session/Browseable/SQLite.pm",
@@ -123,13 +127,17 @@
          "file" : "lib/Apache/Session/Browseable/Store/Oracle.pm",
          "version" : "v1.2.2"
       },
+      "Apache::Session::Browseable::Store::Patroni" : {
+         "file" : "lib/Apache/Session/Browseable/Store/Patroni.pm",
+         "version" : "v1.3.17"
+      },
       "Apache::Session::Browseable::Store::Postgres" : {
          "file" : "lib/Apache/Session/Browseable/Store/Postgres.pm",
          "version" : "v1.2.2"
       },
       "Apache::Session::Browseable::Store::Redis" : {
          "file" : "lib/Apache/Session/Browseable/Store/Redis.pm",
-         "version" : "v1.3.15"
+         "version" : "v1.3.18"
       },
       "Apache::Session::Browseable::Store::SQLite" : {
          "file" : "lib/Apache/Session/Browseable/Store/SQLite.pm",
@@ -165,6 +173,6 @@
          "url" : "https://github.com/LemonLDAPNG/Apache-Session-Browseable"
       }
    },
-   "version" : "v1.3.16",
+   "version" : "v1.3.18",
    "x_serialization_backend" : "JSON::PP version 4.16"
 }
diff -pruN 1.3.16-1/META.yml 1.3.18-1/META.yml
--- 1.3.16-1/META.yml	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/META.yml	2025-09-23 10:45:12.000000000 +0000
@@ -17,7 +17,7 @@ name: Apache-Session-Browseable
 provides:
   Apache::Session::Browseable:
     file: lib/Apache/Session/Browseable.pm
-    version: v1.3.16
+    version: v1.3.18
   Apache::Session::Browseable::Cassandra:
     file: lib/Apache/Session/Browseable/Cassandra.pm
     version: v1.3.13
@@ -42,6 +42,9 @@ provides:
   Apache::Session::Browseable::Oracle:
     file: lib/Apache/Session/Browseable/Oracle.pm
     version: v1.2.2
+  Apache::Session::Browseable::Patroni:
+    file: lib/Apache/Session/Browseable/Patroni.pm
+    version: v1.3.17
   Apache::Session::Browseable::PgHstore:
     file: lib/Apache/Session/Browseable/PgHstore.pm
     version: v1.3.9
@@ -53,7 +56,7 @@ provides:
     version: v1.3.1
   Apache::Session::Browseable::Redis:
     file: lib/Apache/Session/Browseable/Redis.pm
-    version: v1.3.16
+    version: v1.3.18
   Apache::Session::Browseable::SQLite:
     file: lib/Apache/Session/Browseable/SQLite.pm
     version: v1.2.2
@@ -78,12 +81,15 @@ provides:
   Apache::Session::Browseable::Store::Oracle:
     file: lib/Apache/Session/Browseable/Store/Oracle.pm
     version: v1.2.2
+  Apache::Session::Browseable::Store::Patroni:
+    file: lib/Apache/Session/Browseable/Store/Patroni.pm
+    version: v1.3.17
   Apache::Session::Browseable::Store::Postgres:
     file: lib/Apache/Session/Browseable/Store/Postgres.pm
     version: v1.2.2
   Apache::Session::Browseable::Store::Redis:
     file: lib/Apache/Session/Browseable/Store/Redis.pm
-    version: v1.3.15
+    version: v1.3.18
   Apache::Session::Browseable::Store::SQLite:
     file: lib/Apache/Session/Browseable/Store/SQLite.pm
     version: v1.2.7
@@ -113,5 +119,5 @@ requires:
 resources:
   license: http://dev.perl.org/licenses/
   repository: https://github.com/LemonLDAPNG/Apache-Session-Browseable
-version: v1.3.16
+version: v1.3.18
 x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -pruN 1.3.16-1/RELEASE.md 1.3.18-1/RELEASE.md
--- 1.3.16-1/RELEASE.md	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.18-1/RELEASE.md	2025-09-23 10:45:12.000000000 +0000
@@ -0,0 +1,13 @@
+# Release process
+
+## Version
+
+Update version in `lib/Apache/Session/Browseable.pm`
+
+## Changes
+
+Update changelog in `Changes` file
+
+## Github release
+
+Create a new release on [https://github.com/LemonLDAPNG/Apache-Session-Browseable/releases](https://github.com/LemonLDAPNG/Apache-Session-Browseable/releases)
diff -pruN 1.3.16-1/debian/changelog 1.3.18-1/debian/changelog
--- 1.3.16-1/debian/changelog	2025-04-12 10:32:42.000000000 +0000
+++ 1.3.18-1/debian/changelog	2025-09-29 19:00:22.000000000 +0000
@@ -1,3 +1,21 @@
+libapache-session-browseable-perl (1.3.18-1) unstable; urgency=medium
+
+  * Team upload.
+  * Import upstream version 1.3.18.
+
+ -- gregor herrmann <gregoa@debian.org>  Mon, 29 Sep 2025 21:00:22 +0200
+
+libapache-session-browseable-perl (1.3.17-1) unstable; urgency=medium
+
+  * Team upload.
+  * Import upstream version 1.3.17.
+  * Declare compliance with Debian Policy 4.7.2.
+  * Remove «Rules-Requires-Root: no», which is the current default.
+  * Annotate test-only build dependencies with <!nocheck>.
+  * Remove «Priority: optional», which is the current default.
+
+ -- gregor herrmann <gregoa@debian.org>  Sun, 21 Sep 2025 19:07:26 +0200
+
 libapache-session-browseable-perl (1.3.16-1) unstable; urgency=medium
 
   * Import upstream version 1.3.16.
diff -pruN 1.3.16-1/debian/control 1.3.18-1/debian/control
--- 1.3.16-1/debian/control	2025-04-10 19:30:22.000000000 +0000
+++ 1.3.18-1/debian/control	2025-09-29 19:00:22.000000000 +0000
@@ -1,37 +1,40 @@
 Source: libapache-session-browseable-perl
+Standards-Version: 4.7.2
 Maintainer: Debian Perl Group <pkg-perl-maintainers@lists.alioth.debian.org>
-Uploaders: Yadd <yadd@debian.org>
+Uploaders:
+ Yadd <yadd@debian.org>,
 Section: perl
 Testsuite: autopkgtest-pkg-perl
-Priority: optional
-Build-Depends: debhelper-compat (= 13),
-               libmodule-build-perl,
-               perl
-Build-Depends-Indep: libapache-session-perl <!nocheck>,
-                     libdbd-cassandra-perl <!nocheck>,
-                     libdbd-mysql-perl <!nocheck>,
-                     libdbd-sqlite3-perl <!nocheck>,
-                     libdbi-perl <!nocheck>,
-                     libjson-perl <!nocheck>,
-                     libnet-ldap-perl <!nocheck>,
-                     libredis-fast-perl | libredis-perl <!nocheck>
-Standards-Version: 4.6.2
+Build-Depends:
+ debhelper-compat (= 13),
+ libmodule-build-perl,
+ perl,
+Build-Depends-Indep:
+ libapache-session-perl <!nocheck>,
+ libdbd-cassandra-perl <!nocheck>,
+ libdbd-mysql-perl <!nocheck>,
+ libdbd-sqlite3-perl <!nocheck>,
+ libdbi-perl <!nocheck>,
+ libjson-perl <!nocheck>,
+ libnet-ldap-perl <!nocheck>,
+ libredis-fast-perl <!nocheck> | libredis-perl <!nocheck>,
 Vcs-Browser: https://salsa.debian.org/perl-team/modules/packages/libapache-session-browseable-perl
 Vcs-Git: https://salsa.debian.org/perl-team/modules/packages/libapache-session-browseable-perl.git
 Homepage: https://metacpan.org/release/Apache-Session-Browseable
-Rules-Requires-Root: no
 
 Package: libapache-session-browseable-perl
 Architecture: all
 Multi-Arch: foreign
-Depends: ${misc:Depends},
-         ${perl:Depends},
-         libapache-session-perl,
-         libjson-perl
-Suggests: libdbd-cassandra-perl,
-          libdbi-perl,
-          libnet-ldap-perl,
-          libredis-fast-perl | libredis-perl
+Depends:
+ ${misc:Depends},
+ ${perl:Depends},
+ libapache-session-perl,
+ libjson-perl,
+Suggests:
+ libdbd-cassandra-perl,
+ libdbi-perl,
+ libnet-ldap-perl,
+ libredis-fast-perl | libredis-perl,
 Description: module adding index and search methods to Apache::Session
  Apache::Session::Browseable provides some class methods to manipulate all
  sessions and add the capability to index some fields to make research faster.
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable/DBI.pm 1.3.18-1/lib/Apache/Session/Browseable/DBI.pm
--- 1.3.16-1/lib/Apache/Session/Browseable/DBI.pm	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable/DBI.pm	2025-09-23 10:45:12.000000000 +0000
@@ -109,15 +109,20 @@ sub deleteIfLowerThan {
     elsif ( $rule->{and} ) {
         $query = join ' AND ', map {
             $fields{$_}++;
-            $class->_buildLowerThanExpression( $_, $rule->{or}->{$_} )
+            $class->_buildLowerThanExpression( $_, $rule->{and}->{$_} )
           }
-          keys %{ $rule->{or} };
+          keys %{ $rule->{and} };
     }
     if ( $rule->{not} ) {
-        $query = "($query) AND "
-          . join( ' AND ',
-            map { $fields{$_}++; "$_ <> '$rule->{not}->{$_}'" }
-              keys %{ $rule->{not} } );
+        $query = "($query) AND " . join(
+            ' AND ',
+            map {
+                $rule->{not}->{$_} =~ s/'/''/g;
+                $fields{$_}++;
+                "$_ <> '$rule->{not}->{$_}'"
+              }
+              keys %{ $rule->{not} }
+        );
     }
     return 0
       unless ( $query and $class->_tabInTab( [ keys %fields ], $index ) );
@@ -180,12 +185,12 @@ sub get_key_from_all_sessions {
         ? sub {
             require Storable;
             return Storable::thaw( pack( 'H*', $_[0] ) );
-        }
+          }
         : $args->{DataSource} =~ /^mysql/i ? sub {
             require MIME::Base64;
             require Storable;
             return Storable::thaw( MIME::Base64::decode_base64( $_[0] ) );
-        }
+          }
         : undef
     );
     while ( my @row = $sth->fetchrow_array ) {
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable/Patroni.pm 1.3.18-1/lib/Apache/Session/Browseable/Patroni.pm
--- 1.3.16-1/lib/Apache/Session/Browseable/Patroni.pm	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable/Patroni.pm	2025-09-23 10:45:12.000000000 +0000
@@ -0,0 +1,101 @@
+package Apache::Session::Browseable::Patroni;
+
+use strict;
+use Apache::Session::Lock::Null;
+use Apache::Session::Browseable::PgJSON;
+use Apache::Session::Browseable::Store::Patroni;
+use Apache::Session::Generate::SHA256;
+use Apache::Session::Serialize::JSON;
+
+our @ISA     = qw(Apache::Session::Browseable::PgJSON);
+our $VERSION = '1.3.17';
+
+sub populate {
+    my $self = shift;
+
+    $self->{object_store} =
+      new Apache::Session::Browseable::Store::Patroni $self;
+    $self->{lock_manager} = new Apache::Session::Lock::Null $self;
+    $self->{generate}     = \&Apache::Session::Generate::SHA256::generate;
+    $self->{validate}     = \&Apache::Session::Generate::SHA256::validate;
+    $self->{serialize}    = \&Apache::Session::Serialize::JSON::serialize;
+    $self->{unserialize}  = \&Apache::Session::Serialize::JSON::unserialize;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Apache::Session::Browseable::Patroni - PostgreSQL/Patroni cluster support
+for L<Apache::Session::Browseable::PgJSON>
+
+=head1 SYNOPSIS
+
+  CREATE UNLOGGED TABLE sessions (
+      id varchar(64) not null primary key,
+      a_session jsonb,
+  );
+
+Optionally, add indexes on some fields. Example for Lemonldap::NG:
+
+  CREATE INDEX uid1 ON sessions USING BTREE ( (a_session ->> '_whatToTrace') );
+  CREATE INDEX  s1  ON sessions ( (a_session ->> '_session_kind') );
+  CREATE INDEX  u1  ON sessions ( ( cast(a_session ->> '_utime' AS bigint) ) );
+  CREATE INDEX ip1  ON sessions USING BTREE ( (a_session ->> 'ipAddr') );
+
+Use it with Perl:
+
+  use Apache::Session::Browseable::Postgres;
+
+  my $args = {
+       DataSource => 'dbi:Pg:sessions',
+       UserName   => $db_user,
+       Password   => $db_pass,
+       Commit     => 1,
+
+       # List all Patroni API available (to avoid any haproxy and/or floating IP)
+       PatroniUrl => 'http://1.2.3.4:8008/cluster http://2.3.4.5:8008/cluster',
+  };
+
+  # Use it like L<Apache::Session::Browseable::Postgres>
+
+=head1 DESCRIPTION
+
+Apache::Session::Browseable provides some class methods to manipulate all
+sessions and add the capability to index some fields to make research faster.
+
+Apache::Session::Browseable::Patroni implements it for PosqtgreSQL databases
+using "json" or "jsonb" type to be able to browse sessions and is able to dial
+directly with Patroni API to find the master node of PostgreSQL cluster in
+case of error.
+
+=head1 SEE ALSO
+
+L<http://lemonldap-ng.org>, L<Apache::Session::Postgres>
+
+=head1 COPYRIGHT AND LICENSE
+
+=encoding utf8
+
+=over
+
+=item 2009-2025 by Xavier Guimard
+
+=item 2013-2025 by Clément Oudot
+
+=item 2019-2025 by Maxime Besson
+
+=item 2013-2025 by Worteks
+
+=item 2023-2025 by Linagora
+
+=back
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.1 or,
+at your option, any later version of Perl 5 you may have available.
+
+=cut
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable/Redis.pm 1.3.18-1/lib/Apache/Session/Browseable/Redis.pm
--- 1.3.16-1/lib/Apache/Session/Browseable/Redis.pm	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable/Redis.pm	2025-09-23 10:45:12.000000000 +0000
@@ -9,11 +9,9 @@ use Apache::Session::Lock::Null;
 use Apache::Session::Serialize::JSON;
 use Apache::Session::Browseable::_common;
 
-our $VERSION = '1.3.16';
+our $VERSION = '1.3.18';
 our @ISA     = qw(Apache::Session);
 
-our $redis = $Apache::Session::Browseable::Store::Redis::redis;
-
 sub populate {
     my $self = shift;
 
@@ -236,29 +234,8 @@ sub get_key_from_all_sessions {
 }
 
 sub _getRedis {
-    my $class = shift;
-    my $args  = shift;
-
-    # Manage undef encoding
-    $args->{encoding} = undef
-      if (  $args->{encoding}
-        and $args->{encoding} eq "undef" );
-
-    # If sentinels is not given as an array ref, try to parse
-    # a comma delimited list instead
-    if ( $args->{sentinels}
-        and ref $args->{sentinels} ne 'ARRAY' )
-    {
-        $args->{sentinels} =
-          [ split /[,\s]+/, $args->{sentinels} ];
-    }
-
-    my $redisObj = $redis->new( %{$args} );
-
-    # Manage database
-    $redisObj->select( $args->{database} )
-      if defined $args->{database};
-    return $redisObj;
+    my ( $class, $args ) = @_;
+    return Apache::Session::Browseable::Store::Redis->_getRedis($args);
 }
 
 1;
@@ -279,6 +256,12 @@ Apache::Session::Redis
        # Select database (optional)
        #database => 0,
 
+       # Use a persistent connection to the Redis server
+       # (value is the connection cache key)
+       # You'll probably also want to set
+       # read_timeout, write_timeout, reconnect and every
+       reuse => "myserver",
+
        # Choose your browseable fields
        Index          => 'uid mail',
   };
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable/Store/DBI.pm 1.3.18-1/lib/Apache/Session/Browseable/Store/DBI.pm
--- 1.3.16-1/lib/Apache/Session/Browseable/Store/DBI.pm	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable/Store/DBI.pm	2025-09-23 10:45:12.000000000 +0000
@@ -17,14 +17,12 @@ sub insert {
       ? $session->{args}->{Index}
       : [ split /\s+/, $session->{args}->{Index} ];
 
-    if ( !defined $self->{insert_sth} ) {
-        $self->{insert_sth} =
-          $self->{dbh}->prepare_cached( "INSERT INTO $self->{table_name} ("
-              . join( ',', 'id', 'a_session', map { s/'/''/g; $_ } @$index )
-              . ') VALUES ('
-              . join( ',', ('?') x ( 2 + @$index ) )
-              . ')' );
-    }
+    $self->{insert_sth} //=
+      $self->{dbh}->prepare_cached( "INSERT INTO $self->{table_name} ("
+          . join( ',', 'id', 'a_session', map { s/'/''/g; $_ } @$index )
+          . ') VALUES ('
+          . join( ',', ('?') x ( 2 + @$index ) )
+          . ')' );
 
     $self->{insert_sth}->bind_param( 1, $session->{data}->{_session_id} );
     $self->{insert_sth}->bind_param( 2, $session->{serialized} );
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable/Store/Patroni.pm 1.3.18-1/lib/Apache/Session/Browseable/Store/Patroni.pm
--- 1.3.16-1/lib/Apache/Session/Browseable/Store/Patroni.pm	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable/Store/Patroni.pm	2025-09-23 10:45:12.000000000 +0000
@@ -0,0 +1,151 @@
+package Apache::Session::Browseable::Store::Patroni;
+
+use strict;
+
+use DBI;
+use Apache::Session::Store::Postgres;
+
+our @ISA     = qw(Apache::Session::Store::Postgres);
+our $VERSION = '1.3.17';
+
+our %knownMappings;
+
+sub connection {
+    my $self    = shift;
+    my $session = shift;
+
+    return if ( defined $self->{dbh} );
+
+    $self->{'table_name'} =
+      $session->{args}->{TableName} || $Apache::Session::Store::DBI::TableName;
+
+    if ( exists $session->{args}->{Handle} ) {
+        $self->{dbh}    = $session->{args}->{Handle};
+        $self->{commit} = $session->{args}->{Commit};
+        return;
+    }
+
+    # Use last known Patroni response if available
+    $session->{args}->{DataSource} =
+      $knownMappings{ $session->{args}->{DataSource} }
+      if $session->{args}->{DataSource}
+      and $knownMappings{ $session->{args}->{DataSource} };
+
+    foreach ( 0 .. 1 ) {
+        (
+            $self->checkMaster( $session->{args} )
+              or warn "Patroni check failed"
+        ) if $self->{failure};
+        eval {
+            $self->{dbh} = DBI->connect(
+                $session->{args}->{DataSource},
+                $session->{args}->{UserName},
+                $session->{args}->{Password},
+                { RaiseError => 1, AutoCommit => 0 }
+            ) || die $DBI::errstr;
+        };
+        if ( $@ and !$_ ) {
+            $self->{failure} = 1;
+        }
+        else {
+            last;
+        }
+    }
+    die $@ if $@;
+
+    #If we open the connection, we close the connection
+    $self->{disconnect} = 1;
+
+    #the programmer has to tell us what commit policy to use
+    $self->{commit} = $session->{args}->{Commit} // 1;
+}
+
+sub _try {
+    my $self = shift;
+    my $sub  = shift;
+    my $res;
+
+    foreach ( 0 .. 1 ) {
+        $res =
+          eval { Apache::Session::Store::Postgres->can($sub)->( $self, @_ ) };
+        if ( $@ and $@ !~ /Object does not exist/ and !$_ ) {
+            $self->{failure} = 1;
+            $self->DESTROY;
+            delete $self->{"${sub}_sth"};
+            delete $self->{dbh};
+        }
+        else {
+            last;
+        }
+    }
+    die $@ if $@;
+    return $res;
+}
+
+sub insert {
+    my $self = shift;
+    return $self->_try( 'insert', @_ );
+}
+
+sub update {
+    my $self = shift;
+    return $self->_try( 'update', @_ );
+}
+
+sub materialize {
+    my $self = shift;
+    return $self->_try( 'materialize', @_ );
+}
+
+sub remove {
+    my $self = shift;
+    return $self->_try( 'remove', @_ );
+}
+
+sub checkMaster {
+    my ( $self, $args ) = @_;
+    delete $self->{failure};
+    require JSON;
+    require LWP::UserAgent;
+    require IO::Socket::SSL;
+    my $ua = LWP::UserAgent->new(
+        env_proxy => 1,
+        ssl_opts  => {
+            verify_hostname => 0,
+            SSL_verify_mode => &IO::Socket::SSL::SSL_VERIFY_NONE,
+        },
+        timeout => 3,
+    );
+    my $res;
+
+    foreach my $patroniUrl ( split /[,\s]\s*/,
+        ( $args->{PatroniUrl} || $args->{patroniUrl} ) )
+    {
+        my $resp = $ua->get($patroniUrl);
+        if ( $resp->is_success ) {
+            my $c = eval { JSON::from_json( $resp->decoded_content ) };
+            if ( $@ or !$c->{members} or ref( $c->{members} ) ne 'ARRAY' ) {
+                print STDERR "Bad response from $patroniUrl\n"
+                  . $resp->decoded_content;
+                next;
+            }
+            my ($leader) = grep { $_->{role} eq 'leader' } @{ $c->{members} };
+            unless ($leader) {
+                print STDERR "No leader found from $patroniUrl\n"
+                  . $resp->decoded_content;
+                next;
+            }
+            my $old = $args->{DataSource};
+            $args->{DataSource} =~ s/(?:port|host)=[^;]+;*//g;
+            $args->{DataSource} =~ s/;$//;
+            $args->{DataSource} .= ( $args->{DataSource} =~ /:$/ ? '' : ';' )
+              . "host=$leader->{host};port=$leader->{port}";
+            $knownMappings{$old} = $args->{DataSource};
+            $res = 1;
+            last;
+        }
+    }
+    return $res;
+}
+
+1;
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable/Store/Redis.pm 1.3.18-1/lib/Apache/Session/Browseable/Store/Redis.pm
--- 1.3.16-1/lib/Apache/Session/Browseable/Store/Redis.pm	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable/Store/Redis.pm	2025-09-23 10:45:12.000000000 +0000
@@ -2,8 +2,10 @@ package Apache::Session::Browseable::Sto
 
 use strict;
 
-our $VERSION = '1.3.15';
+our $VERSION = '1.3.18';
+
 our $redis;
+our %reused_connections;
 
 BEGIN {
     $redis = 'Redis::Fast';
@@ -18,25 +20,7 @@ sub new {
     my ( $class, $session ) = @_;
     my $self;
 
-    # Manage undef encoding
-    $session->{args}->{encoding} = undef
-      if (  $session->{args}->{encoding}
-        and $session->{args}->{encoding} eq "undef" );
-
-    # If sentinels is not given as an array ref, try to parse
-    # a comma delimited list instead
-    if ( $session->{args}->{sentinels}
-        and ref $session->{args}->{sentinels} ne 'ARRAY' )
-    {
-        $session->{args}->{sentinels} =
-          [ split /[,\s]+/, $session->{args}->{sentinels} ];
-    }
-
-    $self->{cache} = $redis->new( %{ $session->{args} } );
-
-    # Manage database
-    $self->{cache}->select( $session->{args}->{database} )
-      if defined $session->{args}->{database};
+    $self->{cache} = $class->_getRedis( $session->{args} );
 
     bless $self, $class;
 }
@@ -82,6 +66,43 @@ sub remove {
     $self->{cache}->del($id);
 }
 
+sub _getRedis {
+    my $class = shift;
+    my $args  = shift;
+    my $redisObj;
+
+    # Manage undef encoding
+    $args->{encoding} = undef
+      if (  $args->{encoding}
+        and $args->{encoding} eq "undef" );
+
+    # If sentinels is not given as an array ref, try to parse
+    # a comma delimited list instead
+    if ( $args->{sentinels}
+        and ref $args->{sentinels} ne 'ARRAY' )
+    {
+        $args->{sentinels} =
+          [ split /[,\s]+/, $args->{sentinels} ];
+    }
+
+    if (    $args->{reuse}
+        and $reused_connections{ $args->{reuse} } )
+    {
+        $redisObj = $reused_connections{ $args->{reuse} };
+    }
+    else {
+        $redisObj = $redis->new( %{$args} );
+        if ( $args->{reuse} ) {
+            $reused_connections{ $args->{reuse} } = $redisObj;
+        }
+    }
+
+    # Manage database
+    $redisObj->select( $args->{database} )
+      if defined $args->{database};
+    return $redisObj;
+}
+
 1;
 __END__
 
diff -pruN 1.3.16-1/lib/Apache/Session/Browseable.pm 1.3.18-1/lib/Apache/Session/Browseable.pm
--- 1.3.16-1/lib/Apache/Session/Browseable.pm	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/lib/Apache/Session/Browseable.pm	2025-09-23 10:45:12.000000000 +0000
@@ -1,6 +1,6 @@
 package Apache::Session::Browseable;
 
-our $VERSION = '1.3.16';
+our $VERSION = '1.3.18';
 
 print STDERR "Use a sub module of Apache::Session::Browseable such as Apache::Session::Browseable::File";
 
@@ -33,6 +33,9 @@ chosen module documentation carefully to
 
 =item L<Apache::Session::Browseable::PgJSON>: uses "json/jsonb" field
 
+=item L<Apache::Session::Browseable::Patroni>: uses "json/jsonb" field and
+manage connection using Patroni API to find master node of PostgreSQL cluster
+
 =back
 
 =head3 MySQL or MariaDB
diff -pruN 1.3.16-1/t/Apache-Session-Browseable-Patroni.t 1.3.18-1/t/Apache-Session-Browseable-Patroni.t
--- 1.3.16-1/t/Apache-Session-Browseable-Patroni.t	1970-01-01 00:00:00.000000000 +0000
+++ 1.3.18-1/t/Apache-Session-Browseable-Patroni.t	2025-09-23 10:45:12.000000000 +0000
@@ -0,0 +1,53 @@
+use Test::More;
+
+plan skip_all => "Optional modules (DBD::Pg, DBI) not installed"
+  unless eval {
+    require DBI;
+    require DBD::Pg;
+  };
+
+$package = 'Apache::Session::Browseable::Store::Patroni';
+
+use_ok($package);
+
+my $foo = $package->new;
+
+isa_ok $foo, $package;
+
+$package = 'Apache::Session::Browseable::Patroni';
+use_ok($package);
+
+SKIP: {
+    skip 'No patroniUrl, skipping', 1 unless $ENV{PATRONI_URL};
+    my $args = {
+        DataSource => 'dbi:Pg:dbname=sessions;host='
+          . ( $ENV{PG_HOST} || '127.25.76.98:port=300' ),
+        UserName   => 'postgres',
+        Password   => 'postgres',
+        PatroniUrl => $ENV{PATRONI_URL},
+    };
+
+    my %h;
+    tie %h, $package, undef, $args;
+    ok( %h && $h{_session_id}, 'Hash populated' );
+    my $id = $h{_session_id};
+    $h{a} = 'aa';
+    untie %h;
+    %h = ();
+    tie %h, $package, $id, $args;
+    is( $h{a}, 'aa', 'data stored' );
+    untie %h;
+    if ($ENV{PATRONI_NEXT}) {
+        diag `$ENV{PATRONI_NEXT}`;
+        sleep 1;
+    }
+    tie %h, $package, $id, $args;
+    $h{a} = 'bb';
+    untie %h;
+    %h = ();
+    tie %h, $package, $id, $args;
+    is( $h{a}, 'bb', 'data changed' );
+    untie %h;
+}
+
+done_testing();
diff -pruN 1.3.16-1/t/Apache-Session-Browseable-Redis.t 1.3.18-1/t/Apache-Session-Browseable-Redis.t
--- 1.3.16-1/t/Apache-Session-Browseable-Redis.t	2025-04-12 10:29:42.000000000 +0000
+++ 1.3.18-1/t/Apache-Session-Browseable-Redis.t	2025-09-23 10:45:12.000000000 +0000
@@ -116,7 +116,7 @@ $id5             = $session5{_session_id
 untie %session5;
 
 # Pollutes Redis
-my $redis = $package->_getRedis($args);
+my $redis = 'Apache::Session::Browseable::Store::Redis'->_getRedis($args);
 $redis->set( 'aaaa',     'bbbb' );
 $redis->set( '@aaaa:aa', 'bbbb/bb' );
 
