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.

@@ -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.
@@ -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)
@@ -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
- establish_connection ENV["PGHERO_DATABASE_URL"] if ENV["PGHERO_DATABASE_URL"]
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 #{Connection.connection.quote(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
- ENV["PGHERO_DB_INSTANCE_IDENTIFIER"]
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
- @connection ||= Connection.connection
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
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "0.1.10"
2
+ VERSION = "1.0.0"
3
3
  end
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.1.10
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-03-20 00:00:00.000000000 Z
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/indexes.html.erb
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/system_stats.html.erb
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