• Stars
    star
    227
  • Rank 175,900 (Top 4 %)
  • Language
    Ruby
  • License
    MIT License
  • Created about 8 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Ruby gem for using the postgres explain command with Active Record 👀

CircleCI

pg-eyeballs 👀

pg-eyeballs is a ruby gem that gives you detailed information about how the SQL queries created by the active record code you write are executed by the database. It gives you an easy, ruby friendly way to see the output of the Postgres EXPLAIN command and integrates with the popular query analysis tool gocmdpev.

Using it you can see:

  • What queries were run
  • How long they took
  • Which indexes were used
  • Which algorithms were used
  • Much more!

Installation

Add this line to your application's Gemfile:

gem 'pg-eyeballs'

And then execute:

$ bundle

Or install it yourself as:

$ gem install pg-eyeballs

Usage

explain(options: [:analyze, :verbose, :costs, :buffers], format: :text)

User.all.eyeballs.explain

["Seq Scan on public.users  (cost=0.00..22.30 rows=1230 width=36) (actual time=0.002..0.002 rows=1 loops=1)
    Output: id, email
    Buffers: shared hit=1
  Planning time: 0.014 ms
  Execution time: 0.009 ms"
]

Most eyeballs methods return an array because an ActiveRecord::Relation can run more than one query, for instance when it has a preload or with certain subqueries

User.all.preload(:profiles).eyeballs.explain(options: [:verbose], format: :yaml)
['- Plan:
      Node Type: "Seq Scan"
      Relation Name: "users"
      Schema: "public"
      Alias: "users"
      Startup Cost: 0.00
      Total Cost: 22.30
      Plan Rows: 1230
      Plan Width: 36
      Output:
      - "id"
      - "email"',
 '- Plan:
      Node Type: "Seq Scan"
      Relation Name: "profiles"
      Schema: "public"
      Alias: "profiles"
      Startup Cost: 0.00\
      Total Cost: 36.75
      Plan Rows: 11
      Plan Width: 8
      Output:
        - "id"
        - "user_id"
      Filter: "(profiles.user_id = 1)"'
]

formats: :text, :xml, :json, :yaml

explain_queries(options: [:analyze, :verbose, :costs, :buffers], format: :text)

User.all.preload(:profiles).eyeballs.explain_queries
["EXPLAIN (ANALYZE,VERBOSE,COSTS,BUFFERS,FORMAT TEXT) SELECT \"users\".* FROM \"users\"",
 "EXPLAIN (ANALYZE,VERBOSE,COSTS,BUFFERS,FORMAT TEXT) SELECT \"profiles\".* FROM \"profiles\" WHERE \"profiles\".\"user_id\" IN (1)"]

formats: :text, :xml, :json, :yaml

log_json(options: [:analyze, :verbose, :costs, :buffers])

Prints each JSON plan on a separate line. This is useful for command line processing with xargs and jq or gocmdpev

User.all.preload(:profiles).eyeballs.log_json

queries

User.all.preload(:profiles).eyeballs.queries
["SELECT \"users\".* FROM \"users\"",
 "SELECT \"profiles\".* FROM \"profiles\" WHERE \"profiles\".\"user_id\" IN (1)"]

to_hash_array(options: [:analyze, :verbose, :costs, :buffers])

User.all.eyeballs.to_hash_array
[[{"Plan"=>{
    "Node Type"=>"Seq Scan",
    "Relation Name"=>"users",
    "Schema"=>"public",
    "Alias"=>"users",
    "Startup Cost"=>0.0,
    "Total Cost"=>22.3,
    "Plan Rows"=>1230,
    "Plan Width"=>36,
    "Actual Startup Time"=>0.001,
    "Actual Total Time"=>0.001,
    "Actual Rows"=>1,
    "Actual Loops"=>1,
    "Output"=>["id", "email"],
    "Shared Hit Blocks"=>1,
    "Shared Read Blocks"=>0,
    "Shared Dirtied Blocks"=>0,
    "Shared Written Blocks"=>0,
    "Local Hit Blocks"=>0,
    "Local Read Blocks"=>0,
    "Local Dirtied Blocks"=>0,
    "Local Written Blocks"=>0,
    "Temp Read Blocks"=>0,
    "Temp Written Blocks"=>0,
    "I/O Read Time"=>0.0,
    "I/O Write Time"=>0.0},
    "Planning Time"=>0.014,
    "Triggers"=>[],
    "Execution Time"=>0.007}]
]

to_json(options: [:analyze, :verbose, :costs, :buffers])

alias for explain(format: :json)

to_s(options: [:analyze, :verbose, :costs, :buffers])

User.all.preload(:profiles).eyeballs.to_s
"Seq Scan on public.users  (cost=0.00..22.30 rows=1230 width=36) (actual time=0.001..0.002 rows=1 loops=1)
  Output: id, email
  Buffers: shared hit=1
Planning time: 0.010 ms
Execution time: 0.005 ms

Seq Scan on public.profiles  (cost=0.00..36.75 rows=11 width=8) (actual time=0.002..0.002 rows=1 loops=1)
  Output: id, user_id
  Filter: (profiles.user_id = 1)
  Buffers: shared hit=1
Planning time: 0.013 ms
Execution time: 0.006 ms"

Integration with gocmdpev

pg-eyeballs integrates with gocmdpev. If you have gocmdpev installed, you can use it in your Rails console:

User.all.preload(:profiles).eyeballs.gocmdpev

gocmdpev

You can also use pg-eyeballs and gocmdpev together from the command line. First, alias the included command. From inside the directory of you Rails project run

alias eyeballs=$(bundle show pg-eyeballs)/bin/gocmdpev

To use, also from inside your Rails project directory, run

eyeballs User.preload(:profiles)

Compatibility

pg-eyeballs has been tested with Rails versions 4 and 5. It may work on earlier versions, but I haven't tried it.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/bradurani/pg-eyeballs. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

Running the tests

First, bundle install. After this, if you are using the default database, first thing is to run createdb eyeballs_test. After this you can run the tests with rake.

License

The gem is available as open source under the terms of the MIT License.

More Repositories

1

functional-javascript

Functional JavaScript demo using Immutable.js
JavaScript
27
star
2

Gitfile

Installs git repos onto your system and keeps them up-to-date
Go
19
star
3

command-oriented-programming

Trying to distill everything I know about web application architecture into some kind of useful philosophy
9
star
4

rust-iterator-to-hash-map

A Rust crate providing a method to convert Iterators to HashMaps
Rust
6
star
5

vim-powerclose

A vim plugin allowing you to close any type of window with a single function
Vim Script
5
star
6

connect-four-elm

A Connect Four Demo built with Elm
Elm
5
star
7

footprints-tracker

A browser analytics tracking library
JavaScript
4
star
8

functional-ruby

An example of pure functional programming in Ruby using the Hamster library to solve Bloxorz
Ruby
3
star
9

rust-hashmap-to-hashmap

Adds adds a .map method to hashmap that maps to another hashmap
Rust
2
star
10

hamster-matrix

Ruby gem providing a persistent immutable matrix with an interface matching Ruby's Matrix class. Built using the Hamster gem
Ruby
2
star
11

ConwaysGameOfLifeJasmineDemo

A demo of the Jasmine BDD framework for JavaScript using Conway's Game of Life.
JavaScript
2
star
12

ranger

A JavaScript micro-framework that does not try to hide its underlying dependencies
JavaScript
2
star
13

teleport

Ruby
1
star
14

elm-time-ago

Creates string such as "5 minutes ago" from 2 Date objects
1
star
15

ubuntu-xenial-kernel

Fork of Linux kernel for Macbook Pro
C
1
star
16

tacocopter-sql-workshop

Complete SQL querying workshop and Rails coding challenge app
Ruby
1
star
17

truckster

A web hook for syncing a GitHub repo to an AWS S3 bucket
JavaScript
1
star
18

elm-demo

Step-by-Step Elm Demo Built for Lightning Talk at Santa Barbara JavaScript 11/18
1
star
19

connect-four-ruby

A solver for Connect Four used to demonstrate immutable data structures
Ruby
1
star