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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +9 -0
- data/LICENSE +23 -0
- data/README.md +30 -0
- data/Rakefile +6 -0
- data/activerecord-pgbouncer.gemspec +27 -0
- data/lib/active_record/connection_adapters/pgbouncer_adapter.rb +92 -0
- data/lib/active_record/pgbouncer.rb +3 -0
- data/lib/active_record/pgbouncer/version.rb +5 -0
- metadata +117 -0
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
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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,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
|
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: []
|