Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 91e8b74ee499e3f5c9be8dd6aef405eae78f29d0 Yehuda Katz committed Feb 16, 2010
@@ -0,0 +1,17 @@
+require "queryer/vendor/foundation"
+require "queryer/client"
+require "queryer/middleware/builder"
+require "queryer/middleware/core"
+require "queryer/middleware/middleware"
+
+class Queryer
+ class QueryMaker
+ def select(env)
+ env["nk.query"].new(env["nk.connection"], env["nk.query_string"]).select
+ end
+
+ def execute(env)
+ env["nk.query"].new(env["nk.connection"], env["nk.query_string"]).execute
+ end
+ end
+end
@@ -0,0 +1,50 @@
+# This serves as the "server" for the Queryer endpoint. You can implement
+# another server to the Queryer client, so long as the endpoint receives
+# nk.connection, nk.query_string, and nk.query
+class Queryer
+ class Client
+ def initialize(pool, queryer)
+ @pool = pool
+ @queryer = queryer
+ end
+
+ def select(query_string)
+ with_connection(query_string) { |env| @queryer.select(env) }
+ end
+
+ def execute(query_string)
+ with_connection(query_string) { |env| @queryer.execute(env) }
+ end
+
+ def transaction
+ @pool.with_connection { |connection| yield InTransaction.new(connection, @queryer) }
+ end
+
+ def with_connection(query_string)
+ @pool.with_connection { |connection| yield env(connection, query_string) }
+ end
+
+ def env(connection, query_string)
+ {
+ "nk.connection" => connection,
+ "nk.query_string" => query_string,
+ "nk.query" => Query
+ }
+ end
+
+ class InTransaction < Queryer::Client
+ def initialize(connection, queryer)
+ @connection = connection
+ @queryer = queryer
+ end
+
+ def transaction
+ yield self
+ end
+
+ def with_connection(query_string)
+ yield env(@connection, query_string)
+ end
+ end
+ end
+end
@@ -0,0 +1,15 @@
+class Queryer
+ class AlternateStatsCollector < Middleware
+ def delegate(method, env)
+ env["stats.instance"].measure(method) { super }
+ end
+ end
+
+ class AlternateTimeout < Middleware
+ def delegate(method, env)
+ result = ::Timeout.timeout(env["timeout.duration"]) { super }
+ puts "Did not timeout! Yay fast database!"
+ result
+ end
+ end
+end
@@ -0,0 +1,33 @@
+class Queryer
+ class Builder
+ def self.configure(queryer, &block)
+ queryer = new(queryer)
+ queryer.instance_eval(&block)
+ queryer
+ end
+
+ def initialize(queryer)
+ @queryer = queryer
+ @middlewares = []
+ end
+
+ def use(middleware, *args)
+ @middlewares << [middleware, args]
+ end
+
+ def reverse!
+ @middlewares = @middlewares.reverse
+ self
+ end
+
+ def build
+ queryer = @queryer
+
+ @middlewares.each do |klass, args|
+ queryer = klass.new(queryer, *args)
+ end
+
+ queryer
+ end
+ end
+end
@@ -0,0 +1,21 @@
+require "timeout"
+
+class Queryer
+ class Middleware
+ def initialize(queryer)
+ @queryer = queryer
+ end
+
+ def select(env)
+ delegate(:select, env)
+ end
+
+ def execute(env)
+ delegate(:execute, env)
+ end
+
+ def delegate(method, env)
+ @queryer.send(method, env)
+ end
+ end
+end
@@ -0,0 +1,56 @@
+class Queryer
+ class StatsCollector < Middleware
+ def initialize(queryer, stats)
+ super(queryer)
+ @stats = stats
+ end
+
+ def delegate(method, env)
+ @stats.measure(method) { super }
+ end
+ end
+
+ class Timeout < Middleware
+ def initialize(queryer, timeout)
+ super(queryer)
+ @timeout = timeout
+ end
+
+ def delegate(method, env)
+ result = ::Timeout.timeout(@timeout) { super }
+ puts "Did not timeout! Yay fast database!"
+ result
+ end
+ end
+
+ class Memoizing < Middleware
+ class QueryFactory
+ @memo = {}
+ def self.for(query)
+ @memo[query] ||= new(query)
+ end
+
+ def initialize(query)
+ @query = query
+ @memo = {}
+ end
+
+ def new(conn, query_string)
+ @memo[[conn, query_string]] ||= begin
+ puts "Instantiating Query Object"
+ @query.new(conn, query_string)
+ end
+ end
+ end
+
+ def initialize(queryer)
+ super(queryer)
+ @wrap_memo = {}
+ end
+
+ def delegate(method, env)
+ env["nk.query"] = Memoizing::QueryFactory.for(env["nk.query"])
+ super
+ end
+ end
+end
@@ -0,0 +1,43 @@
+require "benchmark"
+
+# This code was mostly provided by Nick for the challenge. I'm using it
+# unaltered here, as though it was an external library
+
+class ConnectionPool
+ def initialize(size)
+ @size = size
+ end
+
+ def with_connection
+ yield Object.new
+ end
+end
+
+class Stats
+ def measure(name)
+ result = nil
+ bm = Benchmark.measure { result = yield }
+ puts "Measured #{name} at #{"%.2f" % bm.real} seconds"
+ result
+ end
+end
+
+class Query
+ def initialize(connection, query_string, *args)
+ @connection = connection
+ @query_string = query_string
+ @args = args
+ end
+
+ def select
+ sleep 1
+ puts "Selecting #{@query_string} on #{@connection}"
+ [1, 2, 3]
+ end
+
+ def execute
+ sleep 1
+ puts "Executing #{@query_string} on #{@connection}"
+ 1
+ end
+end
23 test.rb
@@ -0,0 +1,23 @@
+$:.push File.dirname(__FILE__) + "/lib"
+
+require "queryer"
+
+configured_builder = Queryer::Builder.configure(Queryer::QueryMaker.new) do
+ use Queryer::Memoizing
+ use Queryer::Timeout, 1.1
+ use Queryer::StatsCollector, Stats.new
+end
+
+forward = configured_builder.build
+backward = configured_builder.dup.reverse!.build
+
+[forward, backward].each do |direction|
+ client = Queryer::Client.new(ConnectionPool.new(20), direction)
+
+ client.transaction do |t|
+ t.select("SELECT ... FROM ... FOR UPDATE ...")
+ t.execute("INSERT ...")
+ t.execute("INSERT ...")
+ end
+ puts
+end

0 comments on commit 91e8b74

Please sign in to comment.