pghero 0.1.10 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pghero might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +11 -226
- data/app/controllers/pg_hero/home_controller.rb +47 -9
- data/app/views/layouts/pg_hero/application.html.erb +43 -16
- data/app/views/pg_hero/home/_live_queries_table.html.erb +27 -0
- data/app/views/pg_hero/home/_queries_table.html.erb +25 -14
- data/app/views/pg_hero/home/index.html.erb +22 -2
- data/app/views/pg_hero/home/{indexes.html.erb → index_usage.html.erb} +1 -1
- data/app/views/pg_hero/home/live_queries.html.erb +9 -0
- data/app/views/pg_hero/home/queries.html.erb +13 -5
- data/app/views/pg_hero/home/space.html.erb +8 -5
- data/app/views/pg_hero/home/system.html.erb +14 -0
- data/config/routes.rb +25 -14
- data/guides/Docker.md +5 -0
- data/guides/Heroku.md +58 -0
- data/guides/Linux.md +203 -0
- data/guides/Query-Stats.md +60 -0
- data/guides/Rails.md +187 -0
- data/lib/pghero.rb +95 -9
- data/lib/pghero/version.rb +1 -1
- metadata +11 -6
- data/app/views/pg_hero/home/_query_stats_table.html.erb +0 -38
- data/app/views/pg_hero/home/query_stats.html.erb +0 -17
- data/app/views/pg_hero/home/system_stats.html.erb +0 -9
@@ -0,0 +1,60 @@
|
|
1
|
+
# Query Stats
|
2
|
+
|
3
|
+
The [pg_stat_statements module](http://www.postgresql.org/docs/9.3/static/pgstatstatements.html) is used for query stats.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
If you have trouble enabling query stats from the dashboard, try doing it manually.
|
8
|
+
|
9
|
+
Add the following to your `postgresql.conf`:
|
10
|
+
|
11
|
+
```conf
|
12
|
+
shared_preload_libraries = 'pg_stat_statements'
|
13
|
+
pg_stat_statements.track = all
|
14
|
+
pg_stat_statements.max = 10000
|
15
|
+
track_activity_query_size = 2048
|
16
|
+
```
|
17
|
+
|
18
|
+
Then restart PostgreSQL. As a superuser from the `psql` console, run:
|
19
|
+
|
20
|
+
```psql
|
21
|
+
CREATE extension pg_stat_statements;
|
22
|
+
```
|
23
|
+
|
24
|
+
#### Amazon RDS
|
25
|
+
|
26
|
+
Change `shared_preload_libraries` to `pg_stat_statements` in your [Parameter Group](https://console.aws.amazon.com/rds/home?region=us-east-1#parameter-groups:) and restart the database instance.
|
27
|
+
|
28
|
+
As a superuser from the `psql` console, run:
|
29
|
+
|
30
|
+
```psql
|
31
|
+
CREATE extension pg_stat_statements;
|
32
|
+
```
|
33
|
+
|
34
|
+
## Common Issues
|
35
|
+
|
36
|
+
#### pg_stat_statements must be loaded via shared_preload_libraries
|
37
|
+
|
38
|
+
Follow the instructions above.
|
39
|
+
|
40
|
+
#### FATAL: could not access file "pg_stat_statements": No such file or directory
|
41
|
+
|
42
|
+
Run `apt-get install postgresql-contrib-9.3` and follow the instructions above.
|
43
|
+
|
44
|
+
#### The database user does not have permission to ...
|
45
|
+
|
46
|
+
The database user is not a superuser. You can manually enable stats from the `psql` console with:
|
47
|
+
|
48
|
+
```psql
|
49
|
+
CREATE extension pg_stat_statements;
|
50
|
+
```
|
51
|
+
|
52
|
+
and reset stats with:
|
53
|
+
|
54
|
+
```psql
|
55
|
+
SELECT pg_stat_statements_reset();
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Queries show up as `<insufficient privilege>`
|
59
|
+
|
60
|
+
For security reasons, only superusers can see queries executed by other users.
|
data/guides/Rails.md
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
# PgHero for Rails
|
2
|
+
|
3
|
+
:gem:
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application’s Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'pghero'
|
11
|
+
```
|
12
|
+
|
13
|
+
And mount the dashboard in your `config/routes.rb`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
mount PgHero::Engine, at: "pghero"
|
17
|
+
```
|
18
|
+
|
19
|
+
Be sure to [secure the dashboard](#security) in production.
|
20
|
+
|
21
|
+
## Insights
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
PgHero.running_queries
|
25
|
+
PgHero.long_running_queries
|
26
|
+
PgHero.index_usage
|
27
|
+
PgHero.missing_indexes
|
28
|
+
PgHero.unused_indexes
|
29
|
+
PgHero.unused_tables
|
30
|
+
PgHero.database_size
|
31
|
+
PgHero.relation_sizes
|
32
|
+
PgHero.index_hit_rate
|
33
|
+
PgHero.table_hit_rate
|
34
|
+
PgHero.total_connections
|
35
|
+
```
|
36
|
+
|
37
|
+
Kill queries
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
PgHero.kill(pid)
|
41
|
+
PgHero.kill_long_running_queries
|
42
|
+
PgHero.kill_all
|
43
|
+
```
|
44
|
+
|
45
|
+
Query stats
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
PgHero.query_stats_enabled?
|
49
|
+
PgHero.enable_query_stats
|
50
|
+
PgHero.disable_query_stats
|
51
|
+
PgHero.reset_query_stats
|
52
|
+
PgHero.query_stats
|
53
|
+
PgHero.slow_queries
|
54
|
+
```
|
55
|
+
|
56
|
+
Security
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
PgHero.ssl_used?
|
60
|
+
```
|
61
|
+
|
62
|
+
Replication
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
PgHero.replica?
|
66
|
+
PgHero.replication_lag
|
67
|
+
```
|
68
|
+
|
69
|
+
## Users
|
70
|
+
|
71
|
+
Create a user
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
PgHero.create_user("link")
|
75
|
+
# {password: "zbTrNHk2tvMgNabFgCo0ws7T"}
|
76
|
+
```
|
77
|
+
|
78
|
+
This generates and returns a secure password. The user has full access to the `public` schema.
|
79
|
+
|
80
|
+
Read-only access
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
PgHero.create_user("epona", readonly: true)
|
84
|
+
```
|
85
|
+
|
86
|
+
Set the password
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
PgHero.create_user("zelda", password: "hyrule")
|
90
|
+
```
|
91
|
+
|
92
|
+
Drop a user
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
PgHero.drop_user("ganondorf")
|
96
|
+
```
|
97
|
+
|
98
|
+
## Security
|
99
|
+
|
100
|
+
#### Basic Authentication
|
101
|
+
|
102
|
+
Set the following variables in your environment or an initializer.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
ENV["PGHERO_USERNAME"] = "link"
|
106
|
+
ENV["PGHERO_PASSWORD"] = "hyrule"
|
107
|
+
```
|
108
|
+
|
109
|
+
#### Devise
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
authenticate :user, lambda { |user| user.admin? } do
|
113
|
+
mount PgHero::Engine, at: "pghero"
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
## Query Stats
|
118
|
+
|
119
|
+
Query stats can be enabled from the dashboard. If you run into issues, [view the guide](Query-Stats.md).
|
120
|
+
|
121
|
+
## System Stats
|
122
|
+
|
123
|
+
CPU usage is available for Amazon RDS. Add these lines to your application’s Gemfile:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
gem 'aws-sdk'
|
127
|
+
gem 'chartkick'
|
128
|
+
```
|
129
|
+
|
130
|
+
And add these variables to your environment:
|
131
|
+
|
132
|
+
```sh
|
133
|
+
PGHERO_ACCESS_KEY_ID=accesskey123
|
134
|
+
PGHERO_SECRET_ACCESS_KEY=secret123
|
135
|
+
PGHERO_DB_INSTANCE_IDENTIFIER=epona
|
136
|
+
```
|
137
|
+
|
138
|
+
## Multiple Databases
|
139
|
+
|
140
|
+
Create `config/pghero.yml` with:
|
141
|
+
|
142
|
+
```yml
|
143
|
+
default: &default
|
144
|
+
databases:
|
145
|
+
primary:
|
146
|
+
url: <%= ENV["PGHERO_DATABASE_URL"] %>
|
147
|
+
replica:
|
148
|
+
url: <%= ENV["REPLICA_DATABASE_URL"] %>
|
149
|
+
|
150
|
+
development:
|
151
|
+
<<: *default
|
152
|
+
|
153
|
+
production:
|
154
|
+
<<: *default
|
155
|
+
```
|
156
|
+
|
157
|
+
## Customize
|
158
|
+
|
159
|
+
Minimum time for long running queries
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
PgHero.long_running_query_sec = 60 # default
|
163
|
+
```
|
164
|
+
|
165
|
+
Minimum average time for slow queries
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
PgHero.slow_query_ms = 20 # default
|
169
|
+
```
|
170
|
+
|
171
|
+
Minimum calls for slow queries
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
PgHero.slow_query_calls = 100 # default
|
175
|
+
```
|
176
|
+
|
177
|
+
Minimum connections for high connections warning
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
PgHero.total_connections_threshold = 100 # default
|
181
|
+
```
|
182
|
+
|
183
|
+
## Bonus
|
184
|
+
|
185
|
+
- See where queries come from with [Marginalia](https://github.com/basecamp/marginalia) - comments appear on the Live Queries tab.
|
186
|
+
- Get weekly news and articles with [Postgres Weekly](http://postgresweekly.com)
|
187
|
+
- Optimize your configuration with [PgTune](http://pgtune.leopard.in.ua) and [pgBench](http://www.postgresql.org/docs/devel/static/pgbench.html)
|
data/lib/pghero.rb
CHANGED
@@ -5,18 +5,73 @@ require "pghero/engine" if defined?(Rails)
|
|
5
5
|
module PgHero
|
6
6
|
# hack for connection
|
7
7
|
class Connection < ActiveRecord::Base
|
8
|
-
|
8
|
+
self.abstract_class = true
|
9
9
|
end
|
10
10
|
|
11
11
|
class << self
|
12
|
-
attr_accessor :long_running_query_sec, :slow_query_ms, :slow_query_calls, :total_connections_threshold
|
12
|
+
attr_accessor :long_running_query_sec, :slow_query_ms, :slow_query_calls, :total_connections_threshold, :env
|
13
13
|
end
|
14
|
-
self.long_running_query_sec = 60
|
15
|
-
self.slow_query_ms = 20
|
16
|
-
self.slow_query_calls = 100
|
17
|
-
self.total_connections_threshold = 100
|
14
|
+
self.long_running_query_sec = (ENV["PGHERO_LONG_RUNNING_QUERY_SEC"] || 60).to_i
|
15
|
+
self.slow_query_ms = (ENV["PGHERO_SLOW_QUERY_MS"] || 20).to_i
|
16
|
+
self.slow_query_calls = (ENV["PGHERO_SLOW_QUERY_CALLS"] || 100).to_i
|
17
|
+
self.total_connections_threshold = (ENV["PGHERO_TOTAL_CONNECTIONS_THRESHOLD"] || 100).to_i
|
18
|
+
self.env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
18
19
|
|
19
20
|
class << self
|
21
|
+
def config
|
22
|
+
Thread.current[:pghero_config] ||= begin
|
23
|
+
path = "config/pghero.yml"
|
24
|
+
|
25
|
+
config =
|
26
|
+
if File.exist?(path)
|
27
|
+
YAML.load(ERB.new(File.read(path)).result)[env]
|
28
|
+
end
|
29
|
+
|
30
|
+
if config
|
31
|
+
config
|
32
|
+
else
|
33
|
+
{
|
34
|
+
"databases" => {
|
35
|
+
"primary" => {
|
36
|
+
"url" => ENV["PGHERO_DATABASE_URL"], # nil reverts to default config
|
37
|
+
"db_instance_identifier" => ENV["PGHERO_DB_INSTANCE_IDENTIFIER"]
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def primary_database
|
46
|
+
config["databases"].keys.first
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_database
|
50
|
+
Thread.current[:pghero_current_database] ||= primary_database
|
51
|
+
end
|
52
|
+
|
53
|
+
def current_database=(database)
|
54
|
+
raise "Database not found" unless database_config(database)
|
55
|
+
Thread.current[:pghero_current_database] = database.to_s
|
56
|
+
Thread.current[:pghero_connection] = nil
|
57
|
+
database
|
58
|
+
end
|
59
|
+
|
60
|
+
def database_config(database)
|
61
|
+
config["databases"][database.to_s]
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_config
|
65
|
+
database_config(current_database)
|
66
|
+
end
|
67
|
+
|
68
|
+
def with(database)
|
69
|
+
self.current_database = database
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
self.current_database = primary_database
|
73
|
+
end
|
74
|
+
|
20
75
|
def running_queries
|
21
76
|
select_all <<-SQL
|
22
77
|
SELECT
|
@@ -225,6 +280,11 @@ module PgHero
|
|
225
280
|
execute("SELECT pg_terminate_backend(#{pid.to_i})").first["pg_terminate_backend"] == "t"
|
226
281
|
end
|
227
282
|
|
283
|
+
def kill_long_running_queries
|
284
|
+
long_running_queries.each { |query| kill(query["pid"]) }
|
285
|
+
true
|
286
|
+
end
|
287
|
+
|
228
288
|
def kill_all
|
229
289
|
select_all <<-SQL
|
230
290
|
SELECT
|
@@ -340,6 +400,16 @@ module PgHero
|
|
340
400
|
end
|
341
401
|
end
|
342
402
|
|
403
|
+
def ssl_used?
|
404
|
+
ssl_used = nil
|
405
|
+
Connection.transaction do
|
406
|
+
execute("CREATE EXTENSION IF NOT EXISTS sslinfo")
|
407
|
+
ssl_used = select_all("SELECT ssl_is_used()").first["ssl_is_used"] == "t"
|
408
|
+
raise ActiveRecord::Rollback
|
409
|
+
end
|
410
|
+
ssl_used
|
411
|
+
end
|
412
|
+
|
343
413
|
def cpu_usage
|
344
414
|
rds_stats("CPUUtilization")
|
345
415
|
end
|
@@ -348,6 +418,10 @@ module PgHero
|
|
348
418
|
rds_stats("DatabaseConnections")
|
349
419
|
end
|
350
420
|
|
421
|
+
def replication_lag_stats
|
422
|
+
rds_stats("ReplicaLag")
|
423
|
+
end
|
424
|
+
|
351
425
|
def rds_stats(metric_name)
|
352
426
|
if system_stats_enabled?
|
353
427
|
cw = AWS::CloudWatch.new(access_key_id: access_key_id, secret_access_key: secret_access_key)
|
@@ -387,7 +461,7 @@ module PgHero
|
|
387
461
|
|
388
462
|
commands =
|
389
463
|
[
|
390
|
-
"CREATE ROLE #{user} LOGIN PASSWORD #{
|
464
|
+
"CREATE ROLE #{user} LOGIN PASSWORD #{connection.quote(password)}",
|
391
465
|
"GRANT CONNECT ON DATABASE #{database} TO #{user}",
|
392
466
|
"GRANT USAGE ON SCHEMA #{schema} TO #{user}"
|
393
467
|
]
|
@@ -448,7 +522,7 @@ module PgHero
|
|
448
522
|
end
|
449
523
|
|
450
524
|
def db_instance_identifier
|
451
|
-
|
525
|
+
current_config["db_instance_identifier"]
|
452
526
|
end
|
453
527
|
|
454
528
|
def explain(sql)
|
@@ -485,6 +559,15 @@ module PgHero
|
|
485
559
|
Hash[names.map { |name| [name, values[name]] }]
|
486
560
|
end
|
487
561
|
|
562
|
+
def replica?
|
563
|
+
select_all("SELECT setting FROM pg_settings WHERE name = 'hot_standby'").first["setting"] == "on"
|
564
|
+
end
|
565
|
+
|
566
|
+
# http://www.niwi.be/2013/02/16/replication-status-in-postgresql/
|
567
|
+
def replication_lag
|
568
|
+
select_all("SELECT EXTRACT(EPOCH FROM NOW() - pg_last_xact_replay_timestamp()) AS replication_lag").first["replication_lag"].to_f
|
569
|
+
end
|
570
|
+
|
488
571
|
def friendly_value(setting, unit)
|
489
572
|
if %w(kB 8kB).include?(unit)
|
490
573
|
value = setting.to_i
|
@@ -512,7 +595,10 @@ module PgHero
|
|
512
595
|
end
|
513
596
|
|
514
597
|
def connection
|
515
|
-
|
598
|
+
Thread.current[:pghero_connection] ||= begin
|
599
|
+
Connection.establish_connection(current_config["url"])
|
600
|
+
Connection.connection
|
601
|
+
end
|
516
602
|
end
|
517
603
|
|
518
604
|
# from ActiveSupport
|
data/lib/pghero/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pghero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -96,21 +96,26 @@ files:
|
|
96
96
|
- app/controllers/pg_hero/home_controller.rb
|
97
97
|
- app/views/layouts/pg_hero/application.html.erb
|
98
98
|
- app/views/pg_hero/home/_connections_table.html.erb
|
99
|
+
- app/views/pg_hero/home/_live_queries_table.html.erb
|
99
100
|
- app/views/pg_hero/home/_queries_table.html.erb
|
100
|
-
- app/views/pg_hero/home/_query_stats_table.html.erb
|
101
101
|
- app/views/pg_hero/home/connections.html.erb
|
102
102
|
- app/views/pg_hero/home/explain.html.erb
|
103
103
|
- app/views/pg_hero/home/index.html.erb
|
104
|
-
- app/views/pg_hero/home/
|
104
|
+
- app/views/pg_hero/home/index_usage.html.erb
|
105
|
+
- app/views/pg_hero/home/live_queries.html.erb
|
105
106
|
- app/views/pg_hero/home/queries.html.erb
|
106
|
-
- app/views/pg_hero/home/query_stats.html.erb
|
107
107
|
- app/views/pg_hero/home/space.html.erb
|
108
|
-
- app/views/pg_hero/home/
|
108
|
+
- app/views/pg_hero/home/system.html.erb
|
109
109
|
- app/views/pg_hero/home/tune.html.erb
|
110
110
|
- config/routes.rb
|
111
111
|
- gemfiles/activerecord31.gemfile
|
112
112
|
- gemfiles/activerecord32.gemfile
|
113
113
|
- gemfiles/activerecord40.gemfile
|
114
|
+
- guides/Docker.md
|
115
|
+
- guides/Heroku.md
|
116
|
+
- guides/Linux.md
|
117
|
+
- guides/Query-Stats.md
|
118
|
+
- guides/Rails.md
|
114
119
|
- lib/pghero.rb
|
115
120
|
- lib/pghero/engine.rb
|
116
121
|
- lib/pghero/version.rb
|