Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial version; work in progress; mysql2 should mostly work; simple …
…smoke test
- Loading branch information
qertoip
committed
Feb 5, 2012
0 parents
commit 01ea3f9
Showing
29 changed files
with
512 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
*.gem | ||
.bundle | ||
Gemfile.lock | ||
pkg/* | ||
.idea | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
source "http://rubygems.org" | ||
|
||
# Specify your gem's dependencies in kontolib.gemspec | ||
gemspec | ||
|
||
group :test do | ||
# Use the gem instead of a dated version bundled with Ruby | ||
gem 'minitest', '2.8.1' | ||
|
||
# Allows to test for method invocations. Used sparingly but indispensable | ||
gem 'mocha', :require => false | ||
|
||
gem 'activerecord', "3.1.3" | ||
|
||
gem 'simplecov', :require => false | ||
|
||
# TDD (watch for file changes and fire the test) | ||
gem 'watchr' | ||
gem 'rev' | ||
|
||
gem 'mysql2' | ||
gem 'pg' | ||
gem 'sqlite3' | ||
end | ||
|
||
group :development do | ||
gem 'rake' | ||
# enhance irb | ||
gem 'awesome_print', :require => false | ||
gem 'pry', :require => false | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require "bundler/gem_tasks" | ||
|
||
require 'rake/testtask' | ||
|
||
Rake::TestTask.new do |t| | ||
t.libs += ["test", "lib"] | ||
t.pattern = 'test/smoke/**/*_test.rb' | ||
t.verbose = true | ||
end | ||
|
||
task :default => [:test] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
bundle exec ruby test/test_console.rb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Require version statement | ||
|
||
require_relative 'transaction_isolation/version' | ||
|
||
# Require modules with ActiveRecord enhancements | ||
|
||
def apply_transaction_isolation_patch | ||
require 'active_record' | ||
require_relative 'transaction_isolation/active_record/errors' | ||
require_relative 'transaction_isolation/active_record/base' | ||
require_relative 'transaction_isolation/active_record/connection_adapters/abstract_adapter' | ||
require_relative 'transaction_isolation/active_record/connection_adapters/mysql2_adapter' | ||
require_relative 'transaction_isolation/active_record/connection_adapters/postgresql_adapter' | ||
require_relative 'transaction_isolation/active_record/connection_adapters/sqlite3_adapter' | ||
end | ||
|
||
$stderr.puts( "transaction_isolation" ) | ||
|
||
if defined?( Rails ) | ||
Rails.application.config.after_initialize do | ||
$stderr.puts( "apply_transaction_isolation_patch" ) | ||
apply_transaction_isolation_patch | ||
end | ||
else | ||
apply_transaction_isolation_patch | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
require 'active_record/base' | ||
|
||
module TransactionIsolation | ||
module ActiveRecord | ||
module Base | ||
def isolation_level( isolation_level, &block ) | ||
connection.isolation_level( isolation_level, &block ) | ||
end | ||
end | ||
end | ||
end | ||
|
||
ActiveRecord::Base.extend( TransactionIsolation::ActiveRecord::Base ) |
32 changes: 32 additions & 0 deletions
32
lib/transaction_isolation/active_record/connection_adapters/abstract_adapter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
require 'active_record/connection_adapters/abstract_adapter' | ||
|
||
module TransactionIsolation | ||
module ActiveRecord | ||
module ConnectionAdapters # :nodoc: | ||
module AbstractAdapter | ||
|
||
VALID_ISOLATION_LEVELS = [:read_uncommitted, :read_committed, :repeatable_read, :serializable] | ||
|
||
# If true, #isolation_level(level) method is available | ||
def supports_isolation_levels? | ||
false | ||
end | ||
|
||
def isolation_level( level ) | ||
raise NotImplementedError | ||
end | ||
|
||
private | ||
|
||
def validate_isolation_level( isolation_level ) | ||
unless VALID_ISOLATION_LEVELS.include?( isolation_level ) | ||
raise ArgumentError, "Invalid isolation level '#{isolation_level}'. Supported levels include #{VALID_ISOLATION_LEVELS.join( ', ' )}." | ||
end | ||
end | ||
|
||
end | ||
end | ||
end | ||
end | ||
|
||
ActiveRecord::ConnectionAdapters::AbstractAdapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::AbstractAdapter ) |
64 changes: 64 additions & 0 deletions
64
lib/transaction_isolation/active_record/connection_adapters/mysql2_adapter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
if defined?( ActiveRecord::ConnectionAdapters::Mysql2Adapter ) | ||
|
||
#require 'active_record/connection_adapters/mysql2_adapter' | ||
|
||
module TransactionIsolation | ||
module ActiveRecord | ||
module ConnectionAdapters # :nodoc: | ||
module Mysql2Adapter | ||
|
||
def included( base ) | ||
base.class_eval do | ||
alias_method :translate_exception_without_transaction_isolation_conflict, :translate_exception | ||
alias_method :translate_exception, :translate_exception_with_transaction_isolation_conflict | ||
end | ||
end | ||
|
||
def supports_isolation_levels? | ||
true | ||
end | ||
|
||
VENDOR_SPECIFIC_ISOLATION_LEVELS = { | ||
:read_uncommitted => 'READ UNCOMMITTED', | ||
:read_committed => 'READ COMMITTED', | ||
:repeatable_read => 'REPEATABLE READ', | ||
:serializable => 'SERIALIZABLE' | ||
} | ||
|
||
def translate_exception_with_transaction_isolation_conflict( exception, message ) | ||
if tx_isolation_conflict?( exception ) | ||
::ActiveRecord::TransactionIsolationConflict.new( "Transaction isolation conflict detected: #{exception.message}", exception ) | ||
else | ||
translate_exception_without_transaction_isolation_conflict( exception, message ) | ||
end | ||
end | ||
|
||
def tx_isolation_conflict?( exception ) | ||
[ "Deadlock found when trying to get lock", | ||
"Lock wait timeout exceeded"].any? do |error_message| | ||
exception.message =~ /#{Regexp.escape( error_message )}/ | ||
end | ||
end | ||
|
||
def isolation_level( level ) | ||
validate_isolation_level( level ) | ||
|
||
original_isolation = select_value( "select @@session.tx_isolation" ).gsub( '-', ' ' ) | ||
|
||
execute( "SET SESSION TRANSACTION ISOLATION LEVEL #{VENDOR_SPECIFIC_ISOLATION_LEVELS[level]}" ) | ||
|
||
begin | ||
yield | ||
ensure | ||
execute "SET SESSION TRANSACTION ISOLATION LEVEL #{original_isolation}" | ||
end if block_given? | ||
end | ||
|
||
end | ||
end | ||
end | ||
end | ||
|
||
ActiveRecord::ConnectionAdapters::Mysql2Adapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::Mysql2Adapter ) | ||
|
||
end |
45 changes: 45 additions & 0 deletions
45
lib/transaction_isolation/active_record/connection_adapters/postgresql_adapter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
if defined?( ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ) | ||
|
||
#require 'active_record/connection_adapters/postgresql_adapter' | ||
|
||
module TransactionIsolation | ||
module ActiveRecord | ||
module ConnectionAdapters # :nodoc: | ||
module PostgreSQLAdapter | ||
|
||
def supports_isolation_levels? | ||
true | ||
end | ||
|
||
VENDOR_SPECIFIC_ISOLATION_LEVELS = { | ||
:read_uncommitted => 'READ UNCOMMITTED', | ||
:read_committed => 'READ COMMITTED', | ||
:repeatable_read => 'REPEATABLE READ', | ||
:serializable => 'SERIALIZABLE' | ||
} | ||
|
||
TRANSACTION_CONFLICT_ERRORS = [ | ||
"deadlock detected", | ||
"could not serialize access" | ||
] | ||
|
||
def isolation_level( level ) | ||
validate_isolation_level( level ) | ||
|
||
execute "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL #{VENDOR_SPECIFIC_ISOLATION_LEVELS[level]}" | ||
|
||
begin | ||
yield | ||
ensure | ||
execute "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL #{'READ COMMITTED'}" | ||
end if block_given? | ||
end | ||
|
||
end | ||
end | ||
end | ||
end | ||
|
||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ) | ||
|
||
end |
44 changes: 44 additions & 0 deletions
44
lib/transaction_isolation/active_record/connection_adapters/sqlite3_adapter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
if defined?( ActiveRecord::ConnectionAdapters::SQLiteAdapter ) | ||
|
||
#require 'active_record/connection_adapters/sqlite3_adapter' | ||
|
||
module TransactionIsolation | ||
module ActiveRecord | ||
module ConnectionAdapters # :nodoc: | ||
module SQLite3Adapter | ||
|
||
def supports_isolation_levels? | ||
true | ||
end | ||
|
||
VENDOR_SPECIFIC_ISOLATION_LEVELS = { | ||
:read_uncommitted => 'read_uncommitted = true', | ||
:read_committed => 'read_uncommitted = false', | ||
:repeatable_read => 'read_uncommitted = false', | ||
:serializable => 'read_uncommitted = false' | ||
} | ||
|
||
TRANSACTION_CONFLICT_ERRORS = [ | ||
"is locked" | ||
] | ||
|
||
def isolation_level( level ) | ||
validate_isolation_level( level ) | ||
|
||
execute "PRAGMA #{VENDOR_SPECIFIC_ISOLATION_LEVELS[level]}" | ||
|
||
begin | ||
yield | ||
ensure | ||
execute "PRAGMA #{VENDOR_SPECIFIC_ISOLATION_LEVELS[initial_isolation_level]}" | ||
end if block_given? | ||
end | ||
|
||
end | ||
end | ||
end | ||
end | ||
|
||
ActiveRecord::ConnectionAdapters::SQLite3Adapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::SQLite3Adapter ) | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
require 'active_record/errors' | ||
|
||
module TransactionIsolation | ||
module ActiveRecord | ||
# This exception represents both deadlocks and serialization conflicts. | ||
# Deadlocks happen when db engine is using lock-based concurrency control. | ||
# Serialization conflicts happen when db engine is using multi-version concurrency control. | ||
# Often db engines combine both approaches and thus generate both types of errors. | ||
|
||
class TransactionIsolationConflict < ::ActiveRecord::WrappedDatabaseException; end | ||
end | ||
end | ||
|
||
ActiveRecord.send( :include, TransactionIsolation::ActiveRecord ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# All monkey patching is done in this file | ||
|
||
ActiveRecord.send( :include, TransactionIsolation::ActiveRecord ) | ||
ActiveRecord::Base.extend( TransactionIsolation::ActiveRecord::Base ) | ||
ActiveRecord::ConnectionAdapters::AbstractAdapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::AbstractAdapter ) | ||
ActiveRecord::ConnectionAdapters::Mysql2Adapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::Mysql2Adapter ) | ||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ) | ||
ActiveRecord::ConnectionAdapters::SQLite3Adapter.send( :include, TransactionIsolation::ActiveRecord::ConnectionAdapters::SQLite3Adapter ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module TransactionIsolation | ||
VERSION = "0.9" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
require 'active_record' | ||
require_relative 'db' | ||
require_relative 'migrations' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
require 'fileutils' | ||
|
||
module TransactionIsolation | ||
module Test | ||
module Db | ||
|
||
def self.connect_to_mysql2 | ||
::ActiveRecord::Base.establish_connection( | ||
:adapter => "mysql2", | ||
:database => "transaction_isolation_test", #database_filepath | ||
:user => 'root', | ||
:password => '' | ||
) | ||
end | ||
|
||
def self.connect_to_postgresql | ||
::ActiveRecord::Base.establish_connection( | ||
:adapter => "postgresql", | ||
:database => "transaction_isolation_test", #database_filepath | ||
:user => 'qertoip', | ||
:password => 'test123' | ||
) | ||
end | ||
|
||
def self.connect_to_sqlite3 | ||
ActiveRecord::Base.establish_connection( | ||
:adapter => "sqlite3", | ||
:database => ":memory:", | ||
:verbosity => "silent" | ||
) | ||
end | ||
|
||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module TransactionIsolation | ||
module Test | ||
module Migrations | ||
|
||
def self.run! | ||
c = ::ActiveRecord::Base.connection | ||
|
||
# Queued Jobs | ||
|
||
c.create_table "queued_jobs", :force => true do |t| | ||
t.text "job", :null => false | ||
t.integer "status", :default => 0, :null => false | ||
t.timestamps | ||
end | ||
|
||
end | ||
|
||
end | ||
end | ||
end |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Prepares application to be tested (requires files, connects to db, resets schema and data, applies patches, etc.) | ||
|
||
# Initialize database | ||
require 'db/all' | ||
|
||
case ENV['db'] | ||
when 'mysql2' | ||
TransactionIsolation::Test::Db.connect_to_mysql2 | ||
when 'postgresql' | ||
TransactionIsolation::Test::Db.connect_to_postgresql | ||
when 'sqlite3' | ||
TransactionIsolation::Test::Db.connect_to_sqlite3 | ||
else | ||
TransactionIsolation::Test::Db.connect_to_mysql2 | ||
end | ||
|
||
TransactionIsolation::Test::Migrations.run! | ||
|
||
# Load the code that will be tested | ||
require 'transaction_isolation' |
Empty file.
Oops, something went wrong.