Skip to content
Arproxy is a proxy between ActiveRecord and database adapter
Branch: master
Clone or download
Latest commit 3ff68fe Apr 5, 2017
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
gemfiles Start supporting activerecord-5.0 and stop 3.2-4.1 Jul 6, 2016
lib
spec Set Arproxy::Config#adapter from database.yml Apr 1, 2017
.gitignore
.rspec
.travis.yml Update rubies Apr 5, 2017
Appraisals
ChangeLog.md
Gemfile
LICENSE.txt
README.md Increment copyright year Apr 5, 2017
Rakefile
arproxy.gemspec

README.md

Build Status

Welcome to Arproxy

Arproxy is a proxy between ActiveRecord and Database adapters. You can make a custom proxy what analyze and/or modify SQLs before DB adapter executes them.

Getting Started

Write your proxy and its configurations in Rails' config/initializers:

class QueryTracer < Arproxy::Base
  def execute(sql, name=nil)
    Rails.logger.debug sql
    Rails.logger.debug caller(1).join("\n")
    super(sql, name)
  end
end

Arproxy.configure do |config|
  config.adapter = "mysql2" # A DB Apdapter name which is used in your database.yml
  config.use QueryTracer
end
Arproxy.enable!

Then you can see the backtrace of SQLs in the Rails' log.

# In your Rails code
MyTable.where(:id => id).limit(1) # => The SQL and the backtrace appear in the log

Architecture

Without Arproxy:

+-------------------------+                       +------------------+
| ActiveRecord::Base#find |--execute(sql, name)-->| Database Adapter |
+-------------------------+                       +------------------+

With Arproxy:

Arproxy.configure do |config|
  config.adapter = "mysql2"
  config.use MyProxy1
  config.use MyProxy2
end
+-------------------------+                       +----------+   +----------+   +------------------+
| ActiveRecord::Base#find |--execute(sql, name)-->| MyProxy1 |-->| MyProxy2 |-->| Database Adapter |
+-------------------------+                       +----------+   +----------+   +------------------+

Examples

Slow Query Logger

class SlowQueryLogger < Arproxy::Base
  def initialize(slow_ms)
    @slow_ms = slow_ms
  end

  def execute(sql, name=nil)
    result = nil
    ms = Benchmark.ms { result = super(sql, name) }
    if ms >= @slow_ms
      Rails.logger.info "Slow(#{ms.to_i}ms): #{sql}"
    end
    result
  end
end

Arproxy.configure do |config|
  config.use SlowQueryLogger, 1000
end

Adding Comments to SQLs

class CommentAdder < Arproxy::Base
  def execute(sql, name=nil)
    sql += " /*this_is_comment*/"
    super(sql, name)
  end
end

Readonly Access

class Readonly < Arproxy::Base
  def execute(sql, name=nil)
    if sql =~ /^(SELECT|SET|SHOW|DESCRIBE)\b/
      super sql, name
    else
      Rails.logger.warn "#{name} (BLOCKED) #{sql}"
    end
  end
end

Use plug-in

# any_gem/lib/arproxy/plugin/my_plugin
module Arproxy::Plugin
  class MyPlugin < Arproxy::Base
    Arproxy::Plugin.register(:my_plugin, self)

    def execute(sql, name=nil)
      # Any processing
    end
  end
end
Arproxy.configure do |config|
  config.plugin :my_plugin
end

Appendix

What the `name' argument is

In the Rails' log you may see queries like this:

User Load (22.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'Issei Naruta'

Then "User Load" is the name.

License

Arproxy is released under the MIT license:

Copyright (c) 2017 Issei Naruta

You can’t perform that action at this time.