activerecord-pgbouncer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ec071fbf0fd88fead70eac5b4cd95852e81cc3e7
4
+ data.tar.gz: e3a2cd338297d973e1cb6e6ab1dbba4da140bc77
5
+ SHA512:
6
+ metadata.gz: fa183c8b43fb46cdd6eeef003791878b17d461831dd839dca246302a3acf67752d03c62f9353d419678f66d16abe53f25824d499cd78c2732d5856032417766c
7
+ data.tar.gz: 1ebfee1542d62f54a1bbc936f67a27c737084c3a7cd07c19fda90a30e52422404c78c464cb3f3aa97585c701808d73571c4cb15a77bdc117c94ef00c4a80e7f3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.4
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in activerecord-pgbouncer.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'activerecord', '4.1.14'
8
+ gem 'pg'
9
+ end
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2015, Remind101, Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # ActiveRecord PgBouncer Connection Adapter
2
+
3
+ When using PgBouncer, there are certain considerations to take into account:
4
+
5
+ * If you're using transaction pooling mode, prepared statements must be disabled.
6
+ * If you're using transaction pooling mode, session level features should not be used.
7
+
8
+ This is a light layer above the PostgreSQL connection adapter, that helps ensure that you don't make the mistakes above!
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'activerecord-pgbouncer'
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ This adds a `pgbouncer` adapter, which you can use in `config/database.yml` or `ENV['DATABASE_URL']`:
21
+
22
+ ```yaml
23
+ production:
24
+ adapter: pgbouncer
25
+ pooling_mode: transaction # Can be `session`, `transaction`, or `statement`
26
+ ```
27
+
28
+ ```shell
29
+ export DATABASE_URL=pgbouncer://user:pass@host/db?pooling_mode=transaction
30
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_record/pgbouncer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "activerecord-pgbouncer"
8
+ spec.version = ActiveRecord::PgBouncer::VERSION
9
+ spec.authors = ["Eric J. Holmes"]
10
+ spec.email = ["eric@remind101.com"]
11
+
12
+ spec.summary = %q{ActiveRecord connection adapter for safe PgBouncer use}
13
+ spec.description = %q{ActiveRecord connection adapter for safe PgBouncer use}
14
+ spec.homepage = "https://github.com/remind101/activerecord-pgbouncer"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "activerecord", [">= 4.1", "<= 5"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.10"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec"
27
+ end
@@ -0,0 +1,92 @@
1
+ require 'active_record/connection_adapters/postgresql_adapter'
2
+
3
+ module ActiveRecord
4
+ # This is mostly copy pasta from https://github.com/rails/rails/blob/31ab3aa0e881acfd1475abae602455905a4cadf1/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L20-L42
5
+ module ConnectionHandling
6
+ VALID_PGBOUNCER_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
7
+ :client_encoding, :options, :application_name, :fallback_application_name,
8
+ :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
9
+ :tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
10
+ :sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
11
+
12
+ # Establishes a connection to the database that's used by all Active Record objects
13
+ def pgbouncer_connection(config)
14
+ pgbouncer = ConnectionAdapters::PgBouncerAdapter
15
+
16
+ conn_params = config.symbolize_keys
17
+ mode = conn_params[:pooling_mode]
18
+
19
+ raise "Unknown pooling mode provided: #{mode}" unless pgbouncer::POOLING_MODES.include?(mode)
20
+ disable_session = mode == pgbouncer::TRANSACTION_POOLING || mode == pgbouncer::STATEMENT_POOLING
21
+ raise "Prepared statements cannot be used with pgbouncer in #{mode} pooling mode" if disable_session && conn_params[:prepared_statements]
22
+ raise "Session level variables cannot be used with pgbouncer in #{mode} pooling mode" if disable_session && conn_params[:variables]
23
+
24
+ conn_params.delete_if { |_, v| v.nil? }
25
+
26
+ # Map ActiveRecords param names to PGs.
27
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
28
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
29
+
30
+ # Forward only valid config params to PGconn.connect.
31
+ conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
32
+
33
+ # The postgres drivers don't allow the creation of an unconnected PGconn object,
34
+ # so just pass a nil connection object for the time being.
35
+ pgbouncer::new(mode, nil, logger, conn_params, config)
36
+ end
37
+ end
38
+ end
39
+
40
+ module ActiveRecord
41
+ module ConnectionAdapters
42
+ class PgBouncerAdapter < PostgreSQLAdapter
43
+ # Most polite method. When client connects, a server connection will be
44
+ # assigned to it for the whole duration it stays connected. When client
45
+ # disconnects, the server connection will be put back into pool. This
46
+ # mode supports all PostgeSQL features.
47
+ SESSION_POOLING = 'session'.freeze
48
+
49
+ # Server connection is assigned to client only during a transaction. When
50
+ # PgBouncer notices that transaction is over, the server will be put back
51
+ # into pool. This mode breaks few session-based features of PostgreSQL.
52
+ # You can use it only when application cooperates by not using features
53
+ # that break. See the table below for incompatible features.
54
+ TRANSACTION_POOLING = 'transaction'.freeze
55
+
56
+ # Most aggressive method. This is transaction pooling with a twist -
57
+ # multi-statement transactions are disallowed. This is meant to enforce
58
+ # "autocommit" mode on client, mostly targeted for PL/Proxy.
59
+ STATEMENT_POOLING = 'statement'.freeze
60
+
61
+ POOLING_MODES = [SESSION_POOLING, TRANSACTION_POOLING, STATEMENT_POOLING]
62
+
63
+ attr_reader :pooling_mode
64
+
65
+ def initialize(pooling_mode, connection, logger, connection_parameters, config)
66
+ @pooling_mode = pooling_mode
67
+ super(connection, logger, connection_parameters, config)
68
+ end
69
+
70
+ # The PostgreSQL adapter sets a number of session level settings. In
71
+ # transaction pooling mode, this will have undesirable results, since
72
+ # each `SET` statement is not gauranteed to be executed with the same
73
+ # backend connection.
74
+ #
75
+ # Instead, when using this adapter, you should set a connect_query that sets the following:
76
+ #
77
+ # set client_encoding = 'UTF8'
78
+ # set client_min_messages = 'warning'
79
+ # set standard_conforming_strings = 'on'
80
+ # set timezone = 'UTC'
81
+ #
82
+ # See https://github.com/rails/rails/blob/a456acb2f2af8365eb9151c7cd2d5a10c189d191/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L648-L678
83
+ def configure_connection
84
+ if pooling_mode == TRANSACTION_POOLING || pooling_mode == STATEMENT_POOLING
85
+ # NOOP
86
+ else
87
+ super
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ require 'active_record'
2
+ require 'active_record/pgbouncer/version'
3
+ require 'active_record/connection_adapters/pgbouncer_adapter'
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module PgBouncer
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-pgbouncer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eric J. Holmes
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-06-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.1'
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: '5'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.1'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: '5'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.10'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.10'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ description: ActiveRecord connection adapter for safe PgBouncer use
76
+ email:
77
+ - eric@remind101.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - ".gitignore"
83
+ - ".rspec"
84
+ - ".travis.yml"
85
+ - Gemfile
86
+ - LICENSE
87
+ - README.md
88
+ - Rakefile
89
+ - activerecord-pgbouncer.gemspec
90
+ - lib/active_record/connection_adapters/pgbouncer_adapter.rb
91
+ - lib/active_record/pgbouncer.rb
92
+ - lib/active_record/pgbouncer/version.rb
93
+ homepage: https://github.com/remind101/activerecord-pgbouncer
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.4.5
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: ActiveRecord connection adapter for safe PgBouncer use
117
+ test_files: []