class MiqPglogical
  module ConnectionHandling
    extend ActiveSupport::Concern

    module ClassMethods
      def with_connection_error_handling
        retry_attempted ||= false
        yield
      rescue PG::ConnectionBad
        raise if retry_attempted

        pglogical(true)
        retry_attempted = true
        retry
      end

      def pglogical(refresh = false)
        @pglogical = nil if refresh
        @pglogical ||= PG::LogicalReplication::Client.new(pg_connection)
      end
    end

    def pglogical(refresh = false)
      self.class.pglogical(refresh)
    end
  end
end

class MiqPglogical
  module ConnectionHandling
    extend ActiveSupport::Concern

    module ClassMethods
      def with_connection_error_handling
        retry_attempted ||= false
        yield
      rescue PG::ConnectionBad
        raise if retry_attempted

        pglogical(true)
        retry_attempted = true
        retry
      end

      def pglogical(refresh = false)
        # TODO: Review if the reasons behind the previous caching / refreshing
        # of the PG::LogicalReplication::Client
        #
        #    @pglogical = nil if refresh
        #    @pglogical ||= PG::LogicalReplication::Client.new(pg_connection)
        #
        # is still relevant as it caused segfaults with rails 6 when the
        # caching was in place.
        PG::LogicalReplication::Client.new(pg_connection)
      end
    end

    def pglogical(refresh = false)
      self.class.pglogical(refresh)
    end
  end
end

Remove class cache called in threads to avoid timing issues
Remove class cache called in threads to avoid timing issues

Fixes #20979

The first two calls of the class method pg_logical can and are often called in threads,
leading to race conditions with two threads sharing the same PG::LogicalReplication::Client
object, ultimately ending in various fatal errors including segmentation faults, bad file
descriptor, and malloc (double free for ptr) errors. This is by no means limited to two
threads, with more threads in puma, it's more likely that two threads compete and trip
each other in code that is not thread safe.

Many of the backtraces seen in #20979 show the following area of code:
```
pg-1.2.3/lib/pg/basic_type_mapping.rb:120:in `exec'
pg-1.2.3/lib/pg/basic_type_mapping.rb:120:in `build_coder_maps'
pg-1.2.3/lib/pg/basic_type_mapping.rb:402:in `initialize'
pg-logical_replication-1.0.0/lib/pg/logical_replication/client.rb:316:in `new'
```

We don't know the exact cause of the fatal errors listed above but my best guess follows.

It's important to point out that each PG::LogicalReplication::Client is initialized with a
ApplicationRecord.connection.raw_connection object called from pg_connection[1] and caching
this Client object via the pg_logical method bypasses the normal connection handler/connection
pool code which is meant to handle multiple threads.

When we remove this Client caching, each thread initializes their own Client, leading to separate
calls to ApplicationRecord.connection.raw_connection via the connection method which goes through
the connection handler/connection pool.

Ultimately, we'll need to determine if that caching was needed for performance or consistency
reasons, but we can solve that later. The initial caching was added here[2] at a time when the
cached object kept a reference to the connection, not the connection.raw_connection as it does now,
so some code around this change has moved.  During the rails 6 upgrade, some of this code was moved
in two pull requests [3][4] and could be in play.

[1] https://github.com/ManageIQ/manageiq/blob/ae1b5cacb3ac65b0317fd353a11e1f217870b291/lib/miq_pglogical/connection_handling.rb#L19
[2] https://github.com/ManageIQ/manageiq/commit/733da742d4264c9068584bd0c321a3950efea8df#diff-491fdea69d5c969405eff4d87c23d1751b955161e7b888a5af038892122eba2aR92
[3] https://github.com/ManageIQ/manageiq/pull/20841
[4] https://github.com/ManageIQ/manageiq/pull/20856
