Repository: kovyrin/db-charmer
Branch: master
Commit: 0c71563472ed
Files: 160
Total size: 297.8 KB
Directory structure:
gitextract_9iqagwm9/
├── .gitignore
├── .travis.yml
├── CHANGES
├── LICENSE
├── Makefile
├── README.rdoc
├── Rakefile
├── ci_build
├── db-charmer.gemspec
├── init.rb
├── issues/
│ └── issues-as-of-2014-11-14.json
├── lib/
│ ├── db_charmer/
│ │ ├── action_controller/
│ │ │ └── force_slave_reads.rb
│ │ ├── active_record/
│ │ │ ├── association_preload.rb
│ │ │ ├── class_attributes.rb
│ │ │ ├── connection_switching.rb
│ │ │ ├── db_magic.rb
│ │ │ ├── migration/
│ │ │ │ └── multi_db_migrations.rb
│ │ │ ├── multi_db_proxy.rb
│ │ │ └── sharding.rb
│ │ ├── connection_factory.rb
│ │ ├── connection_proxy.rb
│ │ ├── core_extensions.rb
│ │ ├── force_slave_reads.rb
│ │ ├── rails2/
│ │ │ ├── abstract_adapter/
│ │ │ │ └── log_formatting.rb
│ │ │ └── active_record/
│ │ │ ├── master_slave_routing.rb
│ │ │ └── named_scope/
│ │ │ └── scope_proxy.rb
│ │ ├── rails3/
│ │ │ ├── abstract_adapter/
│ │ │ │ └── connection_name.rb
│ │ │ └── active_record/
│ │ │ ├── log_subscriber.rb
│ │ │ ├── master_slave_routing.rb
│ │ │ ├── relation/
│ │ │ │ └── connection_routing.rb
│ │ │ └── relation_method.rb
│ │ ├── rails31/
│ │ │ └── active_record/
│ │ │ ├── migration/
│ │ │ │ └── command_recorder.rb
│ │ │ └── preloader/
│ │ │ ├── association.rb
│ │ │ └── has_and_belongs_to_many.rb
│ │ ├── railtie.rb
│ │ ├── sharding/
│ │ │ ├── connection.rb
│ │ │ ├── method/
│ │ │ │ ├── db_block_group_map.rb
│ │ │ │ ├── db_block_map.rb
│ │ │ │ ├── hash_map.rb
│ │ │ │ └── range.rb
│ │ │ ├── method.rb
│ │ │ └── stub_connection.rb
│ │ ├── sharding.rb
│ │ ├── tasks/
│ │ │ └── databases.rake
│ │ ├── version.rb
│ │ └── with_remapped_databases.rb
│ └── db_charmer.rb
├── test-project/
│ ├── .gitignore
│ ├── .rspec
│ ├── Gemfile
│ ├── Rakefile
│ ├── TODO
│ ├── app/
│ │ ├── controllers/
│ │ │ ├── application_controller.rb
│ │ │ └── posts_controller.rb
│ │ ├── helpers/
│ │ │ └── application_helper.rb
│ │ ├── models/
│ │ │ ├── avatar.rb
│ │ │ ├── car.rb
│ │ │ ├── categories_posts.rb
│ │ │ ├── category.rb
│ │ │ ├── comment.rb
│ │ │ ├── event.rb
│ │ │ ├── ford.rb
│ │ │ ├── house.rb
│ │ │ ├── log_record.rb
│ │ │ ├── post.rb
│ │ │ ├── range_sharded_model.rb
│ │ │ ├── toyota.rb
│ │ │ └── user.rb
│ │ └── views/
│ │ ├── layouts/
│ │ │ └── application.html.erb
│ │ └── posts/
│ │ ├── index.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── config/
│ │ ├── application.rb
│ │ ├── boot.rb
│ │ ├── database.yml.example
│ │ ├── environment.rb
│ │ ├── environments/
│ │ │ └── test.rb
│ │ ├── initializers/
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── db_charmer.rb
│ │ │ ├── secret_token.rb
│ │ │ ├── session_store.rb
│ │ │ └── sharding.rb
│ │ ├── locales/
│ │ │ └── en.yml
│ │ └── routes.rb
│ ├── db/
│ │ ├── create_databases.sql
│ │ ├── migrate/
│ │ │ ├── 20090810013829_create_log_records.rb
│ │ │ ├── 20090810013922_create_posts.rb
│ │ │ ├── 20090810221944_create_users.rb
│ │ │ ├── 20100305234245_create_categories.rb
│ │ │ ├── 20100305234340_create_categories_posts.rb
│ │ │ ├── 20100305235831_create_avatars.rb
│ │ │ ├── 20100328201317_create_sharding_map_tables.rb
│ │ │ ├── 20100330180517_create_event_tables.rb
│ │ │ ├── 20100817191548_create_cars.rb
│ │ │ └── 20111005193941_create_comments.rb
│ │ ├── seeds.rb
│ │ └── sharding.sql
│ └── spec/
│ ├── controllers/
│ │ └── posts_controller_spec.rb
│ ├── fixtures/
│ │ ├── avatars.yml
│ │ ├── categories.yml
│ │ ├── categories_posts.yml
│ │ ├── comments.yml
│ │ ├── event_shards_info.yml
│ │ ├── event_shards_map.yml
│ │ ├── log_records.yml
│ │ ├── posts.yml
│ │ └── users.yml
│ ├── integration/
│ │ └── multi_threading_spec.rb
│ ├── models/
│ │ ├── avatar_spec.rb
│ │ ├── cars_spec.rb
│ │ ├── categories_posts_spec.rb
│ │ ├── category_spec.rb
│ │ ├── comment_spec.rb
│ │ ├── event_spec.rb
│ │ ├── log_record_spec.rb
│ │ ├── post_spec.rb
│ │ ├── range_sharded_model_spec.rb
│ │ └── user_spec.rb
│ ├── sharding/
│ │ ├── connection_spec.rb
│ │ ├── method/
│ │ │ ├── db_block_map_spec.rb
│ │ │ ├── hash_map_spec.rb
│ │ │ └── range_spec.rb
│ │ └── sharding_spec.rb
│ ├── spec_helper.rb
│ ├── support/
│ │ └── rails31_stub_connection.rb
│ └── unit/
│ ├── abstract_adapter/
│ │ └── log_formatting_spec.rb
│ ├── action_controller/
│ │ └── force_slave_reads_spec.rb
│ ├── active_record/
│ │ ├── association_preload_spec.rb
│ │ ├── association_proxy_spec.rb
│ │ ├── class_attributes_spec.rb
│ │ ├── connection_switching_spec.rb
│ │ ├── db_magic_spec.rb
│ │ ├── master_slave_routing_spec.rb
│ │ ├── migration/
│ │ │ └── multi_db_migrations_spec.rb
│ │ ├── named_scope/
│ │ │ └── named_scope_spec.rb
│ │ └── relation_spec.rb
│ ├── connection_factory_spec.rb
│ ├── connection_proxy_spec.rb
│ ├── db_charmer_spec.rb
│ ├── multi_db_proxy_spec.rb
│ └── with_remapped_databases_spec.rb
└── test-project-2.x/
├── Gemfile
├── Rakefile
├── config/
│ ├── boot.rb
│ ├── database.yml.example
│ ├── environment.rb
│ ├── environments/
│ │ └── test.rb
│ ├── initializers/
│ │ ├── backtrace_silencers.rb
│ │ ├── db_charmer.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ ├── new_rails_defaults.rb
│ │ ├── session_store.rb
│ │ └── sharding.rb
│ ├── locales/
│ │ └── en.yml
│ ├── preinitializer.rb
│ └── routes.rb
├── script/
│ └── console
└── spec/
├── spec.opts
└── spec_helper.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
doc
pkg
.DS_Store
_site
.idea
================================================
FILE: .travis.yml
================================================
language: ruby
rvm:
- 1.8.7
- 1.9.3
- 2.0.0
env:
- RAILS_VERSION=2.x
- RAILS_VERSION=3.0.20
- RAILS_VERSION=3.1.12
- RAILS_VERSION=3.2.3
- RAILS_VERSION=3.2.15
- RAILS_VERSION=3.2.15 DB_CHARMER_GEM=1.9.0
notifications:
recipients:
- alexey@kovyrin.net
script: ./ci_build
# Whitelist branches to test
branches:
only:
- master
- rails4
# Build matrix configuration
matrix:
exclude:
# Do not run Rails 2.x tests on ruby 1.9
- rvm: 1.9.3
env: RAILS_VERSION=2.x
# Do not run Rails 2.x tests on ruby 2.0
- rvm: 2.0.0
env: RAILS_VERSION=2.x
# Do not run Rails 3.0 tests on ruby 2.0
- rvm: 2.0.0
env: RAILS_VERSION=3.0.20
# Do not run Rails 3.1 tests on ruby 2.0
- rvm: 2.0.0
env: RAILS_VERSION=3.1.12
# Do not run early Rails 3.2 tests on ruby 2.0
- rvm: 2.0.0
env: RAILS_VERSION=3.2.3
================================================
FILE: CHANGES
================================================
1.9.1 (2014-11-14):
The project has been suspended. No updates will be provided and no Rails versions
beyond 3.2.x will be supported.
For more information please check out this blog post: http://kovyrin.net/2014/11/14/dbcharmer-suspended/
----------------------------------------------------------------------------------------
1.9.0 (2013-10-09):
Most of the major changes in this version are related to our initial push towards making
DbCharmer thread-safe and making sure it works correctly in multi-threaded environments.
Please note, that even though we now test DbCharmer in multi-threaded environments, we still
consider multi-threaded support experimental.
Bug fix: Improved Rails environment detection (sometimes DbCharmer would use
Rails-specific code while running in non-rails projects).
Bug fix: Make sure on_db() method could restore original connection after an exception is
raised from a DB driver during connection switching (Thanks to Dmytro Shteflyuk for finding
the issue and helping with debugging).
This is the first release that does not have a really strict constraint for point-releases
within the Rails 3.2.x branch.
----------------------------------------------------------------------------------------
1.8.4 (2013-03-18):
Bumped up rails dependencies up to 3.2.13.
----------------------------------------------------------------------------------------
1.8.3 (2013-02-11):
Bumped up rails dependencies up to 3.2.12.
----------------------------------------------------------------------------------------
1.8.2 (2013-01-11):
Bumped up rails dependencies up to 3.2.11.
----------------------------------------------------------------------------------------
1.8.1 (2013-01-02):
Bumped up rails dependencies up to 3.2.10.
----------------------------------------------------------------------------------------
1.8.0 (2012-11-12):
Added support for Rails versions up to 3.2.9. Please note, that Rails 3.2.4 is not officially
supported. Your code may work on that version, but no bug reports will be accepted about this
version.
Tests for DbCharmer have been moved to the gem repository to make Travis-CI integration more
stable. We do not put test files into the gem inself so it should not be a problem for most
of the users. If you still use db-charmer as a plugin, please switch to gem mode.
----------------------------------------------------------------------------------------
1.7.1 (2012-04-22):
Beta feature: Rails 3.1 and 3.2 support
Thanks to the community (and Eugene Pimenov aka @libc in particular) we now have support
for Rails versions up to 3.2.1, including new migrations classes.
----------------------------------------------------------------------------------------
1.7.0 (2011-08-29):
Beta feature: Rails 3 support
Beta feature: Added force_slave_reads functionality. Now we could have models with slaves
that are not used by default, but could be turned on globally (per-controller or per-action).
Heavily reorganized the source code to match Rails code structure (class names, etc). This should
make it much easier for other contributors to work with the code.
Added smarter environment detection (using Rails.env, RAILS_ENV or RACK_ENV).
Changed dependencies a bit: instead of depending on rails, we now require specific components
(ActiveRecord, ActiveSupport, etc) + we do not require blankslate gem anymore.
Bugfixes: Fix for N+1 queries when accessing shard_info for db_block_group_map sharding method.
----------------------------------------------------------------------------------------
1.6.17-19 (2011-04-25):
Bugfixes: Do not touch database for sharded models until we really need to. Before 1.6.17
if a database server was dead and there were any other connection issues, class loading
in Ruby would be broken and sharded model class would not be initialized.
----------------------------------------------------------------------------------------
1.6.14 (2011-01-09):
Bugfixes: We do not support Rails 3, and now we prohibit any versions but 2.2 and 2.3 from
being used with db-charmer gem.
----------------------------------------------------------------------------------------
1.6.13 (2010-08-17):
Starting with this version we use Rails.env instead of RAILS_ENV to auto-detect rails
environment. If you use DbCharmer in non-rails project, please set DbCharmer.env manually.
Bugfixes: Thanks to Eric Lindvall we now allow connection names that have symbols ruby
wouldn't like for class names.
----------------------------------------------------------------------------------------
1.6.12 (2010-05-09):
Starting with this version we use Rails.cache (memcache or whatever you use in your project)
to cache sharding blocks information.
Bugfixes: Thanks to Allen Madsen (github user blatyo) we've fixed a few minor issues in
database connections handling.
----------------------------------------------------------------------------------------
1.6.11 (2010-04-16):
Bugfix: Change the way we allocate sharding blocks in block map sharding method to
prevent race-conditions from happening on block to shard assignments.
Breaking change: We require connections to exist by default in all connection factory
methods. If you need old behavior, pass should_exist=false explicitly.
----------------------------------------------------------------------------------------
1.6.10 (2010-04-09):
Multi-Db migrations changed. Now it is possible to call ActiveRecord::Migration.db_magic
and specify default migration connection that would be used by all migrations without
excplicitly switched connections.
----------------------------------------------------------------------------------------
1.6.9 (2010-04-08):
Bugfix release: now DbCharmer works without Rails.
----------------------------------------------------------------------------------------
1.6.7 (2010-04-07):
Changed the way we handle associations in on_db(:foo).find(:include) calls. Now we
switch association's connection only if its default connection is the same as the
master model's connection (not more "table does not exist" problems I hope).
----------------------------------------------------------------------------------------
1.6.5 (2010-04-05):
Bugfix release: Fixed :connection vs :slave in db_magic behaviour. Model.on_master should
run queries on the master, not on AR's default connection.
----------------------------------------------------------------------------------------
1.6.4 (2010-04-05):
Default behaviour changed: DbCharmer.connections_should_exist is true in all environments
by default. Old default behaviour was too misleading for many developers.
----------------------------------------------------------------------------------------
1.6.3 (2010-04-03):
Bugfix release: Modified stub connection initialization code to set default connections
for sharded models using shards enumeration or default shard features of sharding methods.
----------------------------------------------------------------------------------------
1.6.2 (2010-04-03):
Bugfix release: Modified our stub connection used on sharded models to fail on db-calling
methods only. Proxy the rest to a real shard connection. Another bug fixed in db_block_map
sharding method: we didn't increment block counters when assigning blocks to shards.
----------------------------------------------------------------------------------------
1.6.1 (2010-03-31):
Breaking change from now on all connection-switching methods (both in migrations and in
models) are controlled by a single option DbCharmer.connections_should_exist. This
option is false by default in all non-production environments. Check out README for
more details.
----------------------------------------------------------------------------------------
1.6.0 (2010-03-31):
The major (and arguably the only noticeable) change in this version is our simple database
sharding support. The feature is still in alpha stage and should not be used in production
without complete understanding of the principles of its work.
----------------------------------------------------------------------------------------
1.5.5 (2010-03-15):
Thanks to ngmoco.com (http://github.com/ngmoco) now DbCharmer supports one more use-case
for multi-db migrations. Now you can run the same migration on many databases at once.
For example, the following migration would create test_table on all three shard databases:
class MultiDbTest < ActiveRecord::Migration
db_magic :connections => [ :shard01, :shard02, :shard03 ]
def self.up
create_table :test_table do |t|
t.string :test_string
t.timestamps
end
end
def self.down
drop_table :test_table
end
end
----------------------------------------------------------------------------------------
1.5.4 (2010-03-12):
Added DbCharmer.with_remapped_databases, so that you can change the connection for
many models simultaneously, and implicitly. Very useful for work where you want to use
a particular slave for a whole range of database access.
----------------------------------------------------------------------------------------
1.5.3 (2010-03-10):
Few changes:
* Colorized connection names in the logs for development mode
* We do not log connection names when connection does not exist
----------------------------------------------------------------------------------------
1.5.1 (2010-03-06):
In this version we've added support for connection names logging in Rails queries log.
New log records have [connection_name] prefix for all queries that are executed on
non-standard connections:
[logs] LogRecord Columns (1.1ms) SHOW FIELDS FROM `log_records`
[logs] User Delete all (0.1ms) DELETE FROM `users`
[slave01] User Load (0.2ms) SELECT * FROM `users` WHERE (`users`.`login` = 'foo')
----------------------------------------------------------------------------------------
1.4.6 -> 1.5.0 (2010-03-05):
Major change in this version of DbCharmer is association preload support. For example,
let's say we have a schema:
class Post < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :posts
end
Now, if we have the following call in our code:
User.on_db(:foo).all(:include => :posts)
In 1.4.6 it would load the users from connection :foo and posts from the
default connection, which is not what we would expect from this line of code.
So, starting 1.5.0 all finder calls on models having :include parameter would
switch associated models' connections to the same connection as the main model
in the call.
================================================
FILE: LICENSE
================================================
The MIT License
Copyright (c) 2011, Oleksiy Kovyrin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: Makefile
================================================
doc/files/README_rdoc.html: README.rdoc
rdoc README.rdoc
================================================
FILE: README.rdoc
================================================
= WARNING: The Project Has Been Suspended
Please note, that this project has been suspended. No updates will be provided and no Rails versions
beyond 3.2.x will be supported. For more information please check out this blog post: http://kovyrin.net/2014/11/14/dbcharmer-suspended/
= DB Charmer - ActiveRecord Connection Magic Plugin
+DbCharmer+ is a simple yet powerful plugin for ActiveRecord that significantly extends its ability to work with
multiple databases and/or database servers. The major features we add to ActiveRecord are:
1. Simple management for AR model connections (+switch_connection_to+ method)
2. Switching of default AR model connections to separate servers/databases
3. Ability to easily choose where your query should go (Model.on_* methods family)
4. Automated master/slave queries routing (selects go to a slave, updates handled by the master).
5. Multiple database migrations with very flexible query routing controls.
6. Simple database sharding functionality with multiple sharding methods (value, range, mapping table).
For more information on the project, you can check out our web site at http://kovyrin.github.io/db-charmer/.
== Installation
There are two options when approaching +DbCharmer+ installation:
* using the gem (recommended and the only way of using it with Rails 3.2+)
* install as a Rails plugin (works in Rails 2.x only)
To install as a gem, add this to your Gemfile:
gem 'db-charmer', :require => 'db_charmer'
To install +DbCharmer+ as a Rails plugin use the following command:
./script/plugin install git://github.com/kovyrin/db-charmer.git
_Notice_: If you use +DbCharmer+ in a non-rails project, you may need to set DbCharmer.env to a correct value
before using any of its connection management methods. Correct value here is a valid database.yml
first-level section name.
== Documentation/Questions
For more information about the library, please visit our site at http://dbcharmer.net.
If you need more defails on DbCharmer internals, please check out the source code. All the plugin's
code is ~100% covered with tests. The project located in test-project directory has unit
tests for all or, at least, the most actively used code paths.
If you have any questions regarding this project, you could contact the author using
the DbCharmer Users Group mailing list:
- Group Info: http://groups.google.com/group/db-charmer
- Subscribe using the info page or by sending an email to mailto:db-charmer-subscribe@googlegroups.com
== What Ruby and Rails implementations does it work for?
We have a continuous integration setup for this gem on with Rails 2.3, 3.0, 3.1 and 3.2 using a few
different versions of Ruby.
CI is running on TravisCI.org: https://travis-ci.org/kovyrin/db-charmer
Build status is: {
}[https://travis-ci.org/kovyrin/db-charmer]
At the moment we have the following build matrix:
* Rails versions:
- 2.3
- 3.0
- 3.1
- 3.2
* Ruby versions:
- 1.8.7
- 1.9.3 (Rails 3.0+ only)
- 2.0.0 (Rails 3.2+ only)
* Databases:
- MySQL
In addition to CI testing, this gem is used in production on Scribd.com (one of the largest RoR
sites in the world) with Ruby Enterprise Edition and Rails 2.2, Rails 2.3, Sinatra and plain
Rack applications.
Starting with version 1.8.0 we support Rails versions 3.2.8 and higher. Please note, that Rails 3.2.4
is not officially supported. Your code may work on that version, but no bug reports will be
accepted about this version.
== Is it Thread-Safe?
Starting with version 1.9.0 we have started working on making the code thread-safe and making sure
DbCharmer works correctly in multi-threaded environments. At this moment we consider multi-threaded
mode experimental. If you use it and it works for you - please let us know, if it does not - please
make sure to file a ticket so that we could improve the code and make it work in your situation.
== Who are the authors?
This plugin has been created in Scribd.com for our internal use and then the sources were opened for
other people to use. Most of the code in this package has been developed by Oleksiy Kovyrin for
Scribd.com and is released under the MIT license. For more details, see the LICENSE file.
Other contributors who have helped with the development of this library are (alphabetically ordered):
* Allen Madsen
* Andrew Geweke
* Ashley Martens
* Cauê Guerra
* David Dai
* Dmytro Shteflyuk
* Eric Lindvall
* Eugene Pimenov
* Jonathan Viney
* Gregory Man
* Michael Birk
* Tyler McMullen
================================================
FILE: Rakefile
================================================
require 'rake'
require 'bundler'
Bundler::GemHelper.install_tasks
================================================
FILE: ci_build
================================================
#!/bin/bash
# Making the script more robust
set -e # Exit on errors
set -u # Exit on uninitialized variables
RAILS_VERSION=${RAILS_VERSION:-}
if [ "$RAILS_VERSION" == "" ]; then
echo "Please specify rails version using RAILS_VERSION environment variable!"
exit 1
fi
# Change directory according to the rails version
if [ "$RAILS_VERSION" == "2.x" ]; then
# Downgrade rubygems because rails 2.3 does not work on 2.0+
gem update --system 1.8.25
cd test-project-2.x
else
cd test-project
fi
# Print version info
echo "-----------------------------------------------------------------------------------------------------------------"
echo " * Running specs for Rails version $RAILS_VERSION..."
echo " * Ruby version: `ruby --version`"
echo " * Rubygems version: `gem --version`"
echo " * DbCharmer gem version: '${DB_CHARMER_GEM:-trunk}'"
echo "-----------------------------------------------------------------------------------------------------------------"
# Test environment
export RAILS_ENV=test
# Configure database access
cp -f config/database.yml.example config/database.yml
# Create databases and sharding tables
mysql -u root < db/create_databases.sql
mysql -u root db_charmer_sandbox_test < db/sharding.sql
# Install gems
rm -f Gemfile.lock
bundle install
# Run migrations
bundle exec rake --trace db:migrate
# Run the build and return its exit code
if [ "$RAILS_VERSION" == "2.x" ]; then
exec bundle exec spec -p '/*/**/*_spec.rb' -cbfs spec
else
exec bundle exec rspec -cbfs spec
fi
================================================
FILE: db-charmer.gemspec
================================================
# -*- encoding: utf-8 -*-
$:.push File.expand_path('../lib', __FILE__)
require 'db_charmer/version'
Gem::Specification.new do |s|
s.name = 'db-charmer'
s.version = DbCharmer::Version::STRING
s.platform = Gem::Platform::RUBY
s.authors = [ 'Oleksiy Kovyrin' ]
s.email = 'alexey@kovyrin.net'
s.homepage = 'http://kovyrin.github.io/db-charmer/'
s.summary = 'ActiveRecord Connections Magic (slaves, multiple connections, etc)'
s.description = 'DbCharmer is a Rails plugin (and gem) that could be used to manage AR model connections, implement master/slave query schemes, sharding and other magic features many high-scale applications need.'
s.license = 'MIT'
s.rdoc_options = [ '--charset=UTF-8' ]
s.files = Dir['lib/**/*'] + Dir['*.rb']
s.files += %w[ README.rdoc LICENSE CHANGES ]
s.require_paths = [ 'lib' ]
s.extra_rdoc_files = [ 'LICENSE', 'README.rdoc' ]
# Dependencies
s.add_dependency 'activesupport', '< 4.0.0'
s.add_dependency 'activerecord', '< 4.0.0'
s.add_development_dependency 'rspec'
s.add_development_dependency 'yard'
s.add_development_dependency 'actionpack'
end
================================================
FILE: init.rb
================================================
require 'db_charmer'
================================================
FILE: issues/issues-as-of-2014-11-14.json
================================================
[
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/98",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/98/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/98/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/98/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/98",
"id": 45164122,
"number": 98,
"title": "Rails 4 blocker update_all may ignore condition with db_charmer !!",
"user": {
"login": "AvnerCohen",
"id": 1297254,
"avatar_url": "https://avatars.githubusercontent.com/u/1297254?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/AvnerCohen",
"html_url": "https://github.com/AvnerCohen",
"followers_url": "https://api.github.com/users/AvnerCohen/followers",
"following_url": "https://api.github.com/users/AvnerCohen/following{/other_user}",
"gists_url": "https://api.github.com/users/AvnerCohen/gists{/gist_id}",
"starred_url": "https://api.github.com/users/AvnerCohen/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/AvnerCohen/subscriptions",
"organizations_url": "https://api.github.com/users/AvnerCohen/orgs",
"repos_url": "https://api.github.com/users/AvnerCohen/repos",
"events_url": "https://api.github.com/users/AvnerCohen/events{/privacy}",
"received_events_url": "https://api.github.com/users/AvnerCohen/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2014-10-07T20:42:29Z",
"updated_at": "2014-10-07T20:42:29Z",
"closed_at": null,
"body": "First thing first, thanks for making this gem and putting the effort into it! :bow: \r\n...\r\n\r\nTo the issue, it might just be that this is a combination of an old code being upgraded from Rails 3 to Rails 4, But it is extremely dangerous.\r\n\r\nConsider the following console code:\r\n\r\n````ruby\r\n› rails c\r\nLoading development environment (Rails 4.1.6)\r\n2.1.2 :001 > Test.update_all({updated_at: Time.now}, {somevalue: 123})\r\nArgumentError: wrong number of arguments (2 for 1)\r\n from ./gems/activerecord-4.1.6/lib/active_record/relation.rb:316:in `update_all'\r\n from ./bundler/gems/db-charmer-cb40007c36e2/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:128:in `block in update_all_with_db_charmer'\r\n from ./bundler/gems/db-charmer-cb40007c36e2/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:103:in `block in switch_connection_for_method'\r\n from ./bundler/gems/db-charmer-cb40007c36e2/lib/db_charmer/active_record/multi_db_proxy.rb:37:in `on_db'\r\n from ./bundler/gems/db-charmer-cb40007c36e2/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:102:in `switch_connection_for_method'\r\n from ./bundler/gems/db-charmer-cb40007c36e2/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:127:in `update_all_with_db_charmer'\r\n from ./gems/activerecord-4.1.6/lib/active_record/querying.rb:8:in `update_all'\r\n from (irb):1\r\n from ./gems/railties-4.1.6/lib/rails/commands/console.rb:90:in `start'\r\n from ./gems/railties-4.1.6/lib/rails/commands/console.rb:9:in `start'\r\n from ./gems/railties-4.1.6/lib/rails/commands/commands_tasks.rb:69:in `console'\r\n from ./gems/railties-4.1.6/lib/rails/commands/commands_tasks.rb:40:in `run_command!'\r\n from ./gems/railties-4.1.6/lib/rails/commands.rb:17:in `'\r\n from bin/rails:8:in `require'\r\n from bin/rails:8:in `'\r\n2.1.2 :002 > Test.update_all({updated_at: Time.now})\r\n SQL (0.5ms) UPDATE \"tests\" SET \"updated_at\" = '2014-10-07 20:34:07.694313'\r\n => 0\r\n2.1.2 :003 > Test.update_all({updated_at: Time.now}, {somevalue: 123})\r\n SQL (0.2ms) UPDATE \"tests\" SET \"updated_at\" = '2014-10-07 20:34:12.975116'\r\n => 0\r\n\r\n````\r\nGemfile is simply:\r\n\r\n`````ruby\r\nsource 'https://rubygems.org'\r\n\r\ngem 'rails', '4.1.6'\r\n\r\ngem 'sqlite3'\r\n\r\ngem 'db-charmer',\r\n :git => 'git@github.com:kovyrin/db-charmer.git',\r\n :branch => 'rails4', :ref => 'cb40007c36e2847850a37a490850cfd822016c5f', :require => 'db_charmer'\r\n````\r\n\r\n\r\n\r\nDescribing the issue:\r\n\r\n1. Calling update_all with wrong params (arity changed to 1 on rails 4) failes as expected.\r\n2. Enough to have a single update_all that passed succesfully.\r\n3. Next execution of wrong params (as [1] above) passes succesfully and arity check is ignored + conditions are totally ignored."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/97",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/97/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/97/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/97/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/97",
"id": 38187982,
"number": 97,
"title": "DB connections not closing properly",
"user": {
"login": "nchafai",
"id": 436617,
"avatar_url": "https://avatars.githubusercontent.com/u/436617?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/nchafai",
"html_url": "https://github.com/nchafai",
"followers_url": "https://api.github.com/users/nchafai/followers",
"following_url": "https://api.github.com/users/nchafai/following{/other_user}",
"gists_url": "https://api.github.com/users/nchafai/gists{/gist_id}",
"starred_url": "https://api.github.com/users/nchafai/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/nchafai/subscriptions",
"organizations_url": "https://api.github.com/users/nchafai/orgs",
"repos_url": "https://api.github.com/users/nchafai/repos",
"events_url": "https://api.github.com/users/nchafai/events{/privacy}",
"received_events_url": "https://api.github.com/users/nchafai/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 2,
"created_at": "2014-07-18T15:47:56Z",
"updated_at": "2014-07-21T07:17:26Z",
"closed_at": "2014-07-21T07:16:55Z",
"body": "Hi,\r\n\r\nWe are using db-charmer to handle distributed data accross multiple databases. However, connections are not closing properly when requests are made on a sharded database : hundreds of connections remain opened. \r\n\r\nAfter investigations, this situation is due to `abstract_connection_class_name` used to reference connections in `ConnectionPool` list : as long as different threads occur, a new connection is added in the pool list since `abstract_connection_class_name` is using the Thread id : \r\n```ruby\r\ndef self.abstract_connection_class_name(connection_name)\r\n conn_name_klass = connection_name.to_s.gsub(/\\W+/, '_').camelize\r\n thread = Thread.current.object_id.abs # need to make sure it is non-negative\r\n \"::AutoGeneratedAbstractConnectionClass#{conn_name_klass}ForThread#{thread}\"\r\nend\r\n```\r\n\r\nIf we just use ` \"::AutoGeneratedAbstractConnectionClass#{conn_name_klass}ForThread\"` without the `#{thread}` suffix part, everything looks fine, and connections are properly closed. \r\n\r\nSo, what is the exact purpose of having introduce this `#{thread}` suffix ? (this suffix is not present in the previous versions of db-charmer)"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/96",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/96/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/96/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/96/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/96",
"id": 35993889,
"number": 96,
"title": "[Rails 4] rake db:migrate:redo doesn't redo all migrations",
"user": {
"login": "akshetpandey",
"id": 1213060,
"avatar_url": "https://avatars.githubusercontent.com/u/1213060?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/akshetpandey",
"html_url": "https://github.com/akshetpandey",
"followers_url": "https://api.github.com/users/akshetpandey/followers",
"following_url": "https://api.github.com/users/akshetpandey/following{/other_user}",
"gists_url": "https://api.github.com/users/akshetpandey/gists{/gist_id}",
"starred_url": "https://api.github.com/users/akshetpandey/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/akshetpandey/subscriptions",
"organizations_url": "https://api.github.com/users/akshetpandey/orgs",
"repos_url": "https://api.github.com/users/akshetpandey/repos",
"events_url": "https://api.github.com/users/akshetpandey/events{/privacy}",
"received_events_url": "https://api.github.com/users/akshetpandey/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2014-06-18T14:58:18Z",
"updated_at": "2014-07-08T00:00:58Z",
"closed_at": "2014-07-08T00:00:58Z",
"body": "I am using the rails 4 branch with rails 4. The migrations work fine but when I try to redo the migrations, only the last two migrations are picked for redo."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/95",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/95/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/95/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/95/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/95",
"id": 35756037,
"number": 95,
"title": "Rails 4.1+",
"user": {
"login": "richpeck",
"id": 1104431,
"avatar_url": "https://avatars.githubusercontent.com/u/1104431?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/richpeck",
"html_url": "https://github.com/richpeck",
"followers_url": "https://api.github.com/users/richpeck/followers",
"following_url": "https://api.github.com/users/richpeck/following{/other_user}",
"gists_url": "https://api.github.com/users/richpeck/gists{/gist_id}",
"starred_url": "https://api.github.com/users/richpeck/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/richpeck/subscriptions",
"organizations_url": "https://api.github.com/users/richpeck/orgs",
"repos_url": "https://api.github.com/users/richpeck/repos",
"events_url": "https://api.github.com/users/richpeck/events{/privacy}",
"received_events_url": "https://api.github.com/users/richpeck/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 7,
"created_at": "2014-06-15T19:23:32Z",
"updated_at": "2014-11-03T15:25:13Z",
"closed_at": null,
"body": "Any chance we could get an updated ver to work with ActiveSupport 4.1+? \r\n\r\nIt's not compatible with the latest version of Rails otherwise :("
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/94",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/94/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/94/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/94/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/94",
"id": 28744882,
"number": 94,
"title": "Fix #93 Keep slave connections thread local.",
"user": {
"login": "gworley3",
"id": 809976,
"avatar_url": "https://avatars.githubusercontent.com/u/809976?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/gworley3",
"html_url": "https://github.com/gworley3",
"followers_url": "https://api.github.com/users/gworley3/followers",
"following_url": "https://api.github.com/users/gworley3/following{/other_user}",
"gists_url": "https://api.github.com/users/gworley3/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gworley3/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gworley3/subscriptions",
"organizations_url": "https://api.github.com/users/gworley3/orgs",
"repos_url": "https://api.github.com/users/gworley3/repos",
"events_url": "https://api.github.com/users/gworley3/events{/privacy}",
"received_events_url": "https://api.github.com/users/gworley3/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2014-03-04T21:57:32Z",
"updated_at": "2014-03-04T21:57:32Z",
"closed_at": null,
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/94",
"html_url": "https://github.com/kovyrin/db-charmer/pull/94",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/94.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/94.patch"
},
"body": "This is probably not an ideal fix for #93 but it seems to work."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/93",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/93/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/93/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/93/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/93",
"id": 28739552,
"number": 93,
"title": "Multi-threaded race conditions with Postgres",
"user": {
"login": "gworley3",
"id": 809976,
"avatar_url": "https://avatars.githubusercontent.com/u/809976?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/gworley3",
"html_url": "https://github.com/gworley3",
"followers_url": "https://api.github.com/users/gworley3/followers",
"following_url": "https://api.github.com/users/gworley3/following{/other_user}",
"gists_url": "https://api.github.com/users/gworley3/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gworley3/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gworley3/subscriptions",
"organizations_url": "https://api.github.com/users/gworley3/orgs",
"repos_url": "https://api.github.com/users/gworley3/repos",
"events_url": "https://api.github.com/users/gworley3/events{/privacy}",
"received_events_url": "https://api.github.com/users/gworley3/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2014-03-04T20:52:19Z",
"updated_at": "2014-07-09T00:38:31Z",
"closed_at": null,
"body": "Trying to use db-charmer 1.9.0 with Postgres and rails 3.2.17. Db-charmer works fine with this setup when single threaded. Running multiple threads via Sidekiq, I get the following error:\r\n\r\n```\r\n2014-03-04T20:28:37Z 40445 TID-ox250opo8 WARN: undefined method `fields' for nil:NilClass\r\n2014-03-04T20:28:37Z 40445 TID-ox250opo8 WARN: /Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/postgresql_adapter.rb:664:in `block in exec_query'\r\n```\r\n\r\nI believe I have traced this down to the way `PostgresqlAdapter` works in Rails. If you dig around in its code you'll find that it makes async calls to execute queries and expects no other query to execute on the same connection while the current query is executing. If another query comes along they end up in a race condition where one query gets a result (possibly the wrong one!) and the other gets nil.\r\n\r\nThis suggests db-charmer needs to do a better job of locking connections to particular threads so that a connection is not used by more than one thread at a time.\r\n\r\nFull stack trace for the curious:\r\n\r\n```\r\n2014-03-04T20:28:37Z 40445 TID-ox250opo8 WARN: {\"retry\"=>5, \"queue\"=>\"low_linkedin_metrics\", \"failures\"=>\"exhausted\", \"class\"=>\"MetricsWorker::Linkedin::ImportAdMetrics\", \"args\"=>[1, \"2013-02-09..2013-02-09\"], \"jid\"=>\"e968f2b5935d884385911a83\", \"enqueued_at\"=>1393960671.074003, \"error_message\"=>\"undefined method `fields' for nil:NilClass\", \"error_class\"=>\"NoMethodError\", \"failed_at\"=>1393964917.8832488, \"retry_count\"=>0}\r\n2014-03-04T20:28:37Z 40445 TID-ox250opo8 WARN: undefined method `fields' for nil:NilClass\r\n2014-03-04T20:28:37Z 40445 TID-ox250opo8 WARN: /Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/postgresql_adapter.rb:664:in `block in exec_query'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activesupport-3.2.17/lib/active_support/notifications/instrumenter.rb:20:in `instrument'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/abstract_adapter/connection_name.rb:14:in `instrument'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/instrumentation/active_record.rb:46:in `block in log_with_newrelic_instrumentation'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/method_tracer.rb:271:in `trace_execution_scoped'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/instrumentation/active_record.rb:43:in `log_with_newrelic_instrumentation'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/postgresql_adapter.rb:659:in `exec_query'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/postgresql_adapter.rb:1263:in `select'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/querying.rb:38:in `block in find_by_sql'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/explain.rb:41:in `logging_query_plan'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/querying.rb:37:in `find_by_sql'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/master_slave_routing.rb:13:in `block in find_by_sql'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:71:in `first_level_on_slave'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/master_slave_routing.rb:12:in `find_by_sql'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/method_tracer.rb:521:in `block in find_by_sql_with_trace_ActiveRecord_self_name_find_by_sql'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/method_tracer.rb:271:in `trace_execution_scoped'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/method_tracer.rb:516:in `find_by_sql_with_trace_ActiveRecord_self_name_find_by_sql'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation.rb:171:in `exec_queries'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation.rb:160:in `block in to_a'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/explain.rb:34:in `logging_query_plan'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation.rb:159:in `to_a'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:123:in `block in to_a_with_db_charmer'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:113:in `block in switch_connection_for_method'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:69:in `block in first_level_on_slave'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:40:in `on_db'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:59:in `on_slave'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:69:in `first_level_on_slave'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:112:in `switch_connection_for_method'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation/connection_routing.rb:122:in `to_a_with_db_charmer'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation/finder_methods.rb:381:in `find_first'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation/finder_methods.rb:122:in `first'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation/finder_methods.rb:267:in `find_by_attributes'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/dynamic_matchers.rb:50:in `method_missing'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation/delegation.rb:14:in `block in find_by_id'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation.rb:241:in `block in scoping'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/scoping.rb:98:in `with_scope'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation.rb:241:in `scoping'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/activerecord-3.2.17/lib/active_record/relation/delegation.rb:14:in `find_by_id'\r\n/Users/gworley3/github/adstage/adstage-platform-v2/app/workers/metrics_worker/linkedin/import_ad_metrics.rb:9:in `block in perform'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/db-charmer-1.9.0/lib/db_charmer/force_slave_reads.rb:50:in `force_slave_reads'\r\n/Users/gworley3/github/adstage/adstage-platform-v2/app/workers/metrics_worker/linkedin/import_ad_metrics.rb:8:in `perform'\r\n(eval):3:in `block in perform_with_newrelic_transaction_trace'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:318:in `perform_action_with_newrelic_trace'\r\n(eval):2:in `perform_with_newrelic_transaction_trace'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/processor.rb:49:in `block (3 levels) in process'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:122:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:122:in `block in invoke'\r\n/Users/gworley3/github/adstage/adstage-platform-v2/config/initializers/sidekiq.rb:9:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/instrumentation/sidekiq.rb:25:in `block in call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:318:in `perform_action_with_newrelic_trace'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/newrelic_rpm-3.6.4.122/lib/new_relic/agent/instrumentation/sidekiq.rb:21:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-unique-jobs-2.7.1/lib/sidekiq-unique-jobs/middleware/server/unique_jobs.rb:14:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-failures-0.3.0/lib/sidekiq/failures/middleware.rb:10:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-pro-1.4.3/lib/sidekiq/batch/middleware.rb:26:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/server/active_record.rb:6:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/server/retry_jobs.rb:62:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/server/logging.rb:11:in `block in call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/logging.rb:22:in `with_context'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/server/logging.rb:7:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:124:in `block in invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:127:in `call'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/middleware/chain.rb:127:in `invoke'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/processor.rb:48:in `block (2 levels) in process'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/processor.rb:105:in `stats'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/processor.rb:47:in `block in process'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/processor.rb:86:in `do_defer'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/sidekiq-2.17.6/lib/sidekiq/processor.rb:37:in `process'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `public_send'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `dispatch'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/calls.rb:122:in `dispatch'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/actor.rb:322:in `block in handle_message'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/actor.rb:416:in `block in task'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/tasks.rb:55:in `block in initialize'\r\n/Users/gworley3/.rvm/gems/ruby-2.0.0-p247@platform-v2/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:13:in `block in create'\r\n```"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/92",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/92/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/92/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/92/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/92",
"id": 28669704,
"number": 92,
"title": "habtm associations support broken in rails 4 branch.",
"user": {
"login": "bobbarjung",
"id": 1243200,
"avatar_url": "https://avatars.githubusercontent.com/u/1243200?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/bobbarjung",
"html_url": "https://github.com/bobbarjung",
"followers_url": "https://api.github.com/users/bobbarjung/followers",
"following_url": "https://api.github.com/users/bobbarjung/following{/other_user}",
"gists_url": "https://api.github.com/users/bobbarjung/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bobbarjung/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bobbarjung/subscriptions",
"organizations_url": "https://api.github.com/users/bobbarjung/orgs",
"repos_url": "https://api.github.com/users/bobbarjung/repos",
"events_url": "https://api.github.com/users/bobbarjung/events{/privacy}",
"received_events_url": "https://api.github.com/users/bobbarjung/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 6,
"created_at": "2014-03-04T00:18:47Z",
"updated_at": "2014-07-29T14:40:22Z",
"closed_at": null,
"body": "Hi I have a connection to a recovery database and a habtm relationship between two models.\r\n\r\n```ruby\r\nclass Line\r\n has_and_belongs_to_many: nodes\r\nend\r\n\r\nclass Node\r\n has_and_belongs_to_many :lines\r\nend\r\n```\r\n\r\nI could access these relationships on the secondary database through db_charmer magic.\r\n\r\n```ruby\r\na = Node.on_db(:recovery).first\r\na.on_db(:recovery).lines.each {|b| < do stuff with b> }\r\n```\r\n\r\nBut with rails 4 branch of db_charmer gem (2.0.0.dev1)\r\n\r\nI simply get the following \r\n\r\n```\r\nirb(main):034:0> a.on_db(:recovery).lines\r\nirb(main):034:0># ActiveRecord::Associations::CollectionProxy []\r\n```\r\n\r\nAny interim way to get around this issue will also be appreciated."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/91",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/91/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/91/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/91/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/91",
"id": 28574942,
"number": 91,
"title": "can't query against the default db on_db(:default)",
"user": {
"login": "ohadpartuck",
"id": 2236337,
"avatar_url": "https://avatars.githubusercontent.com/u/2236337?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/ohadpartuck",
"html_url": "https://github.com/ohadpartuck",
"followers_url": "https://api.github.com/users/ohadpartuck/followers",
"following_url": "https://api.github.com/users/ohadpartuck/following{/other_user}",
"gists_url": "https://api.github.com/users/ohadpartuck/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ohadpartuck/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ohadpartuck/subscriptions",
"organizations_url": "https://api.github.com/users/ohadpartuck/orgs",
"repos_url": "https://api.github.com/users/ohadpartuck/repos",
"events_url": "https://api.github.com/users/ohadpartuck/events{/privacy}",
"received_events_url": "https://api.github.com/users/ohadpartuck/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2014-03-02T07:33:20Z",
"updated_at": "2014-03-02T09:53:04Z",
"closed_at": "2014-03-02T09:52:28Z",
"body": "```ruby \r\ndevelopment: &development\r\n database: app_dev\r\n host: masterdb\r\n <<: *defaults\r\n\r\n slave:\r\n <<: *defaults\r\n host: slavedb01\r\n```\r\n\r\nthen trying to force a query against master\r\n``` MyModel.on_db(:default) ```\r\neven tried \r\n``` MyModel.on_master ```\r\n\r\nBut there is no specific querying against any db.\r\n"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/90",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/90/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/90/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/90/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/90",
"id": 28014934,
"number": 90,
"title": "RuntimeError: can't add a new key into hash during iteration",
"user": {
"login": "mhfs",
"id": 78422,
"avatar_url": "https://avatars.githubusercontent.com/u/78422?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/mhfs",
"html_url": "https://github.com/mhfs",
"followers_url": "https://api.github.com/users/mhfs/followers",
"following_url": "https://api.github.com/users/mhfs/following{/other_user}",
"gists_url": "https://api.github.com/users/mhfs/gists{/gist_id}",
"starred_url": "https://api.github.com/users/mhfs/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/mhfs/subscriptions",
"organizations_url": "https://api.github.com/users/mhfs/orgs",
"repos_url": "https://api.github.com/users/mhfs/repos",
"events_url": "https://api.github.com/users/mhfs/events{/privacy}",
"received_events_url": "https://api.github.com/users/mhfs/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 12,
"created_at": "2014-02-21T02:38:17Z",
"updated_at": "2014-06-03T23:26:05Z",
"closed_at": null,
"body": "Hey @kovyrin ,\r\n\r\nI think I might have found a multi-threading bug. Throwing it here to see it rings any bells.\r\n\r\nI'm processing a high volume of quick sidekiq jobs and part of it is performing a query like this:\r\n\r\n```ruby\r\nUser.on_db(:bbdb).find(user_id)\r\n```\r\n\r\nIm' seeing lot's of errors like this:\r\n\r\n```\r\nRuntimeError: can't add a new key into hash during iteration\r\n```\r\nI'm using ruby 2.0.0p353, rails 3.217 and db-charmer 1.9.0 as you can see in the full backtrace below.\r\n\r\nDoes that rings any bells for you?\r\n\r\nThanks in advance.\r\n\r\n```\r\n/vendor/ruby/2.0.0/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract/connection_pool.rb:375 in \"[]=\"\r\n/vendor/ruby/2.0.0/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract/connection_pool.rb:375 in \"establish_connection\"\r\n/vendor/ruby/2.0.0/gems/activerecord-3.2.17/lib/active_record/connection_adapters/abstract/connection_specification.rb:137 in \"establish_connection\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/active_record/connection_switching.rb:25 in \"establish_real_connection_if_exists\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/connection_factory.rb:51 in \"generate_abstract_class\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/connection_factory.rb:35 in \"establish_connection\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/connection_factory.rb:24 in \"connect\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/active_record/connection_switching.rb:66 in \"coerce_to_connection_proxy\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/active_record/connection_switching.rb:82 in \"switch_connection_to\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:39 in \"on_db\"\r\n/vendor/ruby/2.0.0/gems/db-charmer-1.9.0/lib/db_charmer/active_record/multi_db_proxy.rb:19 in \"method_missing\"\r\n/app/jobs/backfill_stats_job.rb:7 in \"perform\"\r\n```"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/89",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/89/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/89/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/89/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/89",
"id": 27979122,
"number": 89,
"title": "force_slave_reads option ignored",
"user": {
"login": "gworley3",
"id": 809976,
"avatar_url": "https://avatars.githubusercontent.com/u/809976?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/gworley3",
"html_url": "https://github.com/gworley3",
"followers_url": "https://api.github.com/users/gworley3/followers",
"following_url": "https://api.github.com/users/gworley3/following{/other_user}",
"gists_url": "https://api.github.com/users/gworley3/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gworley3/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gworley3/subscriptions",
"organizations_url": "https://api.github.com/users/gworley3/orgs",
"repos_url": "https://api.github.com/users/gworley3/repos",
"events_url": "https://api.github.com/users/gworley3/events{/privacy}",
"received_events_url": "https://api.github.com/users/gworley3/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2014-02-20T17:19:06Z",
"updated_at": "2014-02-20T17:41:46Z",
"closed_at": "2014-02-20T17:41:46Z",
"body": "I set `:force_slave_reads => false` in the `db_magic` opts but still reads from slaves by default."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/88",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/88/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/88/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/88/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/88",
"id": 24576212,
"number": 88,
"title": "Skip HasAndBelongsToMany preloader for Rails 4.1",
"user": {
"login": "kmcbride",
"id": 597435,
"avatar_url": "https://avatars.githubusercontent.com/u/597435?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/kmcbride",
"html_url": "https://github.com/kmcbride",
"followers_url": "https://api.github.com/users/kmcbride/followers",
"following_url": "https://api.github.com/users/kmcbride/following{/other_user}",
"gists_url": "https://api.github.com/users/kmcbride/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kmcbride/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kmcbride/subscriptions",
"organizations_url": "https://api.github.com/users/kmcbride/orgs",
"repos_url": "https://api.github.com/users/kmcbride/repos",
"events_url": "https://api.github.com/users/kmcbride/events{/privacy}",
"received_events_url": "https://api.github.com/users/kmcbride/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2013-12-19T18:31:26Z",
"updated_at": "2014-06-11T19:21:31Z",
"closed_at": "2014-06-11T19:21:31Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/88",
"html_url": "https://github.com/kovyrin/db-charmer/pull/88",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/88.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/88.patch"
},
"body": "See: https://github.com/rails/rails/commit/a03ea3ff97b43340d0904525083bf8bc7a1c6ebc"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/87",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/87/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/87/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/87/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/87",
"id": 24129460,
"number": 87,
"title": "Make database rake auto load on rails",
"user": {
"login": "arthurnn",
"id": 833383,
"avatar_url": "https://avatars.githubusercontent.com/u/833383?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/arthurnn",
"html_url": "https://github.com/arthurnn",
"followers_url": "https://api.github.com/users/arthurnn/followers",
"following_url": "https://api.github.com/users/arthurnn/following{/other_user}",
"gists_url": "https://api.github.com/users/arthurnn/gists{/gist_id}",
"starred_url": "https://api.github.com/users/arthurnn/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/arthurnn/subscriptions",
"organizations_url": "https://api.github.com/users/arthurnn/orgs",
"repos_url": "https://api.github.com/users/arthurnn/repos",
"events_url": "https://api.github.com/users/arthurnn/events{/privacy}",
"received_events_url": "https://api.github.com/users/arthurnn/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 3,
"created_at": "2013-12-11T19:02:34Z",
"updated_at": "2013-12-12T23:09:55Z",
"closed_at": "2013-12-12T23:08:46Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/87",
"html_url": "https://github.com/kovyrin/db-charmer/pull/87",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/87.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/87.patch"
},
"body": "@kovyrin \r\n\r\nWe need to load the rake task on `Rails::Railtie`, otherwise they wont be available when just adding the Gem.\r\n\r\nlet me know what you think.\r\n\r\ncheers,"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/86",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/86/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/86/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/86/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/86",
"id": 23877630,
"number": 86,
"title": "Allow AR::B.db_magic m/s split to work with abstract AR classes.",
"user": {
"login": "perplexes",
"id": 13812,
"avatar_url": "https://avatars.githubusercontent.com/u/13812?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/perplexes",
"html_url": "https://github.com/perplexes",
"followers_url": "https://api.github.com/users/perplexes/followers",
"following_url": "https://api.github.com/users/perplexes/following{/other_user}",
"gists_url": "https://api.github.com/users/perplexes/gists{/gist_id}",
"starred_url": "https://api.github.com/users/perplexes/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/perplexes/subscriptions",
"organizations_url": "https://api.github.com/users/perplexes/orgs",
"repos_url": "https://api.github.com/users/perplexes/repos",
"events_url": "https://api.github.com/users/perplexes/events{/privacy}",
"received_events_url": "https://api.github.com/users/perplexes/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2013-12-06T18:56:28Z",
"updated_at": "2013-12-06T18:56:28Z",
"closed_at": null,
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/86",
"html_url": "https://github.com/kovyrin/db-charmer/pull/86",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/86.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/86.patch"
},
"body": "Hi, ran into a SystemStackError. We wanted to split all reads in all models to slaves. The easiest way to do this seemed like:\r\n\r\nIn config/initializers/db_charmer.rb:\r\n```ruby\r\nActiveRecord::Base.db_magic(slave: :main_slave)\r\n```\r\n\r\nBut we also have Rails Engines that have their own version of an AR::B-like class with a separate connection, called base:\r\n\r\n```ruby\r\nclass IsbnDb::Base < ActiveRecord::Base\r\n self.abstract_class = true\r\n db_magic connection: :isbn_master, slave: :isbn_slave\r\nend\r\n```\r\n\r\nThen models underneath that would inherit this connection:\r\n```ruby\r\nclass Isbn < IsbnDb::Base\r\nend\r\n```\r\n\r\nWhat we were seeing were SystemStackErrors:\r\n\r\n```ruby\r\n[3] pry(main)> Isbn.first\r\nSystemStackError: stack level too deep\r\n```\r\n\r\nAfter a lot of tracing (and set_trace_func), the culprit is that putting db_magic on ActiveRecord::Base overrides the default behavior of the master/slave automatic methods like reload, find_by_sql and count_by_sql. In models that directly inherit from AR::B (like User, say) this behavior is fine - but in level-2 inherited models (AR::B > Base > Isbn), is causes infinite recursion.\r\n\r\nThis pull request fixes this situation, but I don't quite know how to test it."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/85",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/85/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/85/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/85/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/85",
"id": 22639819,
"number": 85,
"title": "Rails4 Support",
"user": {
"login": "kovyrin",
"id": 3467,
"avatar_url": "https://avatars.githubusercontent.com/u/3467?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/kovyrin",
"html_url": "https://github.com/kovyrin",
"followers_url": "https://api.github.com/users/kovyrin/followers",
"following_url": "https://api.github.com/users/kovyrin/following{/other_user}",
"gists_url": "https://api.github.com/users/kovyrin/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kovyrin/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kovyrin/subscriptions",
"organizations_url": "https://api.github.com/users/kovyrin/orgs",
"repos_url": "https://api.github.com/users/kovyrin/repos",
"events_url": "https://api.github.com/users/kovyrin/events{/privacy}",
"received_events_url": "https://api.github.com/users/kovyrin/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 16,
"created_at": "2013-11-14T02:58:39Z",
"updated_at": "2014-10-22T09:35:58Z",
"closed_at": null,
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/85",
"html_url": "https://github.com/kovyrin/db-charmer/pull/85",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/85.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/85.patch"
},
"body": "This PR is used to track the process of adding Rails 4 support to DbCharmer. It is not ready yet, but we are really close."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/84",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/84/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/84/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/84/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/84",
"id": 22639397,
"number": 84,
"title": "License missing from gemspec",
"user": {
"login": "bf4",
"id": 142914,
"avatar_url": "https://avatars.githubusercontent.com/u/142914?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/bf4",
"html_url": "https://github.com/bf4",
"followers_url": "https://api.github.com/users/bf4/followers",
"following_url": "https://api.github.com/users/bf4/following{/other_user}",
"gists_url": "https://api.github.com/users/bf4/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bf4/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bf4/subscriptions",
"organizations_url": "https://api.github.com/users/bf4/orgs",
"repos_url": "https://api.github.com/users/bf4/repos",
"events_url": "https://api.github.com/users/bf4/events{/privacy}",
"received_events_url": "https://api.github.com/users/bf4/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 2,
"created_at": "2013-11-14T02:46:06Z",
"updated_at": "2013-11-14T04:18:04Z",
"closed_at": "2013-11-14T02:57:03Z",
"body": " RubyGems.org doesn't report a license for your gem. This is because it is not specified in the [gemspec](http://docs.rubygems.org/read/chapter/20#license) of your last release.\n\n via e.g.\n\n spec.license = 'MIT'\n # or\n spec.licenses = ['MIT', 'GPL-2']\n\n Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how [rubygems.org uses the gemspec to display the rails gem license](https://rubygems.org/gems/rails).\n\n There is even a [License Finder gem](https://github.com/pivotal/LicenseFinder) to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that *even Bundler now generates gems with a default 'MIT' license*.\n\n I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!\n\n Appendix:\n\n If you need help choosing a [license](http://opensource.org/licenses) (sorry, I haven't checked your readme or looked for a license file), GitHub has created a [license picker tool](http://choosealicense.com/). Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.\n Here's a [list of the license names I've found and their frequencies](https://github.com/bf4/gemproject/blob/master/license_usage.csv)\n\n p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and [make issues for gemspecs not specifying a license as a public service :)](https://github.com/bf4/gemproject/issues/1). See the previous link or my [blog post about this project for more information](http://www.benjaminfleischer.com/2013/07/12/make-the-world-a-better-place-put-a-license-in-your-gemspec/).\n"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/83",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/83/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/83/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/83/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/83",
"id": 22158156,
"number": 83,
"title": "Gemspec updates",
"user": {
"login": "bradherman",
"id": 384172,
"avatar_url": "https://avatars.githubusercontent.com/u/384172?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/bradherman",
"html_url": "https://github.com/bradherman",
"followers_url": "https://api.github.com/users/bradherman/followers",
"following_url": "https://api.github.com/users/bradherman/following{/other_user}",
"gists_url": "https://api.github.com/users/bradherman/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bradherman/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bradherman/subscriptions",
"organizations_url": "https://api.github.com/users/bradherman/orgs",
"repos_url": "https://api.github.com/users/bradherman/repos",
"events_url": "https://api.github.com/users/bradherman/events{/privacy}",
"received_events_url": "https://api.github.com/users/bradherman/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-11-05T22:50:37Z",
"updated_at": "2013-11-10T06:31:59Z",
"closed_at": "2013-11-10T06:31:59Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/83",
"html_url": "https://github.com/kovyrin/db-charmer/pull/83",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/83.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/83.patch"
},
"body": ""
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/82",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/82/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/82/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/82/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/82",
"id": 22002182,
"number": 82,
"title": "Update db-charmer.gemspec",
"user": {
"login": "yhuang",
"id": 154587,
"avatar_url": "https://avatars.githubusercontent.com/u/154587?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/yhuang",
"html_url": "https://github.com/yhuang",
"followers_url": "https://api.github.com/users/yhuang/followers",
"following_url": "https://api.github.com/users/yhuang/following{/other_user}",
"gists_url": "https://api.github.com/users/yhuang/gists{/gist_id}",
"starred_url": "https://api.github.com/users/yhuang/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/yhuang/subscriptions",
"organizations_url": "https://api.github.com/users/yhuang/orgs",
"repos_url": "https://api.github.com/users/yhuang/repos",
"events_url": "https://api.github.com/users/yhuang/events{/privacy}",
"received_events_url": "https://api.github.com/users/yhuang/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-11-02T14:09:36Z",
"updated_at": "2013-11-10T15:41:32Z",
"closed_at": "2013-11-10T15:41:32Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/82",
"html_url": "https://github.com/kovyrin/db-charmer/pull/82",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/82.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/82.patch"
},
"body": "Rails 4.0.1 has been released:\r\n\r\nhttp://weblog.rubyonrails.org/2013/11/1/Rails-4-0-1-has-been-released/"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/81",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/81/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/81/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/81/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/81",
"id": 21460804,
"number": 81,
"title": "Sharded connection key parameter meaning?",
"user": {
"login": "fabn",
"id": 324213,
"avatar_url": "https://avatars.githubusercontent.com/u/324213?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/fabn",
"html_url": "https://github.com/fabn",
"followers_url": "https://api.github.com/users/fabn/followers",
"following_url": "https://api.github.com/users/fabn/following{/other_user}",
"gists_url": "https://api.github.com/users/fabn/gists{/gist_id}",
"starred_url": "https://api.github.com/users/fabn/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/fabn/subscriptions",
"organizations_url": "https://api.github.com/users/fabn/orgs",
"repos_url": "https://api.github.com/users/fabn/repos",
"events_url": "https://api.github.com/users/fabn/events{/privacy}",
"received_events_url": "https://api.github.com/users/fabn/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2013-10-23T15:14:48Z",
"updated_at": "2013-10-23T15:14:48Z",
"closed_at": null,
"body": "In the readme there is this example for sharded connections\r\n\r\n```ruby\r\nclass Text < ActiveRecord::Base\r\n db_magic :sharded => {\r\n :key => :id,\r\n :sharded_connection => :texts\r\n }\r\nend\r\n```\r\n\r\nWhat is the meaning of `:key` parameter? I saw that it's not used at all in [setup_sharding_magic](https://github.com/kovyrin/db-charmer/blob/master/lib/db_charmer/active_record/db_magic.rb#L53) method\r\n\r\nI assumed that the usage of that field is to compute the shard before saving or when retrieving records, something like \r\n\r\n```ruby\r\nSHARDING_MAP = {\r\n 'US' => :us_users,\r\n 'CA' => :ca_users,\r\n :default => :other_users\r\n}\r\n\r\nDbCharmer::Sharding.register_connection(\r\n :name => :users,\r\n :method => :hash_map,\r\n :map => SHARDING_MAP\r\n)\r\n\r\nclass User < ActiveRecord::Base\r\n db_magic :sharded => {\r\n :key => :locale,\r\n :sharded_connection => :users\r\n }\r\nend\r\n```\r\n\r\nAnd then when creating users\r\n\r\n```ruby\r\nUser.new(locale: 'US').save # this goes to us_users shard\r\nUser.new(locale: 'CA').save # this goes to ca_users shard\r\n```\r\n\r\nBut this kind of code is not working at all, shard must be selected manually before any operation. Am I right? \r\n\r\nIn that case what is the meaning of sharding? The same effect can (almost) be achieved with normal `on_db` method calls?"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/80",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/80/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/80/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/80/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/80",
"id": 21106472,
"number": 80,
"title": "Bump rails to 3.2.15",
"user": {
"login": "arthurnn",
"id": 833383,
"avatar_url": "https://avatars.githubusercontent.com/u/833383?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/arthurnn",
"html_url": "https://github.com/arthurnn",
"followers_url": "https://api.github.com/users/arthurnn/followers",
"following_url": "https://api.github.com/users/arthurnn/following{/other_user}",
"gists_url": "https://api.github.com/users/arthurnn/gists{/gist_id}",
"starred_url": "https://api.github.com/users/arthurnn/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/arthurnn/subscriptions",
"organizations_url": "https://api.github.com/users/arthurnn/orgs",
"repos_url": "https://api.github.com/users/arthurnn/repos",
"events_url": "https://api.github.com/users/arthurnn/events{/privacy}",
"received_events_url": "https://api.github.com/users/arthurnn/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2013-10-16T20:13:02Z",
"updated_at": "2013-10-16T20:13:33Z",
"closed_at": "2013-10-16T20:13:33Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/80",
"html_url": "https://github.com/kovyrin/db-charmer/pull/80",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/80.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/80.patch"
},
"body": ""
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/79",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/79/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/79/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/79/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/79",
"id": 19604764,
"number": 79,
"title": "Connection not switching connection consistantly.",
"user": {
"login": "tysliu",
"id": 68217,
"avatar_url": "https://avatars.githubusercontent.com/u/68217?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/tysliu",
"html_url": "https://github.com/tysliu",
"followers_url": "https://api.github.com/users/tysliu/followers",
"following_url": "https://api.github.com/users/tysliu/following{/other_user}",
"gists_url": "https://api.github.com/users/tysliu/gists{/gist_id}",
"starred_url": "https://api.github.com/users/tysliu/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/tysliu/subscriptions",
"organizations_url": "https://api.github.com/users/tysliu/orgs",
"repos_url": "https://api.github.com/users/tysliu/repos",
"events_url": "https://api.github.com/users/tysliu/events{/privacy}",
"received_events_url": "https://api.github.com/users/tysliu/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 6,
"created_at": "2013-09-17T09:49:32Z",
"updated_at": "2013-11-14T03:45:53Z",
"closed_at": null,
"body": "Hi,\r\nI'm eager loading an association for a model. for example Model.where('criteria').includes(:some_association)\r\n\r\nThis association is from another database, I'm finding that if the query for Model.where('criteria') takes too long. our view will throw an error telling us ActionView::Template::Error (Mysql2::Error: Table 'database.some_association' doesn't exist.\r\n\r\nIt would be most appreciated if someone can shed some light on this issue!"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/78",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/78/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/78/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/78/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/78",
"id": 19461002,
"number": 78,
"title": "Kill rails 2 support",
"user": {
"login": "arthurnn",
"id": 833383,
"avatar_url": "https://avatars.githubusercontent.com/u/833383?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/arthurnn",
"html_url": "https://github.com/arthurnn",
"followers_url": "https://api.github.com/users/arthurnn/followers",
"following_url": "https://api.github.com/users/arthurnn/following{/other_user}",
"gists_url": "https://api.github.com/users/arthurnn/gists{/gist_id}",
"starred_url": "https://api.github.com/users/arthurnn/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/arthurnn/subscriptions",
"organizations_url": "https://api.github.com/users/arthurnn/orgs",
"repos_url": "https://api.github.com/users/arthurnn/repos",
"events_url": "https://api.github.com/users/arthurnn/events{/privacy}",
"received_events_url": "https://api.github.com/users/arthurnn/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-09-13T15:59:17Z",
"updated_at": "2013-11-10T15:44:43Z",
"closed_at": "2013-11-10T15:44:43Z",
"body": "If @kovyrin is ok with this idea, I can submit a PR to cleaning up code that is there only because of rails 2.\r\nThoughts on that?"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/77",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/77/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/77/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/77/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/77",
"id": 19411140,
"number": 77,
"title": "Rescue error on drop database on the main db",
"user": {
"login": "arthurnn",
"id": 833383,
"avatar_url": "https://avatars.githubusercontent.com/u/833383?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/arthurnn",
"html_url": "https://github.com/arthurnn",
"followers_url": "https://api.github.com/users/arthurnn/followers",
"following_url": "https://api.github.com/users/arthurnn/following{/other_user}",
"gists_url": "https://api.github.com/users/arthurnn/gists{/gist_id}",
"starred_url": "https://api.github.com/users/arthurnn/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/arthurnn/subscriptions",
"organizations_url": "https://api.github.com/users/arthurnn/orgs",
"repos_url": "https://api.github.com/users/arthurnn/repos",
"events_url": "https://api.github.com/users/arthurnn/events{/privacy}",
"received_events_url": "https://api.github.com/users/arthurnn/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-09-12T20:02:28Z",
"updated_at": "2013-10-16T20:08:52Z",
"closed_at": "2013-10-16T20:08:52Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/77",
"html_url": "https://github.com/kovyrin/db-charmer/pull/77",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/77.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/77.patch"
},
"body": "we have this type of config:\r\n\r\n```ruby\r\ndevelopment: &development\r\n database: foo\r\n\r\nbenchmark: &benchmark\r\n database: foo\r\n```\r\n\r\nwhen dropping all it throws an exception as foo was dropped already. "
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/76",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/76/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/76/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/76/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/76",
"id": 19410987,
"number": 76,
"title": "Update .travis.yml",
"user": {
"login": "arthurnn",
"id": 833383,
"avatar_url": "https://avatars.githubusercontent.com/u/833383?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/arthurnn",
"html_url": "https://github.com/arthurnn",
"followers_url": "https://api.github.com/users/arthurnn/followers",
"following_url": "https://api.github.com/users/arthurnn/following{/other_user}",
"gists_url": "https://api.github.com/users/arthurnn/gists{/gist_id}",
"starred_url": "https://api.github.com/users/arthurnn/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/arthurnn/subscriptions",
"organizations_url": "https://api.github.com/users/arthurnn/orgs",
"repos_url": "https://api.github.com/users/arthurnn/repos",
"events_url": "https://api.github.com/users/arthurnn/events{/privacy}",
"received_events_url": "https://api.github.com/users/arthurnn/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2013-09-12T19:59:31Z",
"updated_at": "2013-09-12T20:23:12Z",
"closed_at": "2013-09-12T20:22:48Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/76",
"html_url": "https://github.com/kovyrin/db-charmer/pull/76",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/76.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/76.patch"
},
"body": "use RAILS_VERSION=3.2.14 on travis."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/75",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/75/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/75/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/75/events",
"html_url": "https://github.com/kovyrin/db-charmer/pull/75",
"id": 19130612,
"number": 75,
"title": "Update rails version limits to 3.2.14",
"user": {
"login": "beedub",
"id": 102646,
"avatar_url": "https://avatars.githubusercontent.com/u/102646?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/beedub",
"html_url": "https://github.com/beedub",
"followers_url": "https://api.github.com/users/beedub/followers",
"following_url": "https://api.github.com/users/beedub/following{/other_user}",
"gists_url": "https://api.github.com/users/beedub/gists{/gist_id}",
"starred_url": "https://api.github.com/users/beedub/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/beedub/subscriptions",
"organizations_url": "https://api.github.com/users/beedub/orgs",
"repos_url": "https://api.github.com/users/beedub/repos",
"events_url": "https://api.github.com/users/beedub/events{/privacy}",
"received_events_url": "https://api.github.com/users/beedub/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-09-06T21:56:18Z",
"updated_at": "2013-09-06T21:58:53Z",
"closed_at": "2013-09-06T21:58:48Z",
"pull_request": {
"url": "https://api.github.com/repos/kovyrin/db-charmer/pulls/75",
"html_url": "https://github.com/kovyrin/db-charmer/pull/75",
"diff_url": "https://github.com/kovyrin/db-charmer/pull/75.diff",
"patch_url": "https://github.com/kovyrin/db-charmer/pull/75.patch"
},
"body": ""
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/74",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/74/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/74/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/74/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/74",
"id": 16779471,
"number": 74,
"title": "hasone relationship does not seem to work with db-charmer",
"user": {
"login": "bobbarjung",
"id": 1243200,
"avatar_url": "https://avatars.githubusercontent.com/u/1243200?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/bobbarjung",
"html_url": "https://github.com/bobbarjung",
"followers_url": "https://api.github.com/users/bobbarjung/followers",
"following_url": "https://api.github.com/users/bobbarjung/following{/other_user}",
"gists_url": "https://api.github.com/users/bobbarjung/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bobbarjung/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bobbarjung/subscriptions",
"organizations_url": "https://api.github.com/users/bobbarjung/orgs",
"repos_url": "https://api.github.com/users/bobbarjung/repos",
"events_url": "https://api.github.com/users/bobbarjung/events{/privacy}",
"received_events_url": "https://api.github.com/users/bobbarjung/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 0,
"created_at": "2013-07-15T21:16:41Z",
"updated_at": "2014-06-11T21:06:07Z",
"closed_at": null,
"body": "I have a has_one relationship between A and B\r\n\r\n```ruby\r\nclass A < ActiveRecord::Base\r\n has_one b\r\nend\r\n\r\nclass B < ActiveRecord::Base\r\n belongs_to a\r\nend\r\n```\r\n\r\nI want to access the associations on a secondary db. (Called recovery) using db-charmer.\r\n\r\n```ruby\r\nb = B.on_db(:recovery).find(b_id) # works\r\nb.on_db(:recovery).a # works. a is not nil.\r\n\r\na = A.on_db(:recovery).find(a_id) # works.\r\na.on_db(:recovery).b # does not work,\r\n```\r\n...as it tries to lookup the main db and not the recovery database for the reverse lookup. I am guessing has_many also will not work? I am using db-charmer version 1.8.4.\r\n\r\nThank you for your help."
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/73",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/73/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/73/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/73/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/73",
"id": 16121766,
"number": 73,
"title": "Multi-Threading: with_remapped_databases fails to remap classes",
"user": {
"login": "chadrem",
"id": 22150,
"avatar_url": "https://avatars.githubusercontent.com/u/22150?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/chadrem",
"html_url": "https://github.com/chadrem",
"followers_url": "https://api.github.com/users/chadrem/followers",
"following_url": "https://api.github.com/users/chadrem/following{/other_user}",
"gists_url": "https://api.github.com/users/chadrem/gists{/gist_id}",
"starred_url": "https://api.github.com/users/chadrem/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/chadrem/subscriptions",
"organizations_url": "https://api.github.com/users/chadrem/orgs",
"repos_url": "https://api.github.com/users/chadrem/repos",
"events_url": "https://api.github.com/users/chadrem/events{/privacy}",
"received_events_url": "https://api.github.com/users/chadrem/received_events",
"type": "User",
"site_admin": false
},
"labels": [
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/labels/reproducible-issue",
"name": "reproducible-issue",
"color": "e10c02"
}
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 4,
"created_at": "2013-06-28T00:27:00Z",
"updated_at": "2014-06-11T21:01:35Z",
"closed_at": "2014-06-11T21:01:35Z",
"body": "I started digging into the problem and noticed that new threads are unable to find the map. Because of this, no classes get re-mapped and thus look for data in the wrong database.\r\n\r\n#### Main thread works correctly (it finds the map).\r\n>> puts ::ActiveRecord::Base.db_charmer_database_remappings.inspect\r\n{:limbo=>:realm_0}\r\n\r\n#### New threads don't work (unable to find the above map).\r\n>> Thread.new { puts ::ActiveRecord::Base.db_charmer_database_remappings.inspect }\r\n{}"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/72",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/72/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/72/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/72/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/72",
"id": 15789793,
"number": 72,
"title": "Difference in schemas between master and slave affects whole app.",
"user": {
"login": "andriytyurnikov",
"id": 3668,
"avatar_url": "https://avatars.githubusercontent.com/u/3668?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/andriytyurnikov",
"html_url": "https://github.com/andriytyurnikov",
"followers_url": "https://api.github.com/users/andriytyurnikov/followers",
"following_url": "https://api.github.com/users/andriytyurnikov/following{/other_user}",
"gists_url": "https://api.github.com/users/andriytyurnikov/gists{/gist_id}",
"starred_url": "https://api.github.com/users/andriytyurnikov/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/andriytyurnikov/subscriptions",
"organizations_url": "https://api.github.com/users/andriytyurnikov/orgs",
"repos_url": "https://api.github.com/users/andriytyurnikov/repos",
"events_url": "https://api.github.com/users/andriytyurnikov/events{/privacy}",
"received_events_url": "https://api.github.com/users/andriytyurnikov/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-06-20T09:39:08Z",
"updated_at": "2013-11-10T15:49:36Z",
"closed_at": "2013-11-10T15:49:36Z",
"body": "We had long-running migration on db master (lhm gem), and as one might expect there was some delay between master and slave.\r\nSo when migration finished on master, but was in progress on slave - we had different schemas.\r\nUnexpected surprise is that this difference affected parts of the app which do not use slave connection.\r\n\r\nMysql2::Error: Unknown column 'referrer' in 'field list': INSERT INTO `purchases` (`coupon_code`, `created_at`, `customer_id`, `dont_redeem`, `email`, `encrypted_email`, `ip_address`, `order_date`, `order_number`, `referrer`, `site_id`, `subtotal`, `updated_at`, `visitor_id`) VALUES ('CODE42', '2013-06-19 12:19:51', NULL, 0, 'email@gmail.com', NULL, '127.0.0.1', '2013-06-19 12:19:51', '100379070', NULL, 1887, 123.2, '2013-06-19 12:19:51', 26179393)\r\n\r\nAt the moment of the exception raise referrer column was present on master, but was not present on slave. Unexpected surprise here, is that controller, which raised this exception does not use db-charmer slave. Somehow db-charmer slave connection affects operations on master connection (schema info cached somewhere by activerecord?)"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/71",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/71/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/71/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/71/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/71",
"id": 15777336,
"number": 71,
"title": "Forcing writes to master in bang methods (create!, etc)",
"user": {
"login": "kpumuk",
"id": 10163,
"avatar_url": "https://avatars.githubusercontent.com/u/10163?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/kpumuk",
"html_url": "https://github.com/kpumuk",
"followers_url": "https://api.github.com/users/kpumuk/followers",
"following_url": "https://api.github.com/users/kpumuk/following{/other_user}",
"gists_url": "https://api.github.com/users/kpumuk/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kpumuk/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kpumuk/subscriptions",
"organizations_url": "https://api.github.com/users/kpumuk/orgs",
"repos_url": "https://api.github.com/users/kpumuk/repos",
"events_url": "https://api.github.com/users/kpumuk/events{/privacy}",
"received_events_url": "https://api.github.com/users/kpumuk/received_events",
"type": "User",
"site_admin": false
},
"labels": [
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/labels/reproducible-issue",
"name": "reproducible-issue",
"color": "e10c02"
}
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 1,
"created_at": "2013-06-20T01:27:59Z",
"updated_at": "2013-06-20T01:31:16Z",
"closed_at": null,
"body": "Currently db-charmer forces writes to master for `create`, `update`, `delete`, and some other ActiveRecord methods. Unfortunately, `create!` does not get forced to use master:\r\n\r\n ree-1.8.7-2012.02 >> TestModel.on_db(:slave).create\r\n SQL (0.2ms) BEGIN\r\n \r\n ree-1.8.7-2012.02 >> TestModel.on_db(:slave).create!\r\n [slave] SQL (0.1ms) BEGIN\r\n\r\nIt seems like problematic code is in `master_slave_routing.rb` for Rails 2.x"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/70",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/70/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/70/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/70/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/70",
"id": 15004735,
"number": 70,
"title": "Support for sharing a connection to same server / switching databases as necessary",
"user": {
"login": "boourns",
"id": 699550,
"avatar_url": "https://avatars.githubusercontent.com/u/699550?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/boourns",
"html_url": "https://github.com/boourns",
"followers_url": "https://api.github.com/users/boourns/followers",
"following_url": "https://api.github.com/users/boourns/following{/other_user}",
"gists_url": "https://api.github.com/users/boourns/gists{/gist_id}",
"starred_url": "https://api.github.com/users/boourns/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/boourns/subscriptions",
"organizations_url": "https://api.github.com/users/boourns/orgs",
"repos_url": "https://api.github.com/users/boourns/repos",
"events_url": "https://api.github.com/users/boourns/events{/privacy}",
"received_events_url": "https://api.github.com/users/boourns/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "open",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 2,
"created_at": "2013-05-31T17:41:34Z",
"updated_at": "2013-05-31T18:48:02Z",
"closed_at": null,
"body": "We're considering putting multiple logical databases on the same physical server. \r\n\r\nIf we do that we will hit an issue where every app worker will have many connections to the same mysql server and reach the point of having context switching issues from the mysql perspective.\r\n\r\nWhat do you think about reusing db_charmer connections when there is multiple logical databases on the same physical server?\r\n\r\nIf this is not something you can add we may be doing it at Shopify and would be interested in your view on this & what you would consider to be the best approach.\r\n\r\nThanks\r\nTom"
},
{
"url": "https://api.github.com/repos/kovyrin/db-charmer/issues/69",
"labels_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/69/labels{/name}",
"comments_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/69/comments",
"events_url": "https://api.github.com/repos/kovyrin/db-charmer/issues/69/events",
"html_url": "https://github.com/kovyrin/db-charmer/issues/69",
"id": 14541052,
"number": 69,
"title": "execution expired - timing out getting connections - multi-threading issue",
"user": {
"login": "dashbitla",
"id": 114106,
"avatar_url": "https://avatars.githubusercontent.com/u/114106?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/dashbitla",
"html_url": "https://github.com/dashbitla",
"followers_url": "https://api.github.com/users/dashbitla/followers",
"following_url": "https://api.github.com/users/dashbitla/following{/other_user}",
"gists_url": "https://api.github.com/users/dashbitla/gists{/gist_id}",
"starred_url": "https://api.github.com/users/dashbitla/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dashbitla/subscriptions",
"organizations_url": "https://api.github.com/users/dashbitla/orgs",
"repos_url": "https://api.github.com/users/dashbitla/repos",
"events_url": "https://api.github.com/users/dashbitla/events{/privacy}",
"received_events_url": "https://api.github.com/users/dashbitla/received_events",
"type": "User",
"site_admin": false
},
"labels": [
],
"state": "closed",
"locked": false,
"assignee": null,
"milestone": null,
"comments": 2,
"created_at": "2013-05-20T21:43:35Z",
"updated_at": "2013-11-10T15:52:05Z",
"closed_at": "2013-11-10T15:52:05Z",
"body": "2013-05-20T21:29:11Z 6058 TID-osusvs4o8 WARN: execution expired\r\n2013-05-20T21:29:11Z 6058 TID-osusvs4o8 WARN: /home/deployer/.rvm/rubies/ruby-1.9.3-p429/lib/ruby/1.9.1/monitor.rb:185:in `lock'\r\n/home/deployer/.rvm/rubies/ruby-1.9.3-p429/lib/ruby/1.9.1/monitor.rb:185:in `mon_enter'\r\n/home/deployer/.rvm/rubies/ruby-1.9.3-p429/lib/ruby/1.9.1/monitor.rb:209:in `mon_synchronize'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/connection_pool.rb:95:in `connection'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/connection_pool.rb:404:in `retrieve_connection'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/connection_specification.rb:170:in `retrieve_connection'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/connection_specification.rb:144:in `connection'\r\n/home/deployer/apps/testapp.com/releases/20130519223524/vendor/gems/db-charmer-1.9.0/lib/db_charmer/active_record/connection_switching.rb:34:in `connection_with_magic'\r\n/home/deployer/apps/testapp.com/releases/20130519223524/vendor/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation_method.rb:15:in `block in relation_with_db_charmer'\r\n/home/deployer/apps/testapp.com/releases/20130519223524/vendor/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation_method.rb:14:in `tap'\r\n/home/deployer/apps/testapp.com/releases/20130519223524/vendor/gems/db-charmer-1.9.0/lib/db_charmer/rails3/active_record/relation_method.rb:14:in `relation_with_db_charmer'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/scoping/named.rb:37:in `scoped'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/querying.rb:9:in `select'\r\n/home/deployer/apps/testapp.com/releases/20130519223524/app/models/api_callback.rb:12:in `push'\r\n/home/deployer/apps/testapp.com/releases/20130519223524/app/workers/kiq_agent_callback.rb:9:in `perform'\r\n/home/deployer/apps/testapp.com/shared/bundle/ruby/1.9.1/gems/sidekiq-2.9.0/lib/sidekiq/processor.rb:49:in `block (3 levels)\r\n\r\n"
}
]
================================================
FILE: lib/db_charmer/action_controller/force_slave_reads.rb
================================================
module DbCharmer
module ActionController
module ForceSlaveReads
module ClassMethods
@@db_charmer_force_slave_reads_actions = {}
def force_slave_reads(params = {})
@@db_charmer_force_slave_reads_actions[self.name] = {
:except => params[:except] ? [*params[:except]].map(&:to_s) : [],
:only => params[:only] ? [*params[:only]].map(&:to_s) : []
}
end
def force_slave_reads_options
@@db_charmer_force_slave_reads_actions[self.name]
end
def force_slave_reads_action?(name = nil)
name = name.to_s
options = force_slave_reads_options
# If no options were defined for this controller, all actions are not forced to use slaves
return false unless options
# Actions where force_slave_reads mode was turned off
return false if options[:except].include?(name)
# Only for these actions force_slave_reads was turned on
return options[:only].include?(name) if options[:only].any?
# If :except is not empty, we're done with the checks and rest of the actions are should force slave reads
# Otherwise, all the actions are not in force_slave_reads mode
options[:except].any?
end
end
module InstanceMethods
DISPATCH_METHOD = (DbCharmer.rails3?) ? :process_action : :perform_action
def self.included(base)
base.alias_method_chain DISPATCH_METHOD, :forced_slave_reads
end
def force_slave_reads!
@db_charmer_force_slave_reads = true
end
def dont_force_slave_reads!
@db_charmer_force_slave_reads = false
end
def force_slave_reads?
@db_charmer_force_slave_reads || self.class.force_slave_reads_action?(params[:action])
end
protected
class_eval <<-EOF, __FILE__, __LINE__+1
def #{DISPATCH_METHOD}_with_forced_slave_reads(*args, &block)
DbCharmer.with_controller(self) do
#{DISPATCH_METHOD}_without_forced_slave_reads(*args, &block)
end
end
EOF
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/association_preload.rb
================================================
module DbCharmer
module ActiveRecord
module AssociationPreload
ASSOCIATION_TYPES = [ :has_one, :has_many, :belongs_to, :has_and_belongs_to_many ]
def self.extended(base)
ASSOCIATION_TYPES.each do |association_type|
base.class_eval <<-EOF, __FILE__, __LINE__ + 1
def self.preload_#{association_type}_association(records, reflection, preload_options = {})
if self.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
self.db_charmer_default_connection != reflection.klass.db_charmer_default_connection
return super(records, reflection, preload_options)
end
reflection.klass.on_db(self) do
super(records, reflection, preload_options)
end
end
EOF
end
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/class_attributes.rb
================================================
module DbCharmer
module ActiveRecord
module ClassAttributes
@@db_charmer_opts = {}
def db_charmer_opts=(opts)
@@db_charmer_opts[self.name] = opts
end
def db_charmer_opts
@@db_charmer_opts[self.name] || {}
end
#---------------------------------------------------------------------------------------------
@@db_charmer_default_connections = {}
def db_charmer_default_connection=(conn)
@@db_charmer_default_connections[self.name] = conn
end
def db_charmer_default_connection
@@db_charmer_default_connections[self.name]
end
#---------------------------------------------------------------------------------------------
@@db_charmer_slaves = {}
def db_charmer_slaves=(slaves)
@@db_charmer_slaves[self.name] = slaves
end
def db_charmer_slaves
@@db_charmer_slaves[self.name] || []
end
# Returns a random connection from the list of slaves configured for this AR class
def db_charmer_random_slave
return nil unless db_charmer_slaves.any?
db_charmer_slaves[rand(db_charmer_slaves.size)]
end
#---------------------------------------------------------------------------------------------
def db_charmer_connection_proxies
Thread.current[:db_charmer_connection_proxies] ||= {}
end
def db_charmer_connection_proxy=(proxy)
db_charmer_connection_proxies[self.name] = proxy
end
def db_charmer_connection_proxy
db_charmer_connection_proxies[self.name]
end
#---------------------------------------------------------------------------------------------
def db_charmer_force_slave_reads_flags
Thread.current[:db_charmer_force_slave_reads] ||= {}
end
def db_charmer_force_slave_reads=(force)
db_charmer_force_slave_reads_flags[self.name] = force
end
def db_charmer_force_slave_reads
db_charmer_force_slave_reads_flags[self.name]
end
# Slave reads are used in two cases:
# - per-model slave reads are enabled (see db_magic method for more details)
# - global slave reads enforcing is enabled (in a controller action)
def db_charmer_force_slave_reads?
db_charmer_force_slave_reads || DbCharmer.force_slave_reads?
end
#---------------------------------------------------------------------------------------------
def db_charmer_connection_levels
Thread.current[:db_charmer_connection_levels] ||= Hash.new(0)
end
def db_charmer_connection_level=(level)
db_charmer_connection_levels[self.name] = level
end
def db_charmer_connection_level
db_charmer_connection_levels[self.name] || 0
end
def db_charmer_top_level_connection?
db_charmer_connection_level.zero?
end
#---------------------------------------------------------------------------------------------
def db_charmer_remapped_connection
return nil unless db_charmer_top_level_connection?
name = :master
proxy = db_charmer_model_connection_proxy
name = proxy.db_charmer_connection_name.to_sym if proxy
remapped = db_charmer_database_remappings[name]
remapped ? DbCharmer::ConnectionFactory.connect(remapped, true) : nil
end
def db_charmer_database_remappings
Thread.current[:db_charmer_database_remappings] ||= Hash.new
end
def db_charmer_database_remappings=(mappings)
raise "Mappings must be nil or respond to []" if mappings && (! mappings.respond_to?(:[]))
Thread.current[:db_charmer_database_remappings] = mappings || {}
end
#---------------------------------------------------------------------------------------------
# Returns model-specific connection proxy, ignoring any global connection remappings
def db_charmer_model_connection_proxy
db_charmer_connection_proxy || db_charmer_default_connection
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/connection_switching.rb
================================================
module DbCharmer
module ActiveRecord
module ConnectionSwitching
def establish_real_connection_if_exists(name, should_exist = false)
name = name.to_s
# Check environment name
config = configurations[DbCharmer.env]
unless config
error = "Invalid environment name (does not exist in database.yml): #{DbCharmer.env}. Please set correct Rails.env or DbCharmer.env."
raise ArgumentError, error
end
# Check connection name
config = config[name]
unless config
if should_exist
raise ArgumentError, "Invalid connection name (does not exist in database.yml): #{DbCharmer.env}/#{name}"
end
return # No need to establish connection - they do not want us to
end
# Pass connection name with config
config[:connection_name] = name
establish_connection(config)
end
#-----------------------------------------------------------------------------------------------------------------
def hijack_connection!
return if self.respond_to?(:connection_with_magic)
class << self
# Make sure we check our accessors before going to the default connection retrieval method
def connection_with_magic
db_charmer_remapped_connection || db_charmer_model_connection_proxy || connection_without_magic
end
alias_method_chain :connection, :magic
def connection_pool_with_magic
if connection.respond_to?(:abstract_connection_class)
abstract_connection_class = connection.abstract_connection_class
connection_handler.retrieve_connection_pool(abstract_connection_class) || connection_pool_without_magic
else
connection_pool_without_magic
end
end
alias_method_chain :connection_pool, :magic
end
end
#-----------------------------------------------------------------------------------------------------------------
def coerce_to_connection_proxy(conn, should_exist = true)
# Return nil if given no connection specification
return nil if conn.nil?
# For sharded proxies just use them as-is
return conn if conn.respond_to?(:set_real_connection)
# For connection proxies and objects that could be coerced into a proxy just call the coercion method
return conn.db_charmer_connection_proxy if conn.respond_to?(:db_charmer_connection_proxy)
# For plain AR connection adapters, just use them as-is
return conn if conn.kind_of?(::ActiveRecord::ConnectionAdapters::AbstractAdapter)
# For connection names, use connection factory to create new connections
if conn.kind_of?(Symbol) || conn.kind_of?(String)
return DbCharmer::ConnectionFactory.connect(conn, should_exist)
end
# For connection configs (hashes), create connections
if conn.kind_of?(Hash)
conn = conn.symbolize_keys
raise ArgumentError, "Missing required :connection_name parameter" unless conn[:connection_name]
return DbCharmer::ConnectionFactory.connect_to_db(conn[:connection_name], conn)
end
# Fails for unsupported connection types
raise "Unsupported connection type: #{conn.class}"
end
#-----------------------------------------------------------------------------------------------------------------
def switch_connection_to(conn, should_exist = true)
new_conn = coerce_to_connection_proxy(conn, should_exist)
if db_charmer_connection_proxy.respond_to?(:set_real_connection)
db_charmer_connection_proxy.set_real_connection(new_conn)
end
self.db_charmer_connection_proxy = new_conn
self.hijack_connection!
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/db_magic.rb
================================================
module DbCharmer
module ActiveRecord
module DbMagic
def db_magic(opt = {})
# Make sure we could use our connections management here
hijack_connection!
# Should requested connections exist in the config?
should_exist = opt.has_key?(:should_exist) ? opt[:should_exist] : DbCharmer.connections_should_exist?
# Main connection management
setup_connection_magic(opt[:connection], should_exist)
# Set up slaves pool
opt[:slaves] ||= []
opt[:slaves] = [ opt[:slaves] ].flatten
opt[:slaves] << opt[:slave] if opt[:slave]
# Forced reads are enabled for all models by default, could be disabled by the user
forced_slave_reads = opt.has_key?(:force_slave_reads) ? opt[:force_slave_reads] : true
# Setup all the slaves related magic if needed
setup_slaves_magic(opt[:slaves], forced_slave_reads, should_exist)
# Setup inheritance magic
setup_children_magic(opt)
# Setup sharding if needed
if opt[:sharded]
raise ArgumentError, "Can't use sharding on a model with slaves!" if opt[:slaves].any?
setup_sharding_magic(opt[:sharded])
end
end
private
def setup_children_magic(opt)
self.db_charmer_opts = opt.clone
unless self.respond_to?(:inherited_with_db_magic)
class << self
def inherited_with_db_magic(child)
o = inherited_without_db_magic(child)
child.db_magic(self.db_charmer_opts)
o
end
alias_method_chain :inherited, :db_magic
end
end
end
def setup_sharding_magic(config)
# Add sharding-specific methods
self.extend(DbCharmer::ActiveRecord::Sharding)
# Get configuration
name = config[:sharded_connection] or raise ArgumentError, "No :sharded_connection!"
# Assign sharded connection
self.sharded_connection = DbCharmer::Sharding.sharded_connection(name)
# Setup model default connection
setup_connection_magic(sharded_connection.default_connection)
end
def setup_connection_magic(conn, should_exist = true)
conn_proxy = coerce_to_connection_proxy(conn, should_exist)
self.db_charmer_default_connection = conn_proxy
switch_connection_to(conn_proxy, should_exist)
end
def setup_slaves_magic(slaves, force_slave_reads, should_exist = true)
self.db_charmer_force_slave_reads = force_slave_reads
# Initialize the slave connections list
self.db_charmer_slaves = slaves.collect do |slave|
coerce_to_connection_proxy(slave, should_exist)
end
return if db_charmer_slaves.empty?
# Enable on_slave/on_master methods
self.extend(DbCharmer::ActiveRecord::MultiDbProxy::MasterSlaveClassMethods)
# Enable automatic master/slave queries routing (we have specialized versions on those modules for rails2/3)
self.extend(DbCharmer::ActiveRecord::MasterSlaveRouting::ClassMethods)
self.send(:include, DbCharmer::ActiveRecord::MasterSlaveRouting::InstanceMethods)
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/migration/multi_db_migrations.rb
================================================
module DbCharmer
module ActiveRecord
module Migration
module MultiDbMigrations
def self.append_features(base)
return false if base < self
super
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
base.class_eval do
if DbCharmer.rails31?
alias_method_chain :migrate, :db_wrapper
else
class << self
alias_method_chain :migrate, :db_wrapper
end
end
end
end
module ClassMethods
@@multi_db_names = {}
def multi_db_names
@@multi_db_names[self.name] || @@multi_db_names['ActiveRecord::Migration']
end
def multi_db_names=(names)
@@multi_db_names[self.name] = names
end
unless DbCharmer.rails31?
def migrate_with_db_wrapper(direction)
if names = multi_db_names
names.each do |multi_db_name|
on_db(multi_db_name) do
migrate_without_db_wrapper(direction)
end
end
else
migrate_without_db_wrapper(direction)
end
end
def on_db(db_name)
name = db_name.is_a?(Hash) ? db_name[:connection_name] : db_name.inspect
announce "Switching connection to #{name}"
# Switch connection
old_proxy = ::ActiveRecord::Base.db_charmer_connection_proxy
db_name = nil if db_name == :default
::ActiveRecord::Base.switch_connection_to(db_name, DbCharmer.connections_should_exist?)
# Yield the block
yield
ensure
# Switch it back
::ActiveRecord::Base.verify_active_connections!
announce "Switching connection back"
::ActiveRecord::Base.switch_connection_to(old_proxy)
end
end
def db_magic(opts = {})
# Collect connections from all possible options
conns = [ opts[:connection], opts[:connections] ]
conns << shard_connections(opts[:sharded_connection]) if opts[:sharded_connection]
# Get a unique set of connections
conns = conns.flatten.compact.uniq
raise ArgumentError, "No connection name - no magic!" unless conns.any?
# Save connections
self.multi_db_names = conns
end
# Return a list of connections to shards in a sharded connection
def shard_connections(conn_name)
conn = DbCharmer::Sharding.sharded_connection(conn_name)
conn.shard_connections
end
end
def migrate_with_db_wrapper(direction)
if names = self.class.multi_db_names
names.each do |multi_db_name|
on_db(multi_db_name) do
migrate_without_db_wrapper(direction)
end
end
else
migrate_without_db_wrapper(direction)
end
end
def record_on_db(db_name, block)
recorder = ::ActiveRecord::Migration::CommandRecorder.new(DbCharmer::ConnectionFactory.connect(db_name))
old_recorder, @connection = @connection, recorder
block.call
old_recorder.record :on_db, [db_name, @connection]
@connection = old_recorder
end
def replay_commands_on_db(name, commands)
on_db(name) do
commands.each do |cmd, args|
send(cmd, *args)
end
end
end
def on_db(db_name, &block)
if @connection.is_a?(::ActiveRecord::Migration::CommandRecorder)
record_on_db(db_name, block)
return
end
name = db_name.is_a?(Hash) ? db_name[:connection_name] : db_name.inspect
announce "Switching connection to #{name}"
# Switch connection
old_connection, old_proxy = @connection, ::ActiveRecord::Base.db_charmer_connection_proxy
db_name = nil if db_name == :default
::ActiveRecord::Base.switch_connection_to(db_name, DbCharmer.connections_should_exist?)
# Yield the block
::ActiveRecord::Base.connection_pool.with_connection do |conn|
@connection = conn
yield
end
ensure
@connection = old_connection
# Switch it back
::ActiveRecord::Base.verify_active_connections!
announce "Switching connection back"
::ActiveRecord::Base.switch_connection_to(old_proxy)
end
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/multi_db_proxy.rb
================================================
module DbCharmer
module ActiveRecord
module MultiDbProxy
# Simple proxy class that switches connections and then proxies all the calls
# This class is used to implement chained on_db calls
class OnDbProxy < ActiveSupport::BasicObject
# We need to do this because in Rails 2.3 BasicObject does not remove object_id method, which is stupid
undef_method(:object_id) if instance_methods.member?('object_id')
def initialize(proxy_target, slave)
@proxy_target = proxy_target
@slave = slave
end
private
def method_missing(meth, *args, &block)
# Switch connection and proxy the method call
@proxy_target.on_db(@slave) do |proxy_target|
res = proxy_target.__send__(meth, *args, &block)
# If result is a scope/association, return a new proxy for it, otherwise return the result itself
(res.proxy?) ? OnDbProxy.new(res, @slave) : res
end
end
end
module ClassMethods
def on_db(con, proxy_target = nil)
proxy_target ||= self
# Chain call
return OnDbProxy.new(proxy_target, con) unless block_given?
# Block call
begin
self.db_charmer_connection_level += 1
old_proxy = db_charmer_connection_proxy
switch_connection_to(con, DbCharmer.connections_should_exist?)
yield(proxy_target)
ensure
switch_connection_to(old_proxy)
self.db_charmer_connection_level -= 1
end
end
end
module InstanceMethods
def on_db(con, proxy_target = nil, &block)
proxy_target ||= self
self.class.on_db(con, proxy_target, &block)
end
end
module MasterSlaveClassMethods
def on_slave(con = nil, proxy_target = nil, &block)
con ||= db_charmer_random_slave
raise ArgumentError, "No slaves found in the class and no slave connection given" unless con
on_db(con, proxy_target, &block)
end
def on_master(proxy_target = nil, &block)
on_db(db_charmer_default_connection, proxy_target, &block)
end
def first_level_on_slave
first_level = db_charmer_top_level_connection? && on_master.connection.open_transactions.zero?
if first_level && db_charmer_force_slave_reads? && db_charmer_slaves.any?
on_slave { yield }
else
yield
end
end
end
end
end
end
================================================
FILE: lib/db_charmer/active_record/sharding.rb
================================================
module DbCharmer
module ActiveRecord
module Sharding
def self.extended(model)
model.cattr_accessor(:sharded_connection)
end
def shard_for(key, proxy_target = nil, &block)
raise ArgumentError, "No sharded connection configured!" unless sharded_connection
conn = sharded_connection.sharder.shard_for_key(key)
on_db(conn, proxy_target, &block)
end
# Run on default shard (if supported by the sharding method)
def on_default_shard(proxy_target = nil, &block)
raise ArgumentError, "No sharded connection configured!" unless sharded_connection
if sharded_connection.support_default_shard?
shard_for(:default, proxy_target, &block)
else
raise ArgumentError, "This model's sharding method does not support default shard"
end
end
# Enumerate shards
def on_each_shard(proxy_target = nil, &block)
raise ArgumentError, "No sharded connection configured!" unless sharded_connection
conns = sharded_connection.shard_connections
raise ArgumentError, "This model's sharding method does not support shards enumeration" unless conns
conns.each do |conn|
on_db(conn, proxy_target, &block)
end
end
end
end
end
================================================
FILE: lib/db_charmer/connection_factory.rb
================================================
#
# This class is used to automatically generate small abstract ActiveRecord classes
# that would then be used as a source of database connections for DbCharmer magic.
# This way we do not need to re-implement all the connection establishing code
# that ActiveRecord already has and we make our code less dependant on Rails versions.
#
module DbCharmer
module ConnectionFactory
def self.connection_classes
Thread.current[:db_charmer_generated_connection_classes] ||= {}
end
def self.connection_classes=(val)
Thread.current[:db_charmer_generated_connection_classes] = val
end
def self.reset!
self.connection_classes = {}
end
# Establishes connection or return an existing one from cache
def self.connect(connection_name, should_exist = true)
connection_name = connection_name.to_s
connection_classes[connection_name] ||= establish_connection(connection_name, should_exist)
end
# Establishes connection or return an existing one from cache (not using AR database configs)
def self.connect_to_db(connection_name, config)
connection_name = connection_name.to_s
connection_classes[connection_name] ||= establish_connection_to_db(connection_name, config)
end
# Establish connection with a specified name
def self.establish_connection(connection_name, should_exist = true)
abstract_class = generate_abstract_class(connection_name, should_exist)
DbCharmer::ConnectionProxy.new(abstract_class, connection_name)
end
# Establish connection with a specified name (not using AR database configs)
def self.establish_connection_to_db(connection_name, config)
abstract_class = generate_abstract_class_for_db(connection_name, config)
DbCharmer::ConnectionProxy.new(abstract_class, connection_name)
end
# Generate an abstract AR class with specified connection established
def self.generate_abstract_class(connection_name, should_exist = true)
# Generate class
klass = generate_empty_abstract_ar_class(abstract_connection_class_name(connection_name))
# Establish connection
klass.establish_real_connection_if_exists(connection_name.to_sym, !!should_exist)
# Return the class
return klass
end
# Generate an abstract AR class with specified connection established (not using AR database configs)
def self.generate_abstract_class_for_db(connection_name, config)
# Generate class
klass = generate_empty_abstract_ar_class(abstract_connection_class_name(connection_name))
# Establish connection
klass.establish_connection(config)
# Return the class
return klass
end
def self.generate_empty_abstract_ar_class(klass)
# Define class
module_eval "class #{klass} < ::ActiveRecord::Base; self.abstract_class = true; end"
# Return class
klass.constantize
end
# Generates unique names for our abstract AR classes
def self.abstract_connection_class_name(connection_name)
conn_name_klass = connection_name.to_s.gsub(/\W+/, '_').camelize
thread = Thread.current.object_id.abs # need to make sure it is non-negative
"::AutoGeneratedAbstractConnectionClass#{conn_name_klass}ForThread#{thread}"
end
end
end
================================================
FILE: lib/db_charmer/connection_proxy.rb
================================================
# Simple proxy that sends all method calls to a real database connection
module DbCharmer
class ConnectionProxy < ActiveSupport::BasicObject
# We need to do this because in Rails 2.3 BasicObject does not remove object_id method, which is stupid
undef_method(:object_id) if instance_methods.member?('object_id')
# We use this to get a connection class from the proxy
attr_accessor :abstract_connection_class
def initialize(abstract_class, db_name)
@abstract_connection_class = abstract_class
@db_name = db_name
end
def db_charmer_connection_name
@db_name
end
def db_charmer_connection_proxy
self
end
def db_charmer_retrieve_connection
@abstract_connection_class.retrieve_connection
end
def nil?
false
end
#-----------------------------------------------------------------------------------------------
RESPOND_TO_METHODS = [
:abstract_connection_class,
:db_charmer_connection_name,
:db_charmer_connection_proxy,
:db_charmer_retrieve_connection,
:nil?
].freeze
# Short-circuit some of the methods for which we know there is a separate check in coercion code
DOESNT_RESPOND_TO_METHODS = [
:set_real_connection
].freeze
def respond_to?(method_name, include_all = false)
return true if RESPOND_TO_METHODS.include?(method_name)
return false if DOESNT_RESPOND_TO_METHODS.include?(method_name)
db_charmer_retrieve_connection.respond_to?(method_name, include_all)
end
#-----------------------------------------------------------------------------------------------
def method_missing(meth, *args, &block)
db_charmer_retrieve_connection.send(meth, *args, &block)
end
end
end
================================================
FILE: lib/db_charmer/core_extensions.rb
================================================
class Object
unless defined?(try)
def try(method, *options, &block)
send(method, *options, &block)
end
end
# These methods are added to all objects so we could call proxy? on anything
# and figure if an object is a proxy w/o hitting method_missing or respond_to?
def self.proxy?
false
end
def proxy?
false
end
end
class NilClass
def try(*args)
nil
end
end
================================================
FILE: lib/db_charmer/force_slave_reads.rb
================================================
module DbCharmer
def self.current_controller
Thread.current[:db_charmer_current_controller]
end
def self.current_controller=(val)
Thread.current[:db_charmer_current_controller] = val
end
#-------------------------------------------------------------------------------------------------
def self.forced_slave_reads_setting
Thread.current[:db_charmer_forced_slave_reads]
end
def self.forced_slave_reads_setting=(val)
Thread.current[:db_charmer_forced_slave_reads] = val
end
#-------------------------------------------------------------------------------------------------
def self.force_slave_reads?
# If global force slave reads is requested, do it
return true if Thread.current[:db_charmer_forced_slave_reads]
# If not, try to use current controller to decide on this
return false unless current_controller.respond_to?(:force_slave_reads?)
slave_reads = current_controller.force_slave_reads?
logger.debug("Using controller to figure out if slave reads should be forced: #{slave_reads}")
return slave_reads
end
#-------------------------------------------------------------------------------------------------
def self.with_controller(controller)
raise ArgumentError, "No block given" unless block_given?
logger.debug("Setting current controller for db_charmer: #{controller.class.name}")
self.current_controller = controller
yield
ensure
logger.debug('Clearing current controller for db_charmer')
self.current_controller = nil
end
#-------------------------------------------------------------------------------------------------
# Force all reads in a block of code to go to a slave
def self.force_slave_reads
raise ArgumentError, "No block given" unless block_given?
old_forced_slave_reads = self.forced_slave_reads_setting
begin
self.forced_slave_reads_setting = true
yield
ensure
self.forced_slave_reads_setting = old_forced_slave_reads
end
end
end
================================================
FILE: lib/db_charmer/rails2/abstract_adapter/log_formatting.rb
================================================
module DbCharmer
module AbstractAdapter
module LogFormatting
def self.included(base)
base.alias_method_chain :format_log_entry, :connection_name
end
def connection_name
raise "Can't find connection configuration!" unless @config
@config[:connection_name]
end
# Rails 2.X specific logging method
def format_log_entry_with_connection_name(message, dump = nil)
msg = connection_name ? "[#{connection_name}] " : ''
msg = " \e[0;34;1m#{msg}\e[0m" if connection_name && ::ActiveRecord::Base.colorize_logging
msg << format_log_entry_without_connection_name(message, dump)
end
end
end
end
================================================
FILE: lib/db_charmer/rails2/active_record/master_slave_routing.rb
================================================
module DbCharmer
module ActiveRecord
module MasterSlaveRouting
module ClassMethods
SLAVE_METHODS = [ :find_by_sql, :count_by_sql, :calculate ]
MASTER_METHODS = [ :update, :create, :delete, :destroy, :delete_all, :destroy_all, :update_all, :update_counters ]
SLAVE_METHODS.each do |slave_method|
class_eval <<-EOF, __FILE__, __LINE__ + 1
def #{slave_method}(*args, &block)
first_level_on_slave do
super(*args, &block)
end
end
EOF
end
MASTER_METHODS.each do |master_method|
class_eval <<-EOF, __FILE__, __LINE__ + 1
def #{master_method}(*args, &block)
on_master do
super(*args, &block)
end
end
EOF
end
def find(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:lock]
on_master { super(*args, &block) }
else
super(*args, &block)
end
end
end
module InstanceMethods
def reload(*args, &block)
self.class.on_master do
super(*args, &block)
end
end
end
end
end
end
================================================
FILE: lib/db_charmer/rails2/active_record/named_scope/scope_proxy.rb
================================================
module DbCharmer
module ActiveRecord
module NamedScope
module ScopeProxy
def proxy?
true
end
def on_db(con, proxy_target = nil, &block)
proxy_target ||= self
proxy_scope.on_db(con, proxy_target, &block)
end
def on_slave(con = nil, &block)
proxy_scope.on_slave(con, self, &block)
end
def on_master(&block)
proxy_scope.on_master(self, &block)
end
end
end
end
end
================================================
FILE: lib/db_charmer/rails3/abstract_adapter/connection_name.rb
================================================
module DbCharmer
module AbstractAdapter
module ConnectionName
# We use this proxy to push connection name down to instrumenters w/o monkey-patching the log method itself
class InstrumenterDecorator < ActiveSupport::BasicObject
def initialize(adapter, instrumenter)
@adapter = adapter
@instrumenter = instrumenter
end
def instrument(name, payload = {}, &block)
payload[:connection_name] ||= @adapter.connection_name
@instrumenter.instrument(name, payload, &block)
end
def method_missing(meth, *args, &block)
@instrumenter.send(meth, *args, &block)
end
end
def self.included(base)
base.alias_method_chain :initialize, :connection_name
end
def connection_name
raise "Can't find connection configuration!" unless @config
@config[:connection_name]
end
def initialize_with_connection_name(*args)
initialize_without_connection_name(*args)
@instrumenter = InstrumenterDecorator.new(self, @instrumenter)
end
end
end
end
================================================
FILE: lib/db_charmer/rails3/active_record/log_subscriber.rb
================================================
module DbCharmer
module ActiveRecord
module LogSubscriber
def self.included(base)
base.send(:attr_accessor, :connection_name)
base.alias_method_chain :sql, :connection_name
base.alias_method_chain :debug, :connection_name
end
def sql_with_connection_name(event)
self.connection_name = event.payload[:connection_name]
sql_without_connection_name(event)
end
def debug_with_connection_name(msg)
conn = connection_name ? color(" [#{connection_name}]", ActiveSupport::LogSubscriber::BLUE, true) : ''
debug_without_connection_name(conn + msg)
end
end
end
end
================================================
FILE: lib/db_charmer/rails3/active_record/master_slave_routing.rb
================================================
module DbCharmer
module ActiveRecord
module MasterSlaveRouting
module ClassMethods
SLAVE_METHODS = [ :find_by_sql, :count_by_sql ]
MASTER_METHODS = [ ] # I don't know any methods in AR::Base that change data directly w/o going to the relation object
SLAVE_METHODS.each do |slave_method|
class_eval <<-EOF, __FILE__, __LINE__ + 1
def #{slave_method}(*args, &block)
first_level_on_slave do
super(*args, &block)
end
end
EOF
end
MASTER_METHODS.each do |master_method|
class_eval <<-EOF, __FILE__, __LINE__ + 1
def #{master_method}(*args, &block)
on_master do
super(*args, &block)
end
end
EOF
end
end
module InstanceMethods
MASTER_METHODS = [ :reload ]
MASTER_METHODS.each do |master_method|
class_eval <<-EOF, __FILE__, __LINE__ + 1
def #{master_method}(*args, &block)
self.class.on_master do
super(*args, &block)
end
end
EOF
end
end
end
end
end
================================================
FILE: lib/db_charmer/rails3/active_record/relation/connection_routing.rb
================================================
module DbCharmer
module ActiveRecord
module Relation
module ConnectionRouting
# All the methods that could be querying the database
SLAVE_METHODS = [ :calculate, :exists? ]
MASTER_METHODS = [ :delete, :delete_all, :destroy, :destroy_all, :reload, :update, :update_all ]
ALL_METHODS = SLAVE_METHODS + MASTER_METHODS
DB_CHARMER_ATTRIBUTES = [ :db_charmer_connection, :db_charmer_connection_is_forced, :db_charmer_enable_slaves ]
# Define the default relation connection + override all the query methods here
def self.included(base)
init_attributes(base)
init_routing(base)
end
# Define our attributes + spawn methods shit needs to be changed to make sure our accessors are copied over to the new instances
def self.init_attributes(base)
DB_CHARMER_ATTRIBUTES.each do |attr|
base.send(:attr_accessor, attr)
end
# Override spawn methods
base.alias_method_chain :except, :db_charmer
base.alias_method_chain :only, :db_charmer
end
# Override all query methods
def self.init_routing(base)
ALL_METHODS.each do |meth|
base.alias_method_chain meth, :db_charmer
end
# Special case: for normal selects we go to the slave, but for selects with a lock we should use master
base.alias_method_chain :to_a, :db_charmer
end
# Copy db_charmer attributes in addition to what they're copying
def except_with_db_charmer(*args)
except_without_db_charmer(*args).tap do |result|
copy_db_charmer_options(self, result)
end
end
# Copy db_charmer attributes in addition to what they're copying
def only_with_db_charmer(*args)
only_without_db_charmer(*args).tap do |result|
copy_db_charmer_options(self, result)
end
end
# Copy our accessors from one instance to another
def copy_db_charmer_options(src, dst)
DB_CHARMER_ATTRIBUTES.each do |attr|
dst.send("#{attr}=".to_sym, src.send(attr))
end
end
# Connection switching (changes the default relation connection)
def on_db(con, &block)
if block_given?
@klass.on_db(con, &block)
else
clone.tap do |result|
result.db_charmer_connection = con
result.db_charmer_connection_is_forced = true
end
end
end
# Make sure we get the right connection here
def connection
@klass.on_db(db_charmer_connection).connection
end
# Selects preferred destination (master/slave/default) for a query
def select_destination(method, recommendation = :default)
# If this relation was created within a forced connection block (e.g Model.on_db(:foo).relation)
# Then we should use that connection everywhere except cases when a model is slave-enabled
# in those cases DML queries go to the master
if db_charmer_connection_is_forced
return :master if db_charmer_enable_slaves && MASTER_METHODS.member?(method)
return :default
end
# If this relation is created from a slave-enabled model, let's do the routing if possible
if db_charmer_enable_slaves
return :slave if SLAVE_METHODS.member?(method)
return :master if MASTER_METHODS.member?(method)
else
# Make sure we do not use recommended destination
recommendation = :default
end
# If nothing else came up, let's use the default or recommended connection
return recommendation
end
# Switch the model to default relation connection
def switch_connection_for_method(method, recommendation = nil)
# Choose where to send the query
destination ||= select_destination(method, recommendation)
# What method to use
on_db_method = [ :on_db, db_charmer_connection ]
on_db_method = :on_master if destination == :master
on_db_method = :first_level_on_slave if destination == :slave
# Perform the query
@klass.send(*on_db_method) do
yield
end
end
# For normal selects we go to the slave, but for selects with a lock we should use master
def to_a_with_db_charmer(*args, &block)
preferred_destination = :slave
preferred_destination = :master if lock_value
switch_connection_for_method(:to_a, preferred_destination) do
to_a_without_db_charmer(*args, &block)
end
end
# Need this to mimick alias_method_chain name generation (exists? => exists_with_db_charmer?)
def self.aliased_method_name(target, with)
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
"#{aliased_target}_#{with}_db_charmer#{punctuation}"
end
# Override all the query methods here
ALL_METHODS.each do |method|
class_eval <<-EOF, __FILE__, __LINE__ + 1
def #{aliased_method_name method, :with}(*args, &block)
switch_connection_for_method(:#{method.to_s}) do
#{aliased_method_name method, :without}(*args, &block)
end
end
EOF
end
end
end
end
end
================================================
FILE: lib/db_charmer/rails3/active_record/relation_method.rb
================================================
module DbCharmer
module ActiveRecord
module RelationMethod
def self.extended(base)
class << base
alias_method_chain :relation, :db_charmer
alias_method_chain :arel_engine, :db_charmer
end
end
# Create a relation object and initialize its default connection
def relation_with_db_charmer(*args, &block)
relation_without_db_charmer(*args, &block).tap do |rel|
rel.db_charmer_connection = self.connection
rel.db_charmer_enable_slaves = self.db_charmer_slaves.any?
rel.db_charmer_connection_is_forced = !db_charmer_top_level_connection?
end
end
# Use the model itself an engine for Arel, do not fall back to AR::Base
def arel_engine_with_db_charmer(*)
self
end
end
end
end
================================================
FILE: lib/db_charmer/rails31/active_record/migration/command_recorder.rb
================================================
module DbCharmer
module ActiveRecord
module Migration
module CommandRecorder
def invert_on_db(args)
[:replay_commands_on_db, [args.first, args[1].inverse]]
end
end
end
end
end
================================================
FILE: lib/db_charmer/rails31/active_record/preloader/association.rb
================================================
module DbCharmer
module ActiveRecord
module Preloader
module Association
extend ActiveSupport::Concern
included do
alias_method_chain :build_scope, :db_magic
end
def build_scope_with_db_magic
if model.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
model.db_charmer_default_connection != klass.db_charmer_default_connection
build_scope_without_db_magic
else
build_scope_without_db_magic.on_db(model)
end
end
end
end
end
end
================================================
FILE: lib/db_charmer/rails31/active_record/preloader/has_and_belongs_to_many.rb
================================================
module DbCharmer
module ActiveRecord
module Preloader
module HasAndBelongsToMany
extend ActiveSupport::Concern
included do
alias_method_chain :records_for, :db_magic
end
def records_for_with_db_magic(ids)
if model.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
model.db_charmer_default_connection != klass.db_charmer_default_connection
records_for_without_db_magic(ids)
else
klass.on_db(model) do
records_for_without_db_magic(ids)
end
end
end
end
end
end
end
================================================
FILE: lib/db_charmer/railtie.rb
================================================
module DbCharmer
class Railtie < Rails::Railtie
rake_tasks do
load "db_charmer/tasks/databases.rake"
end
end
end
================================================
FILE: lib/db_charmer/sharding/connection.rb
================================================
module DbCharmer
module Sharding
class Connection
attr_accessor :config, :sharder
def initialize(config)
@config = config
@sharder = self.instantiate_sharder
end
def instantiate_sharder
raise ArgumentError, "No :method passed!" unless config[:method]
sharder_class_name = "DbCharmer::Sharding::Method::#{config[:method].to_s.classify}"
sharder_class = sharder_class_name.constantize
sharder_class.new(config)
end
def shard_connections
sharder.respond_to?(:shard_connections) ? sharder.shard_connections : nil
end
def support_default_shard?
sharder.respond_to?(:support_default_shard?) && sharder.support_default_shard?
end
def default_connection
@default_connection ||= DbCharmer::Sharding::StubConnection.new(self)
end
end
end
end
================================================
FILE: lib/db_charmer/sharding/method/db_block_group_map.rb
================================================
# This is a more sophisticated sharding method based on a two layer database-backed
# blocks map that holds block-shard associations. Record blocks are mapped to tablegroups
# and groups are mapped to shards.
#
# It automatically creates new blocks for new keys and assigns them to existing groups.
# Warning: make sure to create at least one shard and one group before inserting any records.
#
module DbCharmer
module Sharding
module Method
class DbBlockGroupMap
# Shard connection info model
class Shard < ::ActiveRecord::Base
validates_presence_of :db_host
validates_presence_of :db_port
validates_presence_of :db_user
validates_presence_of :db_pass
validates_presence_of :db_name_prefix
has_many :groups, :class_name => 'DbCharmer::Sharding::Method::DbBlockGroupMap::Group'
end
# Table group info model
class Group < ::ActiveRecord::Base
validates_presence_of :shard_id
belongs_to :shard, :class_name => 'DbCharmer::Sharding::Method::DbBlockGroupMap::Shard'
end
#---------------------------------------------------------------------------------------------------------------
# Sharder name
attr_accessor :name
# Mapping db connection
attr_accessor :connection, :connection_name
# Mapping table name
attr_accessor :map_table
# Tablegroups table name
attr_accessor :groups_table
# Shards table name
attr_accessor :shards_table
# Sharding keys block size
attr_accessor :block_size
def initialize(config)
@name = config[:name] or raise(ArgumentError, "Missing required :name parameter!")
@connection = DbCharmer::ConnectionFactory.connect(config[:connection], true)
@block_size = (config[:block_size] || 10000).to_i
@map_table = config[:map_table] or raise(ArgumentError, "Missing required :map_table parameter!")
@groups_table = config[:groups_table] or raise(ArgumentError, "Missing required :groups_table parameter!")
@shards_table = config[:shards_table] or raise(ArgumentError, "Missing required :shards_table parameter!")
# Local caches
@shard_info_cache = {}
@group_info_cache = {}
@blocks_cache = Rails.cache
@blocks_cache_prefix = config[:blocks_cache_prefix] || "#{@name}_block:"
end
#---------------------------------------------------------------------------------------------------------------
def shard_for_key(key)
block = block_for_key(key)
# Auto-allocate new blocks
block ||= allocate_new_block_for_key(key)
raise ArgumentError, "Invalid key value, no shards found for this key and could not create a new block!" unless block
# Load shard
group_id = block['group_id'].to_i
shard_info = shard_info_by_group_id(group_id)
# Get config
shard_connection_config(shard_info, group_id)
end
#---------------------------------------------------------------------------------------------------------------
# Returns a block for a key
def block_for_key(key, cache = true)
# Cleanup the cache if asked to
key_range = [ block_start_for_key(key), block_end_for_key(key) ]
block_cache_key = "%d-%d" % key_range
if cache
cached_block = get_cached_block(block_cache_key)
return cached_block if cached_block
end
# Fetch cached value or load from db
block = begin
sql = "SELECT * FROM #{map_table} WHERE start_id = #{key_range.first} AND end_id = #{key_range.last} LIMIT 1"
connection.select_one(sql, 'Find a shard block')
end
set_cached_block(block_cache_key, block)
return block
end
#---------------------------------------------------------------------------------------------------------------
def get_cached_block(block_cache_key)
@blocks_cache.read("#{@blocks_cache_prefix}#{block_cache_key}")
end
def set_cached_block(block_cache_key, block)
@blocks_cache.write("#{@blocks_cache_prefix}#{block_cache_key}", block)
end
#---------------------------------------------------------------------------------------------------------------
# Load group info
def group_info_by_id(group_id, cache = true)
# Cleanup the cache if asked to
@group_info_cache[group_id] = nil unless cache
# Either load from cache or from db
@group_info_cache[group_id] ||= begin
prepare_shard_models
Group.find_by_id(group_id)
end
end
# Load shard info
def shard_info_by_id(shard_id, cache = true)
# Cleanup the cache if asked to
@shard_info_cache[shard_id] = nil unless cache
# Either load from cache or from db
@shard_info_cache[shard_id] ||= begin
prepare_shard_models
Shard.find_by_id(shard_id)
end
end
# Load shard info using mapping info for a group
def shard_info_by_group_id(group_id)
# Load group
group_info = group_info_by_id(group_id)
raise ArgumentError, "Invalid group_id: #{group_id}" unless group_info
shard_info = shard_info_by_id(group_info.shard_id)
raise ArgumentError, "Invalid shard_id: #{group_info.shard_id}" unless shard_info
return shard_info
end
#---------------------------------------------------------------------------------------------------------------
def allocate_new_block_for_key(key)
# Can't find any groups to use for blocks allocation!
return nil unless group = least_loaded_group
# Figure out block limits
start_id = block_start_for_key(key)
end_id = block_end_for_key(key)
# Try to insert a new mapping (ignore duplicate key errors)
sql = <<-SQL
INSERT IGNORE INTO #{map_table}
SET start_id = #{start_id},
end_id = #{end_id},
group_id = #{group.id},
block_size = #{block_size},
created_at = NOW(),
updated_at = NOW()
SQL
connection.execute(sql, "Allocate new block")
# Increment the blocks counter on the shard
Group.update_counters(group.id, :blocks_count => +1)
# Retry block search after creation
block_for_key(key)
end
def least_loaded_group
prepare_shard_models
# Select group
group = Group.first(:conditions => { :enabled => true, :open => true }, :order => 'blocks_count ASC')
raise "Can't find any tablegroups to use for blocks allocation!" unless group
return group
end
#---------------------------------------------------------------------------------------------------------------
def block_start_for_key(key)
block_size.to_i * (key.to_i / block_size.to_i)
end
def block_end_for_key(key)
block_size.to_i + block_start_for_key(key)
end
#---------------------------------------------------------------------------------------------------------------
# Create configuration (use mapping connection as a template)
def shard_connection_config(shard, group_id)
# Format connection name
shard_name = "db_charmer_db_block_group_map_#{name}_s%d_g%d" % [ shard.id, group_id]
# Here we get the mapping connection's configuration
# They do not expose configs so we hack in and get the instance var
# FIXME: Find a better way, maybe move config method to our ar extenstions
connection.instance_variable_get(:@config).clone.merge(
# Name for the connection factory
:connection_name => shard_name,
# Connection params
:host => shard.db_host,
:port => shard.db_port,
:username => shard.db_user,
:password => shard.db_pass,
:database => group_database_name(shard, group_id)
)
end
def group_database_name(shard, group_id)
"%s_%05d" % [ shard.db_name_prefix, group_id ]
end
#---------------------------------------------------------------------------------------------------------------
def create_shard(params)
params = params.symbolize_keys
[ :db_host, :db_port, :db_user, :db_pass, :db_name_prefix ].each do |arg|
raise ArgumentError, "Missing required parameter: #{arg}" unless params[arg]
end
# Prepare model
prepare_shard_models
# Create the record
Shard.create! do |shard|
shard.db_host = params[:db_host]
shard.db_port = params[:db_port]
shard.db_user = params[:db_user]
shard.db_pass = params[:db_pass]
shard.db_name_prefix = params[:db_name_prefix]
end
end
def shard_connections
# Find all groups
prepare_shard_models
groups = Group.all(:conditions => { :enabled => true }, :include => :shard)
# Map them to shards
groups.map { |group| shard_connection_config(group.shard, group.id) }
end
# Prepare model for working with our shards table
def prepare_shard_models
Shard.set_table_name(shards_table)
Shard.switch_connection_to(connection)
Group.set_table_name(groups_table)
Group.switch_connection_to(connection)
end
end
end
end
end
================================================
FILE: lib/db_charmer/sharding/method/db_block_map.rb
================================================
# This is a more sophisticated sharding method based on a database-backed
# blocks map that holds block-shard associations. It automatically
# creates new blocks for new keys and assigns them to shards.
#
module DbCharmer
module Sharding
module Method
class DbBlockMap
# Sharder name
attr_accessor :name
# Mapping db connection
attr_accessor :connection, :connection_name
# Mapping table name
attr_accessor :map_table
# Shards table name
attr_accessor :shards_table
# Sharding keys block size
attr_accessor :block_size
def initialize(config)
@name = config[:name] or raise(ArgumentError, "Missing required :name parameter!")
@connection = DbCharmer::ConnectionFactory.connect(config[:connection], true)
@block_size = (config[:block_size] || 10000).to_i
@map_table = config[:map_table] or raise(ArgumentError, "Missing required :map_table parameter!")
@shards_table = config[:shards_table] or raise(ArgumentError, "Missing required :shards_table parameter!")
# Local caches
@shard_info_cache = {}
@blocks_cache = Rails.cache
@blocks_cache_prefix = config[:blocks_cache_prefix] || "#{@name}_block:"
end
def shard_for_key(key)
block = block_for_key(key)
begin
# Auto-allocate new blocks
block ||= allocate_new_block_for_key(key)
rescue ::ActiveRecord::StatementInvalid => e
raise unless e.message.include?('Duplicate entry')
block = block_for_key(key)
end
raise ArgumentError, "Invalid key value, no shards found for this key and could not create a new block!" unless block
# Bail if no shard found
shard_id = block['shard_id'].to_i
shard_info = shard_info_by_id(shard_id)
raise ArgumentError, "Invalid shard_id: #{shard_id}" unless shard_info
# Get config
shard_connection_config(shard_info)
end
class ShardInfo < ::ActiveRecord::Base
validates_presence_of :db_host
validates_presence_of :db_port
validates_presence_of :db_user
validates_presence_of :db_pass
validates_presence_of :db_name
end
# Returns a block for a key
def block_for_key(key, cache = true)
# Cleanup the cache if asked to
key_range = [ block_start_for_key(key), block_end_for_key(key) ]
block_cache_key = "%d-%d" % key_range
if cache
cached_block = get_cached_block(block_cache_key)
return cached_block if cached_block
end
# Fetch cached value or load from db
block = begin
sql = "SELECT * FROM #{map_table} WHERE start_id = #{key_range.first} AND end_id = #{key_range.last} LIMIT 1"
connection.select_one(sql, 'Find a shard block')
end
set_cached_block(block_cache_key, block)
return block
end
def get_cached_block(block_cache_key)
@blocks_cache.read("#{@blocks_cache_prefix}#{block_cache_key}")
end
def set_cached_block(block_cache_key, block)
@blocks_cache.write("#{@blocks_cache_prefix}#{block_cache_key}", block)
end
# Load shard info
def shard_info_by_id(shard_id, cache = true)
# Cleanup the cache if asked to
@shard_info_cache[shard_id] = nil unless cache
# Either load from cache or from db
@shard_info_cache[shard_id] ||= begin
prepare_shard_model
ShardInfo.find_by_id(shard_id)
end
end
def allocate_new_block_for_key(key)
# Can't find any shards to use for blocks allocation!
return nil unless shard = least_loaded_shard
# Figure out block limits
start_id = block_start_for_key(key)
end_id = block_end_for_key(key)
# Try to insert a new mapping (ignore duplicate key errors)
sql = <<-SQL
INSERT INTO #{map_table}
SET start_id = #{start_id},
end_id = #{end_id},
shard_id = #{shard.id},
block_size = #{block_size},
created_at = NOW(),
updated_at = NOW()
SQL
connection.execute(sql, "Allocate new block")
# Increment the blocks counter on the shard
ShardInfo.update_counters(shard.id, :blocks_count => +1)
# Retry block search after creation
block_for_key(key)
end
def least_loaded_shard
prepare_shard_model
# Select shard
shard = ShardInfo.all(:conditions => { :enabled => true, :open => true }, :order => 'blocks_count ASC', :limit => 1).first
raise "Can't find any shards to use for blocks allocation!" unless shard
return shard
end
def block_start_for_key(key)
block_size.to_i * (key.to_i / block_size.to_i)
end
def block_end_for_key(key)
block_size.to_i + block_start_for_key(key)
end
# Create configuration (use mapping connection as a template)
def shard_connection_config(shard)
# Format connection name
shard_name = "db_charmer_db_block_map_#{name}_shard_%05d" % shard.id
# Here we get the mapping connection's configuration
# They do not expose configs so we hack in and get the instance var
# FIXME: Find a better way, maybe move config method to our ar extenstions
connection.instance_variable_get(:@config).clone.merge(
# Name for the connection factory
:connection_name => shard_name,
# Connection params
:host => shard.db_host,
:port => shard.db_port,
:username => shard.db_user,
:password => shard.db_pass,
:database => shard.db_name
)
end
def create_shard(params)
params = params.symbolize_keys
[ :db_host, :db_port, :db_user, :db_pass, :db_name ].each do |arg|
raise ArgumentError, "Missing required parameter: #{arg}" unless params[arg]
end
# Prepare model
prepare_shard_model
# Create the record
ShardInfo.create! do |shard|
shard.db_host = params[:db_host]
shard.db_port = params[:db_port]
shard.db_user = params[:db_user]
shard.db_pass = params[:db_pass]
shard.db_name = params[:db_name]
end
end
def shard_connections
# Find all shards
prepare_shard_model
shards = ShardInfo.all(:conditions => { :enabled => true })
# Map them to connections
shards.map { |shard| shard_connection_config(shard) }
end
# Prepare model for working with our shards table
def prepare_shard_model
ShardInfo.table_name = shards_table
ShardInfo.switch_connection_to(connection)
end
end
end
end
end
================================================
FILE: lib/db_charmer/sharding/method/hash_map.rb
================================================
module DbCharmer
module Sharding
module Method
class HashMap
attr_accessor :map
def initialize(config)
@map = config[:map].clone or raise ArgumentError, "No :map defined!"
end
def shard_for_key(key)
res = map[key] || map[:default]
raise ArgumentError, "Invalid key value, no shards found for this key!" unless res
return res
end
def support_default_shard?
map.has_key?(:default)
end
end
end
end
end
================================================
FILE: lib/db_charmer/sharding/method/range.rb
================================================
module DbCharmer
module Sharding
module Method
class Range
attr_accessor :ranges
def initialize(config)
@ranges = config[:ranges] ? config[:ranges].clone : raise(ArgumentError, "No :ranges defined!")
end
def shard_for_key(key)
return ranges[:default] if key == :default
ranges.each do |range, shard|
next if range == :default
return shard if range.member?(key.to_i)
end
return ranges[:default] if ranges[:default]
raise ArgumentError, "Invalid key value, no shards found for this key!"
end
def support_default_shard?
ranges.has_key?(:default)
end
def shard_connections
ranges.values.uniq
end
end
end
end
end
================================================
FILE: lib/db_charmer/sharding/method.rb
================================================
module DbCharmer
module Sharding
module Method
autoload :Range, 'db_charmer/sharding/method/range'
autoload :HashMap, 'db_charmer/sharding/method/hash_map'
autoload :DbBlockMap, 'db_charmer/sharding/method/db_block_map'
autoload :DbBlockGroupMap, 'db_charmer/sharding/method/db_block_group_map'
end
end
end
================================================
FILE: lib/db_charmer/sharding/stub_connection.rb
================================================
# This is a simple proxy class used as a default connection on sharded models
#
# The idea is to proxy all utility method calls to a real connection (set by
# the +set_real_connection+ method when we switch shards) and fail on real
# database querying calls forcing users to switch shard connections.
#
module DbCharmer
module Sharding
class StubConnection
attr_accessor :sharded_connection
def initialize(sharded_connection)
@sharded_connection = sharded_connection
@real_conn = nil
end
def set_real_connection(real_conn)
@real_conn = real_conn
end
def db_charmer_connection_name
"StubConnection"
end
def real_connection
# Return memoized real connection
return @real_conn if @real_conn
# If sharded connection supports shards enumeration, get the first shard
conn = sharded_connection.shard_connections.try(:first)
# If we do not have real connection yet, try to use the default one (if it is supported by the sharder)
conn ||= sharded_connection.sharder.shard_for_key(:default) if sharded_connection.support_default_shard?
# Get connection proxy for our real connection
return nil unless conn
@real_conn = ::ActiveRecord::Base.coerce_to_connection_proxy(conn, DbCharmer.connections_should_exist?)
end
def respond_to?(method_name, include_all = false)
return true if super
return false if real_connection.object_id == self.object_id
real_connection.respond_to?(method_name, include_all)
end
def method_missing(meth, *args, &block)
# Fail on database statements
if ::ActiveRecord::ConnectionAdapters::DatabaseStatements.instance_methods.member?(meth.to_s)
raise ::ActiveRecord::ConnectionNotEstablished, "You have to switch connection on your model before using it!"
end
# Fail if no connection has been established yet
unless real_connection
raise ::ActiveRecord::ConnectionNotEstablished, "No real connection to proxy this method to!"
end
if real_connection.kind_of?(DbCharmer::Sharding::StubConnection)
raise ::ActiveRecord::ConnectionNotEstablished, "You have to switch connection on your model before using it!"
end
# Proxy the call to our real connection target
real_connection.__send__(meth, *args, &block)
end
end
end
end
================================================
FILE: lib/db_charmer/sharding.rb
================================================
module DbCharmer
module Sharding
autoload :Connection, 'db_charmer/sharding/connection'
autoload :StubConnection, 'db_charmer/sharding/stub_connection'
autoload :Method, 'db_charmer/sharding/method'
@@sharded_connections = {}
def self.register_connection(config)
name = config[:name] or raise ArgumentError, "No :name in connection!"
@@sharded_connections[name] = DbCharmer::Sharding::Connection.new(config)
end
def self.sharded_connection(name)
@@sharded_connections[name] or raise ArgumentError, "Invalid sharded connection name!"
end
end
end
================================================
FILE: lib/db_charmer/tasks/databases.rake
================================================
namespace :db_charmer do
namespace :create do
desc 'Create all the local databases defined in config/database.yml'
task :all => "db:load_config" do
::ActiveRecord::Base.configurations.each_value do |config|
# Skip entries that don't have a database key, such as the first entry here:
#
# defaults: &defaults
# adapter: mysql
# username: root
# password:
# host: localhost
#
# development:
# database: blog_development
# <<: *defaults
next unless config['database']
# Only connect to local databases
local_database?(config) { create_core_and_sub_database(config) }
end
end
end
desc 'Create the databases defined in config/database.yml for the current RAILS_ENV'
task :create => "db:load_config" do
create_core_and_sub_database(ActiveRecord::Base.configurations[RAILS_ENV])
end
def create_core_and_sub_database(config)
create_database(config)
config.each_value do | sub_config |
next unless sub_config.is_a?(Hash)
next unless sub_config['database']
create_database(sub_config)
end
end
namespace :drop do
desc 'Drops all the local databases defined in config/database.yml'
task :all => "db:load_config" do
::ActiveRecord::Base.configurations.each_value do |config|
# Skip entries that don't have a database key
next unless config['database']
# Only connect to local databases
local_database?(config) { drop_core_and_sub_database(config) }
end
end
end
desc 'Drops the database for the current RAILS_ENV'
task :drop => "db:load_config" do
config = ::ActiveRecord::Base.configurations[RAILS_ENV || 'development']
begin
drop_core_and_sub_database(config)
rescue Exception => e
puts "Couldn't drop #{config['database']} : #{e.inspect}"
end
end
def local_database?(config, &block)
if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
yield
else
puts "This task only modifies local databases. #{config['database']} is on a remote host."
end
end
end
def drop_core_and_sub_database(config)
begin
drop_database(config)
rescue
$stderr.puts "#{config['database']} not exists"
end
config.each_value do | sub_config |
next unless sub_config.is_a?(Hash)
next unless sub_config['database']
begin
drop_database(sub_config)
rescue
$stderr.puts "#{config['database']} not exists"
end
end
end
================================================
FILE: lib/db_charmer/version.rb
================================================
module DbCharmer
module Version
MAJOR = 1
MINOR = 9
PATCH = 1
BUILD = nil
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
end
end
================================================
FILE: lib/db_charmer/with_remapped_databases.rb
================================================
module DbCharmer
def self.with_remapped_databases(mappings, &proc)
old_mappings = ::ActiveRecord::Base.db_charmer_database_remappings
begin
::ActiveRecord::Base.db_charmer_database_remappings = mappings
if mappings[:master] || mappings['master']
with_all_hijacked(&proc)
else
proc.call
end
ensure
::ActiveRecord::Base.db_charmer_database_remappings = old_mappings
end
end
def self.hijack_new_classes?
!! Thread.current[:db_charmer_hijack_new_classes]
end
private
def self.with_all_hijacked
old_hijack_new_classes = Thread.current[:db_charmer_hijack_new_classes]
begin
Thread.current[:db_charmer_hijack_new_classes] = true
subclasses_method = DbCharmer.rails3? ? :descendants : :subclasses
::ActiveRecord::Base.send(subclasses_method).each do |subclass|
subclass.hijack_connection!
end
yield
ensure
Thread.current[:db_charmer_hijack_new_classes] = old_hijack_new_classes
end
end
end
#---------------------------------------------------------------------------------------------------
# Hijack connection on all new AR classes when we're in a block with main AR connection remapped
class ActiveRecord::Base
class << self
def inherited_with_hijacking(subclass)
out = inherited_without_hijacking(subclass)
hijack_connection! if DbCharmer.hijack_new_classes?
out
end
alias_method_chain :inherited, :hijacking
end
end
================================================
FILE: lib/db_charmer.rb
================================================
# In Rails 2.2 they did not add it to the autoload so it won't work w/o this require
require 'active_record/version' unless defined?(::ActiveRecord::VERSION::MAJOR)
require 'active_support/core_ext'
#---------------------------------------------------------------------------------------------------
module DbCharmer
# Configure autoload
autoload :Sharding, 'db_charmer/sharding'
autoload :Version, 'db_charmer/version'
module ActionController
autoload :ForceSlaveReads, 'db_charmer/action_controller/force_slave_reads'
end
#-------------------------------------------------------------------------------------------------
# Used in all Rails3-specific places
def self.rails3?
::ActiveRecord::VERSION::MAJOR > 2
end
# Used in all Rails3.1-specific places
def self.rails31?
rails3? && ::ActiveRecord::VERSION::MINOR >= 1
end
# Used in all Rails2-specific places
def self.rails2?
::ActiveRecord::VERSION::MAJOR == 2
end
# Detect broken Rails version
def self.rails324?
ActiveRecord::VERSION::STRING == '3.2.4'
end
#-------------------------------------------------------------------------------------------------
# Returns true if we're running within a Rails project
def self.running_with_rails?
defined?(Rails) && Rails.respond_to?(:env)
end
# Returns current environment name based on Rails or Rack environment variables
def self.detect_environment
return Rails.env if running_with_rails?
ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
end
# Try to detect current environment or use development by default
@@env = DbCharmer.detect_environment
mattr_accessor :env
#-------------------------------------------------------------------------------------------------
# Accessors
@@connections_should_exist = true
mattr_accessor :connections_should_exist
def self.connections_should_exist?
!! connections_should_exist
end
#-------------------------------------------------------------------------------------------------
def self.logger
return Rails.logger if running_with_rails?
@@logger ||= Logger.new(STDERR)
end
#-------------------------------------------------------------------------------------------------
# Extend ActionController to support forcing slave reads
def self.enable_controller_magic!
::ActionController::Base.extend(DbCharmer::ActionController::ForceSlaveReads::ClassMethods)
::ActionController::Base.send(:include, DbCharmer::ActionController::ForceSlaveReads::InstanceMethods)
end
end
#---------------------------------------------------------------------------------------------------
# Print warning about the broken Rails 2.3.4
puts "WARNING: Rails 3.2.4 is not officially supported by DbCharmer. Please upgrade." if DbCharmer.rails324?
#---------------------------------------------------------------------------------------------------
# Add useful methods to global object
require 'db_charmer/core_extensions'
require 'db_charmer/connection_factory'
require 'db_charmer/connection_proxy'
require 'db_charmer/force_slave_reads'
require 'db_charmer/with_remapped_databases'
if DbCharmer.rails3?
require "db_charmer/railtie"
end
#---------------------------------------------------------------------------------------------------
# Add our custom class-level attributes to AR models
require 'db_charmer/active_record/class_attributes'
require 'active_record'
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::ClassAttributes)
#---------------------------------------------------------------------------------------------------
# Enable connections switching in AR
require 'db_charmer/active_record/connection_switching'
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::ConnectionSwitching)
#---------------------------------------------------------------------------------------------------
# Enable AR logging extensions
if DbCharmer.rails3?
require 'db_charmer/rails3/abstract_adapter/connection_name'
require 'db_charmer/rails3/active_record/log_subscriber'
ActiveRecord::LogSubscriber.send(:include, DbCharmer::ActiveRecord::LogSubscriber)
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, DbCharmer::AbstractAdapter::ConnectionName)
else
require 'db_charmer/rails2/abstract_adapter/log_formatting'
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, DbCharmer::AbstractAdapter::LogFormatting)
end
#---------------------------------------------------------------------------------------------------
# Enable connection proxy in AR
require 'db_charmer/active_record/multi_db_proxy'
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::MultiDbProxy::ClassMethods)
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::MultiDbProxy::MasterSlaveClassMethods)
ActiveRecord::Base.send(:include, DbCharmer::ActiveRecord::MultiDbProxy::InstanceMethods)
#---------------------------------------------------------------------------------------------------
# Enable connection proxy for relations
if DbCharmer.rails3?
require 'db_charmer/rails3/active_record/relation_method'
require 'db_charmer/rails3/active_record/relation/connection_routing'
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::RelationMethod)
ActiveRecord::Relation.send(:include, DbCharmer::ActiveRecord::Relation::ConnectionRouting)
end
#---------------------------------------------------------------------------------------------------
# Enable connection proxy for scopes (rails 2.x only)
if DbCharmer.rails2?
require 'db_charmer/rails2/active_record/named_scope/scope_proxy'
ActiveRecord::NamedScope::Scope.send(:include, DbCharmer::ActiveRecord::NamedScope::ScopeProxy)
end
#---------------------------------------------------------------------------------------------------
# Enable connection proxy for associations
# WARNING: Inject methods to association class right here because they proxy +include+ calls
# somewhere else, which means we could not use +include+ method here
association_proxy_class = DbCharmer.rails31? ? ActiveRecord::Associations::CollectionProxy :
ActiveRecord::Associations::AssociationProxy
association_proxy_class.class_eval do
def proxy?
true
end
if DbCharmer.rails31?
def on_db(con, proxy_target = nil, &block)
proxy_target ||= self
@association.klass.on_db(con, proxy_target, &block)
end
def on_slave(con = nil, &block)
@association.klass.on_slave(con, self, &block)
end
def on_master(&block)
@association.klass.on_master(self, &block)
end
else
def on_db(con, proxy_target = nil, &block)
proxy_target ||= self
@reflection.klass.on_db(con, proxy_target, &block)
end
def on_slave(con = nil, &block)
@reflection.klass.on_slave(con, self, &block)
end
def on_master(&block)
@reflection.klass.on_master(self, &block)
end
end
end
#---------------------------------------------------------------------------------------------------
# Enable multi-db migrations
require 'db_charmer/active_record/migration/multi_db_migrations'
ActiveRecord::Migration.send(:include, DbCharmer::ActiveRecord::Migration::MultiDbMigrations)
if DbCharmer.rails31?
require 'db_charmer/rails31/active_record/migration/command_recorder'
ActiveRecord::Migration::CommandRecorder.send(:include, DbCharmer::ActiveRecord::Migration::CommandRecorder)
end
#---------------------------------------------------------------------------------------------------
# Enable the magic
if DbCharmer.rails3?
require 'db_charmer/rails3/active_record/master_slave_routing'
else
require 'db_charmer/rails2/active_record/master_slave_routing'
end
require 'db_charmer/active_record/sharding'
require 'db_charmer/active_record/db_magic'
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::DbMagic)
#---------------------------------------------------------------------------------------------------
# Setup association preload magic
if DbCharmer.rails31?
require 'db_charmer/rails31/active_record/preloader/association'
ActiveRecord::Associations::Preloader::Association.send(:include, DbCharmer::ActiveRecord::Preloader::Association)
require 'db_charmer/rails31/active_record/preloader/has_and_belongs_to_many'
ActiveRecord::Associations::Preloader::HasAndBelongsToMany.send(:include, DbCharmer::ActiveRecord::Preloader::HasAndBelongsToMany)
else
require 'db_charmer/active_record/association_preload'
ActiveRecord::Base.extend(DbCharmer::ActiveRecord::AssociationPreload)
# Open up really useful API method
ActiveRecord::AssociationPreload::ClassMethods.send(:public, :preload_associations)
end
================================================
FILE: test-project/.gitignore
================================================
log/*.log
db/schema.*
.idea
TAGS
config/database.yml
.bundle
tmp
.DS_Store
vendor
Gemfile.lock
doc
================================================
FILE: test-project/.rspec
================================================
--colour
--format documentation
================================================
FILE: test-project/Gemfile
================================================
source 'http://rubygems.org'
gem 'rake', "0.9.2.2"
gem 'mysql', "2.8.1"
gem 'rspec', '< 3.0'
gem 'rspec-core', '< 3.0'
gem 'rspec-rails', '< 3.0'
# Load DbCharmer as a gem
if ENV['DB_CHARMER_GEM'].to_s == ''
gem_path = File.expand_path(File.dirname(File.dirname(__FILE__)))
puts "Using on-disk db-charmer code from '#{gem_path}'..."
gem 'db-charmer', :path => gem_path, :require => 'db_charmer'
else
puts "Using db-charmer gem: #{ENV['DB_CHARMER_GEM']}..."
gem 'db-charmer', ENV['DB_CHARMER_GEM'], :require => 'db_charmer'
end
# Detect Rails version we need to use
rails_version_file = File.expand_path("../.rails-version", __FILE__)
version = File.exists?(rails_version_file) && File.read(rails_version_file).chomp
version ||= ENV['RAILS_VERSION']
version ||= '3-2-stable'
# Require gems for selected rails version
case version
when /master/
gem "rails", :git => "git://github.com/rails/rails.git"
gem "arel", :git => "git://github.com/rails/arel.git"
gem "journey", :git => "git://github.com/rails/journey.git"
when /3-0-stable/
gem "rails", :git => "git://github.com/rails/rails.git", :branch => "3-0-stable"
gem "arel", :git => "git://github.com/rails/arel.git", :branch => "2-0-stable"
when /3-1-stable/
gem "rails", :git => "git://github.com/rails/rails.git", :branch => "3-1-stable"
when /3-2-stable/
gem "rails", :git => "git://github.com/rails/rails.git", :branch => "3-2-stable"
else
gem "rails", version
end
================================================
FILE: test-project/Rakefile
================================================
ENV['RAILS_ENV'] = 'test'
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
require 'rake'
DbCharmerSandbox::Application.load_tasks
================================================
FILE: test-project/TODO
================================================
Functionality:
- Add a controller wrapper to force all queries to the master (thanks mascohism for the idea)
Docs:
- Document (make more obvious) multi-db migrations code with migration_connections_should_exist
- Document the fact, that all queries in a transaction go to the master
================================================
FILE: test-project/app/controllers/application_controller.rb
================================================
class ApplicationController < ActionController::Base
protect_from_forgery
end
================================================
FILE: test-project/app/controllers/posts_controller.rb
================================================
class PostsController < ApplicationController
force_slave_reads :only => [ :index, :show, :new ], :except => :new
# We'll use this to make sure count query would be sent to a proper server
before_filter do
Post.count
end
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
def new
@post = Post.new
end
def create
post = Post.create!(params[:post])
redirect_to(post_url(post))
end
def destroy
Post.delete(params[:id])
redirect_to(:action => :index)
end
end
================================================
FILE: test-project/app/helpers/application_helper.rb
================================================
module ApplicationHelper
end
================================================
FILE: test-project/app/models/avatar.rb
================================================
class Avatar < ActiveRecord::Base
end
================================================
FILE: test-project/app/models/car.rb
================================================
class Car < ActiveRecord::Base
db_magic :slave => :slave01
end
================================================
FILE: test-project/app/models/categories_posts.rb
================================================
class CategoriesPosts < ActiveRecord::Base
belongs_to :category
belongs_to :post
end
================================================
FILE: test-project/app/models/category.rb
================================================
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
================================================
FILE: test-project/app/models/comment.rb
================================================
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
================================================
FILE: test-project/app/models/event.rb
================================================
class Event < ActiveRecord::Base
self.table_name = :timeline_events
db_magic :sharded => {
:key => :to_uid,
:sharded_connection => :social
}
end
================================================
FILE: test-project/app/models/ford.rb
================================================
class Ford < Car
end
================================================
FILE: test-project/app/models/house.rb
================================================
class House < ActiveRecord::Base
db_magic :slave => :slave01, :force_slave_reads => false
end
================================================
FILE: test-project/app/models/log_record.rb
================================================
class LogRecord < ActiveRecord::Base
db_magic :connection => :logs
belongs_to :user
end
================================================
FILE: test-project/app/models/post.rb
================================================
class Post < ActiveRecord::Base
DB_MAGIC_DEFAULT_PARAMS = { :slave => :slave01, :force_slave_reads => false }
db_magic DB_MAGIC_DEFAULT_PARAMS
belongs_to :user
has_and_belongs_to_many :categories
def self.define_scope(*args, &block)
if DbCharmer.rails3?
scope(*args, &block)
else
named_scope(*args, &block)
end
end
define_scope :windows_posts, :conditions => "title like '%win%'"
define_scope :dummy_scope, :conditions => '1'
end
================================================
FILE: test-project/app/models/range_sharded_model.rb
================================================
class RangeShardedModel < ActiveRecord::Base
db_magic :sharded => {
:key => :id,
:sharded_connection => :texts
}
end
================================================
FILE: test-project/app/models/toyota.rb
================================================
class Toyota < Car
end
================================================
FILE: test-project/app/models/user.rb
================================================
class User < ActiveRecord::Base
has_many :posts
has_many :log_records
has_one :avatar
end
================================================
FILE: test-project/app/views/layouts/application.html.erb
================================================
DbCharmerSandbox
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
<%= yield %>
================================================
FILE: test-project/app/views/posts/index.html.erb
================================================
Posts
<% @posts.each do |post| %>
Post #<%= post.id %>
<%= post.inspect %>
<% end %>
================================================
FILE: test-project/app/views/posts/new.html.erb
================================================
Posts#new
Find me in app/views/posts/new.html.erb
================================================
FILE: test-project/app/views/posts/show.html.erb
================================================
Posts#show
Find me in app/views/posts/show.html.erb
================================================
FILE: test-project/config/application.rb
================================================
require File.expand_path('../boot', __FILE__)
require 'rails/all'
# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)
module DbCharmerSandbox
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# JavaScript files you want as :defaults (application.js is always included).
# config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
end
end
================================================
FILE: test-project/config/boot.rb
================================================
require 'rubygems'
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
================================================
FILE: test-project/config/database.yml.example
================================================
common: &common
adapter: mysql
encoding: utf8
reconnect: false
pool: 10
username: root
password:
#----------------------------------------------------------------
test:
<<: *common
database: db_charmer_sandbox_test
# logs database
logs:
<<: *common
database: db_charmer_logs_test
# slave database
slave01:
<<: *common
username: db_charmer_ro
database: db_charmer_sandbox_test
user_master:
<<: *common
database: db_charmer_sandbox_test
# shard mapping db
social_shard_info:
<<: *common
database: db_charmer_sandbox_test
# for migrations only
social_shard01:
<<: *common
database: db_charmer_events_test_shard01
# for migrations only
social_shard02:
<<: *common
database: db_charmer_events_test_shard02
#----------------------------------------------------------------
test22:
<<: *common
database: db_charmer_sandbox22_test
# logs database
logs:
<<: *common
database: db_charmer_logs22_test
# slave database
slave01:
<<: *common
username: db_charmer_ro
database: db_charmer_sandbox22_test
user_master:
<<: *common
database: db_charmer_sandbox22_test
# shard mapping db
social_shard_info:
<<: *common
database: db_charmer_sandbox22_test
# for migrations only
social_shard01:
<<: *common
database: db_charmer_events22_test_shard01
# for migrations only
social_shard02:
<<: *common
database: db_charmer_events22_test_shard02
================================================
FILE: test-project/config/environment.rb
================================================
# Load the rails application
require File.expand_path('../application', __FILE__)
# Initialize the rails application
DbCharmerSandbox::Application.initialize!
================================================
FILE: test-project/config/environments/test.rb
================================================
DbCharmerSandbox::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
end
================================================
FILE: test-project/config/initializers/backtrace_silencers.rb
================================================
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!
================================================
FILE: test-project/config/initializers/db_charmer.rb
================================================
DbCharmer.connections_should_exist = false # Since we are not in production
DbCharmer.enable_controller_magic!
================================================
FILE: test-project/config/initializers/secret_token.rb
================================================
# Be sure to restart your server when you modify this file.
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
DbCharmerSandbox::Application.config.secret_token = 'bf10223f7ec7f4b2f2c6f98545ee0d172d5fe052deeba6e416e34e0a7534bc2f5a9983f331b5a799ac6544bf99d906c2a5a3bee8260d4cb985f2c096527aa3ad'
================================================
FILE: test-project/config/initializers/session_store.rb
================================================
# Be sure to restart your server when you modify this file.
DbCharmerSandbox::Application.config.session_store :cookie_store, :key => '_db-charmer-sandbox_session'
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails generate session_migration")
# DbCharmerSandbox::Application.config.session_store :active_record_store
================================================
FILE: test-project/config/initializers/sharding.rb
================================================
# Range-based shards for testing
TEXTS_SHARDING_RANGES = {
0...100 => :shard1,
100..200 => :shard2,
:default => :shard3
}
DbCharmer::Sharding.register_connection(
:name => :texts,
:method => :range,
:ranges => TEXTS_SHARDING_RANGES
)
#------------------------------------------------
# Db blocks map sharding for testing
SOCIAL_SHARDING = DbCharmer::Sharding.register_connection(
:name => :social,
:method => :db_block_map,
:block_size => 10,
:map_table => :event_shards_map,
:shards_table => :event_shards_info,
:connection => :social_shard_info
)
================================================
FILE: test-project/config/locales/en.yml
================================================
# Sample localization file for English. Add more files in this directory for other locales.
# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
hello: "Hello world"
================================================
FILE: test-project/config/routes.rb
================================================
DbCharmerSandbox::Application.routes.draw do
# The priority is based upon order of creation:
# first created -> highest priority.
# Resource routes
resources :posts
resources :cars
# Sample of regular route:
# match 'products/:id' => 'catalog#view'
# Keep in mind you can assign values other than :controller and :action
# Sample of named route:
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
# This route can be invoked with purchase_url(:id => product.id)
# Sample resource route (maps HTTP verbs to controller actions automatically):
# resources :products
# Sample resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Sample resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Sample resource route with more complex sub-resources
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', :on => :collection
# end
# end
# Sample resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
# root :to => "welcome#index"
# See how all your routes lay out with "rake routes"
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
match ':controller(/:action(/:id(.:format)))'
end
================================================
FILE: test-project/db/create_databases.sql
================================================
drop database if exists db_charmer_sandbox_test;
create database db_charmer_sandbox_test;
drop database if exists db_charmer_logs_test;
create database db_charmer_logs_test;
drop database if exists db_charmer_events_test_shard01;
create database db_charmer_events_test_shard01;
drop database if exists db_charmer_events_test_shard02;
create database db_charmer_events_test_shard02;
grant all privileges on db_charmer_sandbox_test.* to 'db_charmer_ro'@'localhost';
================================================
FILE: test-project/db/migrate/20090810013829_create_log_records.rb
================================================
class CreateLogRecords < ActiveRecord::Migration
db_magic :connection => :logs
def self.up
create_table :log_records do |t|
t.integer :user_id
t.string :level
t.string :message
t.timestamps
end
end
def self.down
drop_table :log_records
end
end
================================================
FILE: test-project/db/migrate/20090810013922_create_posts.rb
================================================
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.text :body
t.integer :user_id
t.timestamps
end
end
def self.down
drop_table :posts
end
end
================================================
FILE: test-project/db/migrate/20090810221944_create_users.rb
================================================
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :login
t.string :password
t.timestamps
end
end
def self.down
drop_table :users
end
end
================================================
FILE: test-project/db/migrate/20100305234245_create_categories.rb
================================================
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :categories
end
end
================================================
FILE: test-project/db/migrate/20100305234340_create_categories_posts.rb
================================================
class CreateCategoriesPosts < ActiveRecord::Migration
def self.up
pk_in_join_table = !DbCharmer.rails3?
create_table :categories_posts, :id => pk_in_join_table do |t|
t.integer :post_id
t.integer :category_id
end
end
def self.down
drop_table :categories_posts
end
end
================================================
FILE: test-project/db/migrate/20100305235831_create_avatars.rb
================================================
class CreateAvatars < ActiveRecord::Migration
def self.up
create_table :avatars do |t|
t.integer :user_id
t.string :name
t.timestamps
end
end
def self.down
drop_table :avatars
end
end
================================================
FILE: test-project/db/migrate/20100328201317_create_sharding_map_tables.rb
================================================
class CreateShardingMapTables < ActiveRecord::Migration
db_magic :connection => :social_shard_info
def self.up
create_table :event_shards_info, :force => true do |t|
t.timestamps
t.string :db_host, :null => false
t.integer :db_port, :null => false, :default => 3306
t.string :db_user, :null => false, :default => 'root'
t.string :db_pass, :null => false, :default => ''
t.string :db_name, :null => false
t.boolean :open, :null => false, :default => false
t.boolean :enabled, :null => false, :default => false
t.integer :blocks_count, :null => false, :default => 0
end
add_index :event_shards_info, [:enabled, :open, :blocks_count], :name => "alloc"
create_table :event_shards_map, :id => false, :force => true do |t|
t.integer :start_id, :null => false
t.integer :end_id, :null => false
t.integer :shard_id, :null => false
t.integer :block_size, :null => false, :default => 0
t.timestamps
end
add_index :event_shards_map, [:start_id, :end_id], :unique => true
add_index :event_shards_map, :shard_id
end
def self.down
drop_table :event_shards_map
drop_table :event_shards_info
end
end
================================================
FILE: test-project/db/migrate/20100330180517_create_event_tables.rb
================================================
class CreateEventTables < ActiveRecord::Migration
# In test environment just use database.yml-defined connections
if Rails.env.test?
db_magic :connections => [ :social_shard01, :social_shard02 ]
else
db_magic :sharded_connection => :social
end
def self.up
sql = <<-SQL
CREATE TABLE `timeline_events` (
`event_id` int(11) NOT NULL AUTO_INCREMENT,
`from_uid` int(11) NOT NULL,
`to_uid` int(11) NOT NULL,
`original_created_at` datetime NOT NULL,
`event_type` int(11) NOT NULL,
`event_data` text,
`replies_count` int(11) NOT NULL DEFAULT '0',
`parent_id` int(11) NOT NULL DEFAULT '0',
`touched_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`on_profile` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`to_uid`,`parent_id`,`touched_at`,`event_id`),
UNIQUE KEY `event_id_and_to_uid_key` (`event_id`,`to_uid`),
KEY `on_profile_index` (`to_uid`,`on_profile`,`touched_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
SQL
execute(sql)
end
def self.down
drop_table :timeline_events
end
end
================================================
FILE: test-project/db/migrate/20100817191548_create_cars.rb
================================================
class CreateCars < ActiveRecord::Migration
def self.up
create_table :cars do |t|
t.string :type
t.string :license
t.timestamps
end
end
def self.down
drop_table :cars
end
end
================================================
FILE: test-project/db/migrate/20111005193941_create_comments.rb
================================================
class CreateComments < ActiveRecord::Migration
def self.up
create_table :comments do |t|
t.string :commentable_type, :null => false
t.integer :commentable_id, :null => false
t.text :body, :null => false
t.timestamps
end
end
def self.down
drop_table :comments
end
end
================================================
FILE: test-project/db/seeds.rb
================================================
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
#
# Examples:
#
# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
# Mayor.create(:name => 'Daley', :city => cities.first)
================================================
FILE: test-project/db/sharding.sql
================================================
-- MySQL dump 10.13 Distrib 5.1.44, for apple-darwin10.2.0 (i386)
--
-- Host: localhost Database: db_charmer_sandbox_test
-- ------------------------------------------------------
-- Server version 5.1.44
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `events_shard_info`
--
DROP TABLE IF EXISTS `events_shard_info`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `events_shard_info` (
`id` int(10) unsigned NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`db_host` varchar(255) NOT NULL,
`db_port` int(10) unsigned NOT NULL DEFAULT '3306',
`db_user` varchar(255) NOT NULL DEFAULT 'root',
`db_pass` varchar(255) NOT NULL DEFAULT '',
`open` tinyint(1) unsigned NOT NULL DEFAULT '0',
`enabled` tinyint(1) unsigned NOT NULL DEFAULT '0',
`blocks_count` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `alloc` (`enabled`,`open`,`blocks_count`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `events_shard_info`
--
LOCK TABLES `events_shard_info` WRITE;
/*!40000 ALTER TABLE `events_shard_info` DISABLE KEYS */;
/*!40000 ALTER TABLE `events_shard_info` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `events_shard_dict`
--
DROP TABLE IF EXISTS `events_shard_dict`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `events_shard_dict` (
`start_id` int(10) unsigned NOT NULL,
`end_id` int(10) unsigned NOT NULL,
`shard_id` int(10) unsigned NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`block_size` int(10) unsigned NOT NULL,
PRIMARY KEY (`start_id`,`end_id`),
KEY `shard_id` (`shard_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `events_shard_dict`
--
LOCK TABLES `events_shard_dict` WRITE;
/*!40000 ALTER TABLE `events_shard_dict` DISABLE KEYS */;
/*!40000 ALTER TABLE `events_shard_dict` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2010-03-22 1:37:30
================================================
FILE: test-project/spec/controllers/posts_controller_spec.rb
================================================
require 'spec_helper'
describe PostsController do
fixtures :posts
# Delete these examples and add some real ones
it "should support db_charmer readonly actions method" do
PostsController.respond_to?(:force_slave_reads).should be_true
end
it "index action should force slave reads" do
PostsController.force_slave_reads_action?(:index).should be_true
end
it "create action should not force slave reads" do
PostsController.force_slave_reads_action?(:create).should be_false
end
describe "GET 'index'" do
context "slave reads enforcing (action is listed in :only)" do
it "should enable enforcing" do
get 'index'
controller.force_slave_reads?.should be_true
end
it "should actually force slave reads" do
Post.connection.should_not_receive(:select_value) # no counts
Post.connection.should_not_receive(:select_all) # no finds
Post.on_slave.connection.should_receive(:select_value).and_return(1)
get 'index'
end
end
end
describe "GET 'show'" do
context "slave reads enforcing (action is listed in :only)" do
it "should enable enforcing" do
get 'show', :id => Post.first.id
controller.force_slave_reads?.should be_true
end
it "should actually force slave reads" do
post = Post.first
Post.connection.should_not_receive(:select_value) # no counts
Post.connection.should_not_receive(:select_all) # no finds
Post.on_slave.connection.should_receive(:select_value).and_return(1)
Post.on_slave.connection.should_receive(:select_all).and_return([post.attributes])
get 'show', :id => post.id
end
end
end
describe "GET 'new'" do
context "slave reads enforcing (action is listed in :except)" do
it "should not enable enforcing" do
get 'new'
controller.force_slave_reads?.should be_false
end
it "should not do any actual enforcing" do
Post.connection.should_receive(:select_value).and_return(0) # count
Post.on_slave.connection.should_not_receive(:select_value) # no counts
Post.on_slave.connection.should_not_receive(:select_all) # no selects
get 'new'
end
end
end
describe "GET 'create'" do
it "should redirect to post url upon successful completion" do
get 'create', :post => { :title => 'xxx', :user_id => 1 }
response.should redirect_to(post_url(Post.last))
end
it "should create a Post record" do
lambda {
get 'create', :post => { :title => 'xxx', :user_id => 1 }
}.should change { Post.count }.by(+1)
end
context "slave reads enforcing (action is not listed in force_slave_reads params)" do
it "should not enable enforcing" do
get 'create'
controller.force_slave_reads?.should_not be_true
end
it "should not do any actual enforcing" do
Post.on_slave.connection.should_not_receive(:select_value)
Post.connection.should_receive(:select_value).once.and_return(1)
get 'create'
end
end
end
describe "GET 'destroy'" do
it "should redurect to index upon completion" do
get 'destroy', :id => Post.first.id
response.should redirect_to(:action => :index)
end
it "should delete a record" do
lambda {
get 'destroy', :id => Post.first.id
}.should change { Post.count }.by(-1)
end
end
end
================================================
FILE: test-project/spec/fixtures/avatars.yml
================================================
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
user_id: 1
name: avatar1
two:
user_id: 2
name: avatar2
================================================
FILE: test-project/spec/fixtures/categories.yml
================================================
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
id: 1
name: one
two:
id: 2
name: two
================================================
FILE: test-project/spec/fixtures/categories_posts.yml
================================================
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one_one:
post_id: 1
category_id: 1
one_two:
post_id: 1
category_id: 2
two_one:
post_id: 2
category_id: 1
windoze_two:
post_id: 4
category_id: 2
================================================
FILE: test-project/spec/fixtures/comments.yml
================================================
avatar:
commentable: one (Avatar)
body: "This is an avatar"
post:
commentable: one (Post)
body: "This is a post"
user:
commentable: one (User)
body: "This is a user"
================================================
FILE: test-project/spec/fixtures/event_shards_info.yml
================================================
shard1:
id: 1
db_host: localhost
db_name: db_charmer_events_test_shard01
open: 1
enabled: 1
blocks_count: 2
created_at: <%= Time.now.to_s(:db) %>
updated_at: <%= Time.now.to_s(:db) %>
shard2:
id: 2
db_host: localhost
db_name: db_charmer_events_test_shard02
open: 1
enabled: 1
blocks_count: 1
created_at: <%= Time.now.to_s(:db) %>
updated_at: <%= Time.now.to_s(:db) %>
empty:
id: 3
db_host: localhost
db_name: db_charmer_events_test_shard01
open: 1
enabled: 1
blocks_count: 0
created_at: <%= Time.now.to_s(:db) %>
updated_at: <%= Time.now.to_s(:db) %>
================================================
FILE: test-project/spec/fixtures/event_shards_map.yml
================================================
block1:
start_id: 0
end_id: 10
shard_id: 1
block_size: 10
created_at: <%= Time.now.to_s(:db) %>
updated_at: <%= Time.now.to_s(:db) %>
block2:
start_id: 10
end_id: 20
shard_id: 2
block_size: 10
created_at: <%= Time.now.to_s(:db) %>
updated_at: <%= Time.now.to_s(:db) %>
block3:
start_id: 20
end_id: 30
shard_id: 1
block_size: 10
created_at: <%= Time.now.to_s(:db) %>
updated_at: <%= Time.now.to_s(:db) %>
================================================
FILE: test-project/spec/fixtures/log_records.yml
================================================
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
level: MyString
message: MyString
two:
level: MyString
message: MyString
================================================
FILE: test-project/spec/fixtures/posts.yml
================================================
one:
id: 1
title: MyString
body: MyText
user_id: 1
two:
id: 2
title: MyString
body: MyText
user_id: 2
windoze:
id: 3
title: Windows Sucks
body: Yeah, it does!
user_id: 3
foo:
id: 4
title: Foo
body: Foo body
user_id: 3
================================================
FILE: test-project/spec/fixtures/users.yml
================================================
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
id: 1
login: MyString
password: MyString
two:
id: 2
login: MyString
password: MyString
bill:
id: 3
login: bill
password: windoze
================================================
FILE: test-project/spec/integration/multi_threading_spec.rb
================================================
require 'spec_helper'
describe "DbCharmer integration tests" do
def do_test(test_seconds, thread_count)
start_time = Time.now.to_f
threads = Array.new
while threads.size < thread_count
threads << Thread.new do
while Time.now.to_f - start_time < test_seconds do
User.create!(:login => "user#{rand}", :password => rand)
User.uncached { User.on_db(:slave01).first }
end
end
end
# Wait for threads to finish
threads.each(&:join)
end
it "should work in single-threaded mode" do
do_test(10, 1)
end
it "should work with 5 threads" do
do_test(10, 5)
end
it "should use default connection passed in db_magic call in all threads" do
# Define a class with db magic in it
class TestLogRecordWithThreads < ActiveRecord::Base
self.table_name = :log_records
db_magic :connection => :logs
end
# Check conection in the same thread
TestLogRecordWithThreads.connection.db_charmer_connection_name.should == "logs"
# Check connection in a different thread
Thread.new {
TestLogRecordWithThreads.connection.db_charmer_connection_name.should == "logs"
}.join
end
it "should use default connection passed in db_magic call when master connection is being remapped" do
class TestLogRecordWithThreadsAndRemapping < ActiveRecord::Base
self.table_name = :log_records
db_magic :connection => :logs
end
# Test in main thread
expect {
DbCharmer.with_remapped_databases(:master => :slave01) do
TestLogRecordWithThreadsAndRemapping.first
end
}.to_not raise_error
# Test in another thread
Thread.new {
expect {
DbCharmer.with_remapped_databases(:master => :slave01) do
TestLogRecordWithThreadsAndRemapping.first
end
}.to_not raise_error
}.join
end
end unless ENV['SKIP_MT_TESTS']
================================================
FILE: test-project/spec/models/avatar_spec.rb
================================================
require 'spec_helper'
describe Avatar do
before(:each) do
@valid_attributes = {
:user_id => 1,
:name => "value for name"
}
end
it "should create a new instance given valid attributes" do
Avatar.create!(@valid_attributes)
end
end
================================================
FILE: test-project/spec/models/cars_spec.rb
================================================
require 'spec_helper'
describe Ford, "STI model" do
before(:each) do
@valid_attributes = {
:license => "FFGH-9134"
}
end
it "should create a new instance given valid attributes" do
Ford.create!(@valid_attributes)
end
it "should properly handle slave find calls" do
Ford.first.should be_valid
end
end
describe Toyota, "STI model" do
before(:each) do
@valid_attributes = {
:license => "TFGH-9134"
}
end
it "should create a new instance given valid attributes" do
Toyota.create!(@valid_attributes)
end
it "should properly handle slave find calls" do
Toyota.first.should be_valid
end
end
================================================
FILE: test-project/spec/models/categories_posts_spec.rb
================================================
require 'spec_helper'
describe CategoriesPosts do
before(:each) do
@valid_attributes = {
:post_id => 1,
:category_id => 1
}
end
it "should create a new instance given valid attributes" do
CategoriesPosts.create!(@valid_attributes)
end
end
================================================
FILE: test-project/spec/models/category_spec.rb
================================================
require 'spec_helper'
describe Category do
before(:each) do
@valid_attributes = {
:name => "value for name"
}
end
it "should create a new instance given valid attributes" do
Category.create!(@valid_attributes)
end
end
================================================
FILE: test-project/spec/models/comment_spec.rb
================================================
require 'spec_helper'
describe Comment do
fixtures :comments, :avatars, :posts, :users
describe "preload polymorphic association" do
subject do
lambda {
Comment.find(:all, :include => :commentable)
}
end
it { should_not raise_error }
end
end
================================================
FILE: test-project/spec/models/event_spec.rb
================================================
require 'spec_helper'
describe Event, "sharded model" do
fixtures :event_shards_info, :event_shards_map
it "should respond to shard_for method" do
Event.should respond_to(:shard_for)
end
it "should correctly switch shards" do
# Cleanup sharded tables
Event.on_each_shard { |event| event.delete_all }
# Check that they are empty
Event.shard_for(2).all.should be_empty
Event.shard_for(12).all.should be_empty
# Create some data (one record in each shard)
Event.shard_for(2).create!(
:from_uid => 1,
:to_uid => 2,
:original_created_at => Time.now,
:event_type => 1,
:event_data => 'foo'
)
Event.shard_for(12).create!(
:from_uid => 1,
:to_uid => 12,
:original_created_at => Time.now,
:event_type => 1,
:event_data => 'bar'
)
# Check sharded tables to make sure they have the data
Event.shard_for(2).find_all_by_from_uid(1).map(&:event_data).should == [ 'foo' ]
Event.shard_for(12).find_all_by_from_uid(1).map(&:event_data).should == [ 'bar' ]
end
it "should allocate new blocks when needed" do
# Cleanup sharded tables
Event.on_each_shard { |event| event.delete_all }
# Check new block, it should be empty
Event.shard_for(100).count.should be_zero
# Create an object
Event.shard_for(100).create!(
:from_uid => 1,
:to_uid => 100,
:original_created_at => Time.now,
:event_type => 1,
:event_data => 'blah'
)
# Check the new block
Event.shard_for(100).count.should == 1
end
it "should fail to perform any database operations w/o a shard specification" do
Event.stub(:column_defaults).and_return({})
Event.stub(:columns_hash).and_return({})
lambda { Event.first }.should raise_error(ActiveRecord::ConnectionNotEstablished)
lambda { Event.create }.should raise_error(ActiveRecord::ConnectionNotEstablished)
lambda { Event.delete_all }.should raise_error(ActiveRecord::ConnectionNotEstablished)
end
it "should not fail when AR does some internal calls to the database" do
# Cleanup sharded tables
Event.on_each_shard { |event| event.delete_all }
# Create an object
x = Event.shard_for(100).create!(
:from_uid => 1,
:to_uid => 100,
:original_created_at => Time.now,
:event_type => 1,
:event_data => 'blah'
)
Event.reset_column_information
lambda { x.inspect }.should_not raise_error
end
end
================================================
FILE: test-project/spec/models/log_record_spec.rb
================================================
require 'spec_helper'
describe LogRecord do
before(:each) do
@valid_attributes = {
:level => "value for level",
:message => "value for message"
}
end
it "should create a new instance given valid attributes" do
LogRecord.create!(@valid_attributes)
end
end
================================================
FILE: test-project/spec/models/post_spec.rb
================================================
require 'spec_helper'
describe Post do
before(:each) do
@valid_attributes = {
:title => "value for title",
:body => "value for body"
}
end
it "should create a new instance given valid attributes" do
Post.create!(@valid_attributes)
end
end
================================================
FILE: test-project/spec/models/range_sharded_model_spec.rb
================================================
require 'spec_helper'
describe RangeShardedModel do
describe "class method shard_for" do
describe "should correctly set shards in range-defined shards" do
[ 0, 1, 50, 99].each do |id|
it "for #{id}" do
RangeShardedModel.shard_for(id) do |m|
m.connection.object_id.should == RangeShardedModel.on_db(:shard1).connection.object_id
end
end
end
[ 100, 101, 150, 199, 200].each do |id|
it "for #{id}" do
RangeShardedModel.shard_for(id) do |m|
m.connection.object_id.should == RangeShardedModel.on_db(:shard2).connection.object_id
end
end
end
end
describe "should correctly set shards in default shard" do
[ 201, 500].each do |id|
it "for #{id}" do
RangeShardedModel.shard_for(id) do |m|
m.connection.object_id.should == RangeShardedModel.on_db(:shard3).connection.object_id
end
end
end
end
it "should raise an exception when there is no default shard and no ranged shards matched" do
begin
default_shard = RangeShardedModel.sharded_connection.sharder.ranges.delete(:default)
lambda { RangeShardedModel.shard_for(500) }.should raise_error(ArgumentError)
ensure
RangeShardedModel.sharded_connection.sharder.ranges[:default] = default_shard
end
end
end
end
================================================
FILE: test-project/spec/models/user_spec.rb
================================================
require 'spec_helper'
describe User do
before(:each) do
@valid_attributes = {
:login => "value for login",
:password => "value for password"
}
User.switch_connection_to(nil)
User.db_charmer_default_connection = nil
end
it "should create a new instance given valid attributes" do
User.create!(@valid_attributes)
end
it "should create a new instance in a specified db" do
# Just to make sure
User.on_db(:user_master).connection.object_id.should_not == User.connection.object_id
# Default connection should not be touched
User.connection.should_not_receive(:insert)
# Only specified connection receives an insert
User.on_db(:user_master).connection.should_receive(:insert)
# Test!
User.on_db(:user_master).create!(@valid_attributes)
end
end
================================================
FILE: test-project/spec/sharding/connection_spec.rb
================================================
require 'spec_helper'
describe DbCharmer::Sharding::Connection do
describe "in constructor" do
it "should not fail if method name is correct" do
lambda { DbCharmer::Sharding::Connection.new(:name => :foo, :method => :range, :ranges => {}) }.should_not raise_error
end
it "should fail if method name is missing" do
lambda { DbCharmer::Sharding::Connection.new(:name => :foo) }.should raise_error(ArgumentError)
end
it "should fail if method name is invalid" do
lambda { DbCharmer::Sharding::Connection.new(:name => :foo, :method => :foo) }.should raise_error(NameError)
end
it "should instantiate a sharder class according to the :method value" do
DbCharmer::Sharding::Method::Range.should_receive(:new)
DbCharmer::Sharding::Connection.new(:name => :foo, :method => :range, :ranges => {})
end
end
end
================================================
FILE: test-project/spec/sharding/method/db_block_map_spec.rb
================================================
require 'spec_helper'
describe DbCharmer::Sharding::Method::DbBlockMap do
fixtures :event_shards_info, :event_shards_map
before(:each) do
@sharder = DbCharmer::Sharding::Method::DbBlockMap.new(
:name => :social,
:block_size => 10,
:map_table => :event_shards_map,
:shards_table => :event_shards_info,
:connection => :social_shard_info
)
@conn = DbCharmer::ConnectionFactory.connect(:social_shard_info)
end
describe "standard interface" do
it "should respond to shard_for_id" do
@sharder.should respond_to(:shard_for_key)
end
it "should return a shard config to be used for a key" do
@sharder.shard_for_key(1).should be_kind_of(Hash)
end
it "should have shard_connections method and return a list of db connections" do
@sharder.shard_connections.should_not be_empty
end
end
it "should correctly return shards for all blocks defined in the mapping table" do
blocks = @conn.select_all("SELECT * FROM event_shards_map")
blocks.each do |blk|
shard = @sharder.shard_for_key(blk['start_id'])
shard[:connection_name].should match(/social.*#{blk['shard_id']}$/)
shard = @sharder.shard_for_key(blk['start_id'].to_i + 1)
shard[:connection_name].should match(/social.*#{blk['shard_id']}$/)
shard = @sharder.shard_for_key(blk['end_id'].to_i - 1)
shard[:connection_name].should match(/social.*#{blk['shard_id']}$/)
end
end
describe "for non-existing blocks" do
before do
@max_id = @conn.select_value("SELECT max(end_id) FROM event_shards_map").to_i
Rails.cache.clear
end
it "should not fail" do
lambda {
@sharder.shard_for_key(@max_id + 1)
}.should_not raise_error
end
it "should create a new one" do
@sharder.shard_for_key(@max_id + 1).should_not be_nil
end
it "should assign it to the least loaded shard" do
@sharder.shard_for_key(@max_id + 1)[:connection_name].should match(/shard.*03$/)
end
it "should not consider non-open shards" do
@conn.execute("UPDATE event_shards_info SET open = 0 WHERE id = 3")
@sharder.shard_for_key(@max_id + 1)[:connection_name].should_not match(/shard.*03$/)
end
it "should not consider disabled shards" do
@conn.execute("UPDATE event_shards_info SET enabled = 0 WHERE id = 3")
@sharder.shard_for_key(@max_id + 1)[:connection_name].should_not match(/shard.*03$/)
end
it "should increment the blocks counter on the shard" do
lambda {
@sharder.shard_for_key(@max_id + 1)
}.should change {
@conn.select_value("SELECT blocks_count FROM event_shards_info WHERE id = 3").to_i
}.by(+1)
end
it "should raise duplicate key error when allocating same block twice" do
@sharder.allocate_new_block_for_key(@max_id + 1)
lambda {
@sharder.allocate_new_block_for_key(@max_id + 1)
}.should raise_error(ActiveRecord::StatementInvalid)
end
it "should handle duplicate key errors" do
@sharder.shard_for_key(@max_id + 1)
actual_block = @sharder.block_for_key(@max_id + 1)
@sharder.should_receive(:block_for_key).twice.and_return(nil, actual_block)
@sharder.shard_for_key(@max_id + 1)
end
end
it "should fail on invalid shard references" do
@conn.execute("DELETE FROM event_shards_info")
lambda { @sharder.shard_for_key(1) }.should raise_error(ArgumentError)
end
it "should cache shards info" do
shard = DbCharmer::Sharding::Method::DbBlockMap::ShardInfo.first
DbCharmer::Sharding::Method::DbBlockMap::ShardInfo.should_receive(:find_by_id).once.and_return(shard)
@sharder.shard_info_by_id(1)
@sharder.shard_info_by_id(1)
end
it "should not cache shards info when explicitly asked not to" do
shard = DbCharmer::Sharding::Method::DbBlockMap::ShardInfo.first
DbCharmer::Sharding::Method::DbBlockMap::ShardInfo.should_receive(:find_by_id).twice.and_return(shard)
@sharder.shard_info_by_id(1, false)
@sharder.shard_info_by_id(1, false)
end
it "should cache blocks" do
@sharder.block_for_key(1)
@sharder.connection.should_not_receive(:select_one)
@sharder.block_for_key(1)
@sharder.block_for_key(2)
end
it "should not cache blocks if asked not to" do
block = @sharder.block_for_key(1)
@sharder.connection.should_receive(:select_one).twice.and_return(block)
@sharder.block_for_key(1, false)
@sharder.block_for_key(2, false)
end
end
================================================
FILE: test-project/spec/sharding/method/hash_map_spec.rb
================================================
require 'spec_helper'
describe DbCharmer::Sharding::Method::HashMap do
SHARDING_MAP = {
'US' => :us_users,
'CA' => :ca_users,
:default => :other_users
}
before do
@sharder = DbCharmer::Sharding::Method::HashMap.new(:map => SHARDING_MAP)
end
describe "standard interface" do
it "should respond to shard_for_id" do
@sharder.should respond_to(:shard_for_key)
end
it "should return a shard name to be used for an key" do
@sharder.shard_for_key('US').should be_kind_of(Symbol)
end
it "should support default shard" do
@sharder.support_default_shard?.should be_true
end
end
describe "should correctly return shards for all keys defined in the map" do
SHARDING_MAP.except(:default).each do |key, val|
it "for #{key}" do
@sharder.shard_for_key(key).should == val
end
end
end
it "should correctly return default shard" do
@sharder.shard_for_key('UA').should == :other_users
end
it "should raise an exception when there is no default shard and nothing matched" do
@sharder.map.delete(:default)
lambda { @sharder.shard_for_key('UA') }.should raise_error(ArgumentError)
end
end
================================================
FILE: test-project/spec/sharding/method/range_spec.rb
================================================
require 'spec_helper'
describe DbCharmer::Sharding::Method::Range do
SHARDING_RANGES = {
0...100 => :shard1,
100..200 => :shard2,
:default => :shard3
}
before do
@sharder = DbCharmer::Sharding::Method::Range.new(:ranges => SHARDING_RANGES)
end
describe "standard interface" do
it "should respond to shard_for_id" do
@sharder.should respond_to(:shard_for_key)
end
it "should return a shard name to be used for an key" do
@sharder.shard_for_key(1).should be_kind_of(Symbol)
end
it "should support default shard" do
@sharder.support_default_shard?.should be_true
end
end
describe "should correctly return shards for all ids in defined ranges" do
[ 0, 1, 50, 99].each do |id|
it "for #{id}" do
@sharder.shard_for_key(id).should == :shard1
end
end
[ 100, 101, 150, 199, 200].each do |id|
it "for #{id}" do
@sharder.shard_for_key(id).should == :shard2
end
end
end
describe "should correctly return shard for all ids outside the ranges if has a default" do
[ 201, 500].each do |id|
it "for #{id}" do
@sharder.shard_for_key(id).should == :shard3
end
end
end
it "should raise an exception when there is no default shard and no ranges matched" do
@sharder.ranges.delete(:default)
lambda { @sharder.shard_for_key(500) }.should raise_error(ArgumentError)
end
end
================================================
FILE: test-project/spec/sharding/sharding_spec.rb
================================================
require 'spec_helper'
describe "DbCharmer::Sharding" do
describe "in register_connection method" do
it "should raise an exception if passed config has no :name parameter" do
lambda {
DbCharmer::Sharding.register_connection(:method => :range, :ranges => { :default => :foo })
}.should raise_error(ArgumentError)
end
it "should not raise an exception if passed config has all required params" do
lambda {
DbCharmer::Sharding.register_connection(:method => :range, :ranges => { :default => :foo }, :name => :foo)
}.should_not raise_error
end
end
describe "in sharded_connection method" do
it "should raise an error for invalid connection names" do
lambda { DbCharmer::Sharding.sharded_connection(:blah) }.should raise_error(ArgumentError)
end
end
end
================================================
FILE: test-project/spec/spec_helper.rb
================================================
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] = 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
end
================================================
FILE: test-project/spec/support/rails31_stub_connection.rb
================================================
def stub_columns_for_rails31(connection)
return unless DbCharmer.rails31?
connection.abstract_connection_class.retrieve_connection.stub(:columns).and_return([])
end
================================================
FILE: test-project/spec/unit/abstract_adapter/log_formatting_spec.rb
================================================
require 'spec_helper'
if DbCharmer.rails2?
describe 'AbstractAdapter' do
it "should respond to connection_name accessor" do
ActiveRecord::Base.connection.respond_to?(:connection_name).should be_true
end
it "should have connection_name read accessor working" do
DbCharmer::ConnectionFactory.generate_abstract_class('logs').connection.connection_name.should == 'logs'
DbCharmer::ConnectionFactory.generate_abstract_class('slave01').connection.connection_name.should == 'slave01'
ActiveRecord::Base.connection.connection_name.should be_nil
end
it "should append connection name to log records on non-default connections" do
User.switch_connection_to nil
default_message = User.connection.send(:format_log_entry, 'hello world')
switched_message = User.on_db(:slave01).connection.send(:format_log_entry, 'hello world')
switched_message.should_not == default_message
switched_message.should match(/slave01/)
end
end
end
================================================
FILE: test-project/spec/unit/action_controller/force_slave_reads_spec.rb
================================================
require 'spec_helper'
class BlahController < ActionController::Base; end
describe ActionController, "with force_slave_reads extension" do
before do
BlahController.force_slave_reads({}) # cleanup status
end
it "should not force slave reads when there are no actions defined as forced" do
BlahController.force_slave_reads_action?(:index).should be_false
end
it "should force slave reads for :only actions" do
BlahController.force_slave_reads :only => :index
BlahController.force_slave_reads_action?(:index).should be_true
end
it "should not force slave reads for non-listed actions when there is :only parameter" do
BlahController.force_slave_reads :only => :index
BlahController.force_slave_reads_action?(:show).should be_false
end
it "should not force slave reads for :except actions" do
BlahController.force_slave_reads :except => :delete
BlahController.force_slave_reads_action?(:delete).should be_false
end
it "should force slave reads for non-listed actions when there is :except parameter" do
BlahController.force_slave_reads :except => :delete
BlahController.force_slave_reads_action?(:index).should be_true
end
it "should not force slave reads for actions listed in both :except and :only lists" do
BlahController.force_slave_reads :only => :delete, :except => :delete
BlahController.force_slave_reads_action?(:delete).should be_false
end
it "should not force slave reads for non-listed actions when there are :except and :only lists present" do
BlahController.force_slave_reads :only => :index, :except => :delete
BlahController.force_slave_reads_action?(:show).should be_false
end
end
================================================
FILE: test-project/spec/unit/active_record/association_preload_spec.rb
================================================
require 'spec_helper'
if DbCharmer.rails2?
describe "ActiveRecord preload_associations method" do
it "should be public" do
ActiveRecord::Base.public_methods.collect(&:to_s).member?('preload_associations').should be_true
end
end
end
describe "ActiveRecord in finder methods" do
fixtures :categories, :users, :posts, :categories_posts, :avatars
before do
Post.db_magic :connection => nil
User.db_magic :connection => nil
end
after do
Post.db_magic(Post::DB_MAGIC_DEFAULT_PARAMS)
end
it "should switch all belongs_to association connections when :include is used" do
User.connection.should_not_receive(:select_all)
Post.on_db(:slave01).all(:include => :user)
end
it "should switch all has_many association connections when :include is used" do
Post.connection.should_not_receive(:select_all)
User.on_db(:slave01).all(:include => :posts)
end
it "should switch all has_one association connections when :include is used" do
Avatar.connection.should_not_receive(:select_all)
User.on_db(:slave01).all(:include => :avatar)
end
it "should switch all has_and_belongs_to_many association connections when :include is used" do
Post.connection.should_not_receive(:select_all)
Category.on_db(:slave01).all(:include => :posts)
end
#-------------------------------------------------------------------------------------------
it "should not switch assocations when called on a top-level connection" do
User.connection.should_receive(:select_all).and_return([])
Post.all(:include => :user)
end
it "should not switch connection when association model and main model are on different servers" do
LogRecord.connection.should_receive(:select_all).and_return([])
User.on_db(:slave01).all(:include => :log_records)
end
end
================================================
FILE: test-project/spec/unit/active_record/association_proxy_spec.rb
================================================
require 'spec_helper'
describe "DbCharmer::AssociationProxy extending AR::Associations" do
fixtures :users, :posts
it "should add proxy? => true method" do
users(:bill).posts.proxy?.should be_true
end
describe "in has_many associations" do
before do
@user = users(:bill)
@posts = @user.posts.all
Post.switch_connection_to(:logs)
User.switch_connection_to(:logs)
end
after do
Post.switch_connection_to(nil)
User.switch_connection_to(nil)
end
it "should implement on_db proxy" do
Post.connection.should_not_receive(:select_all)
User.connection.should_not_receive(:select_all)
stub_columns_for_rails31 Post.on_db(:logs).connection
Post.on_db(:slave01).connection.should_receive(:select_all).and_return(@posts.map { |p| p.attributes })
assert_equal @posts, @user.posts.on_db(:slave01)
end
it "on_db should work in prefix mode" do
Post.connection.should_not_receive(:select_all)
User.connection.should_not_receive(:select_all)
stub_columns_for_rails31 Post.on_db(:logs).connection
Post.on_db(:slave01).connection.should_receive(:select_all).and_return(@posts.map { |p| p.attributes })
@user.on_db(:slave01).posts.should == @posts
end
it "should actually proxy calls to the rails association proxy" do
Post.switch_connection_to(nil)
@user.posts.on_db(:slave01).count.should == @user.posts.count
end
it "should work with named scopes" do
Post.switch_connection_to(nil)
@user.posts.windows_posts.on_db(:slave01).count.should == @user.posts.windows_posts.count
end
it "should work with chained named scopes" do
Post.switch_connection_to(nil)
@user.posts.windows_posts.dummy_scope.on_db(:slave01).count.should == @user.posts.windows_posts.dummy_scope.count
end
end
describe "in belongs_to associations" do
before do
@post = posts(:windoze)
@user = users(:bill)
User.switch_connection_to(:logs)
User.connection.object_id.should_not == Post.connection.object_id
end
after do
User.switch_connection_to(nil)
end
it "should implement on_db proxy" do
pending
Post.connection.should_not_receive(:select_all)
User.connection.should_not_receive(:select_all)
User.on_db(:slave01).connection.should_receive(:select_all).once.and_return([ @user ])
@post.user.on_db(:slave01).should == @post.user
end
it "on_db should work in prefix mode" do
pending
Post.connection.should_not_receive(:select_all)
User.connection.should_not_receive(:select_all)
User.on_db(:slave01).connection.should_receive(:select_all).once.and_return([ @user ])
@post.on_db(:slave01).user.should == @post.user
end
it "should actually proxy calls to the rails association proxy" do
User.switch_connection_to(nil)
@post.user.on_db(:slave01).should == @post.user
end
end
end
================================================
FILE: test-project/spec/unit/active_record/class_attributes_spec.rb
================================================
require 'spec_helper'
class FooModel < ActiveRecord::Base; end
describe DbCharmer, "for ActiveRecord models" do
context "in db_charmer_connection_proxy methods" do
before do
FooModel.db_charmer_connection_proxy = nil
FooModel.db_charmer_default_connection = nil
end
it "should implement both accessor methods" do
proxy = double('connection proxy')
FooModel.db_charmer_connection_proxy = proxy
FooModel.db_charmer_connection_proxy.should be(proxy)
end
end
context "in db_charmer_default_connection methods" do
before do
FooModel.db_charmer_default_connection = nil
FooModel.db_charmer_default_connection = nil
end
it "should implement both accessor methods" do
conn = double('connection')
FooModel.db_charmer_default_connection = conn
FooModel.db_charmer_default_connection.should be(conn)
end
end
context "in db_charmer_opts methods" do
before do
FooModel.db_charmer_opts = nil
end
it "should implement both accessor methods" do
opts = { :foo => :bar}
FooModel.db_charmer_opts = opts
FooModel.db_charmer_opts.should be(opts)
end
end
context "in db_charmer_slaves methods" do
it "should return [] if no slaves set for a model" do
FooModel.db_charmer_slaves = nil
FooModel.db_charmer_slaves.should == []
end
it "should implement both accessor methods" do
proxy = double('connection proxy')
FooModel.db_charmer_slaves = [ proxy ]
FooModel.db_charmer_slaves.should == [ proxy ]
end
it "should implement random slave selection" do
FooModel.db_charmer_slaves = [ :proxy1, :proxy2, :proxy3 ]
srand(0)
FooModel.db_charmer_random_slave.should == :proxy1
FooModel.db_charmer_random_slave.should == :proxy2
FooModel.db_charmer_random_slave.should == :proxy1
FooModel.db_charmer_random_slave.should == :proxy2
FooModel.db_charmer_random_slave.should == :proxy2
FooModel.db_charmer_random_slave.should == :proxy3
end
end
context "in db_charmer_connection_levels methods" do
it "should return 0 by default" do
FooModel.db_charmer_connection_level = nil
FooModel.db_charmer_connection_level.should == 0
end
it "should implement both accessor methods and support inc/dec operations" do
FooModel.db_charmer_connection_level = 1
FooModel.db_charmer_connection_level.should == 1
FooModel.db_charmer_connection_level += 1
FooModel.db_charmer_connection_level.should == 2
FooModel.db_charmer_connection_level -= 1
FooModel.db_charmer_connection_level.should == 1
end
it "should implement db_charmer_top_level_connection? method" do
FooModel.db_charmer_connection_level = 1
FooModel.should_not be_db_charmer_top_level_connection
FooModel.db_charmer_connection_level = 0
FooModel.should be_db_charmer_top_level_connection
end
end
context "in connection method" do
it "should return AR's original connection if no connection proxy is set" do
FooModel.db_charmer_connection_proxy = nil
FooModel.db_charmer_default_connection = nil
FooModel.connection.should be_kind_of(ActiveRecord::ConnectionAdapters::AbstractAdapter)
end
end
context "in db_charmer_force_slave_reads? method" do
it "should use per-model settings when possible" do
FooModel.db_charmer_force_slave_reads = true
DbCharmer.should_not_receive(:force_slave_reads?)
FooModel.db_charmer_force_slave_reads?.should be_true
end
it "should use global settings when local setting is false" do
FooModel.db_charmer_force_slave_reads = false
DbCharmer.should_receive(:force_slave_reads?).and_return(true)
FooModel.db_charmer_force_slave_reads?.should be_true
DbCharmer.should_receive(:force_slave_reads?).and_return(false)
FooModel.db_charmer_force_slave_reads?.should be_false
end
end
end
================================================
FILE: test-project/spec/unit/active_record/connection_switching_spec.rb
================================================
require 'spec_helper'
class FooModelForConnSwitching < ActiveRecord::Base; end
class BarModelForConnSwitching < ActiveRecord::Base; end
describe DbCharmer, "AR connection switching" do
describe "in switch_connection_to method" do
before(:all) do
BarModelForConnSwitching.hijack_connection!
end
before :each do
@proxy = double('proxy')
@proxy.stub(:db_charmer_connection_name).and_return(:myproxy)
end
before do
BarModelForConnSwitching.db_charmer_connection_proxy = @proxy
BarModelForConnSwitching.connection.should be(@proxy)
end
it "should accept nil and reset connection to default" do
BarModelForConnSwitching.switch_connection_to(nil)
BarModelForConnSwitching.connection.should be(ActiveRecord::Base.connection)
end
it "should accept a string and generate an abstract class with connection factory" do
BarModelForConnSwitching.switch_connection_to('logs')
BarModelForConnSwitching.connection.object_id == DbCharmer::ConnectionFactory.connect('logs').object_id
end
it "should accept a symbol and generate an abstract class with connection factory" do
BarModelForConnSwitching.switch_connection_to(:logs)
BarModelForConnSwitching.connection.object_id.should == DbCharmer::ConnectionFactory.connect('logs').object_id
end
it "should accept a model and use its connection proxy value" do
FooModelForConnSwitching.switch_connection_to(:logs)
BarModelForConnSwitching.switch_connection_to(FooModelForConnSwitching)
BarModelForConnSwitching.connection.object_id.should == DbCharmer::ConnectionFactory.connect('logs').object_id
end
context "with a hash parameter" do
before do
@conf = {
:adapter => 'mysql',
:username => "db_charmer_ro",
:database => "db_charmer_sandbox_test",
:connection_name => 'sanbox_ro'
}
end
it "should fail if there is no :connection_name parameter" do
@conf.delete(:connection_name)
lambda { BarModelForConnSwitching.switch_connection_to(@conf) }.should raise_error(ArgumentError)
end
it "generate an abstract class with connection factory" do
BarModelForConnSwitching.switch_connection_to(@conf)
BarModelForConnSwitching.connection.object_id.should == DbCharmer::ConnectionFactory.connect_to_db(@conf[:connection_name], @conf).object_id
end
end
it "should support connection switching for AR::Base" do
ActiveRecord::Base.switch_connection_to(:logs)
ActiveRecord::Base.connection.object_id == DbCharmer::ConnectionFactory.connect('logs').object_id
ActiveRecord::Base.switch_connection_to(nil)
end
end
end
describe DbCharmer, "for ActiveRecord models" do
describe "in establish_real_connection_if_exists method" do
it "should check connection name if requested" do
lambda { FooModelForConnSwitching.establish_real_connection_if_exists(:foo, true) }.should raise_error(ArgumentError)
end
it "should not check connection name if not reqested" do
lambda { FooModelForConnSwitching.establish_real_connection_if_exists(:foo) }.should_not raise_error
end
it "should not check connection name if reqested not to" do
lambda { FooModelForConnSwitching.establish_real_connection_if_exists(:foo, false) }.should_not raise_error
end
it "should establish connection when connection configuration exists" do
FooModelForConnSwitching.should_receive(:establish_connection)
FooModelForConnSwitching.establish_real_connection_if_exists(:logs)
end
it "should not establish connection even when connection configuration does not exist" do
FooModelForConnSwitching.should_not_receive(:establish_connection)
FooModelForConnSwitching.establish_real_connection_if_exists(:blah)
end
end
end
================================================
FILE: test-project/spec/unit/active_record/db_magic_spec.rb
================================================
require 'spec_helper'
class Blah < ActiveRecord::Base; end
describe "In ActiveRecord models" do
describe "db_magic method" do
context "with :connection parameter" do
after do
DbCharmer.connections_should_exist = false
end
it "should change model's connection to specified one" do
Blah.db_magic :connection => :logs
Blah.connection.object_id.should == DbCharmer::ConnectionFactory.connect(:logs).object_id
end
it "should pass :should_exist paramater value to the underlying connection logic" do
DbCharmer::ConnectionFactory.should_receive(:connect).with(:logs, 'blah')
Blah.db_magic :connection => :logs, :should_exist => 'blah'
DbCharmer.connections_should_exist = true
DbCharmer::ConnectionFactory.should_receive(:connect).with(:logs, false)
Blah.db_magic :connection => :logs, :should_exist => false
end
it "should use global DbCharmer's connections_should_exist attribute if no :should_exist passed" do
DbCharmer.connections_should_exist = true
DbCharmer::ConnectionFactory.should_receive(:connect).with(:logs, true)
Blah.db_magic :connection => :logs
end
end
context "with :slave or :slaves parameter" do
it "should merge :slave and :slaves values" do
Blah.db_charmer_slaves = []
Blah.db_charmer_slaves.should be_empty
Blah.db_magic :slave => :slave01
Blah.db_charmer_slaves.size.should == 1
Blah.db_magic :slaves => [ :slave01 ]
Blah.db_charmer_slaves.size.should == 1
Blah.db_magic :slaves => [ :slave01 ], :slave => :logs
Blah.db_charmer_slaves.size.should == 2
end
it "should make db_charmer_force_slave_reads = true by default" do
Blah.db_magic :slave => :slave01
Blah.db_charmer_force_slave_reads.should be_true
end
it "should pass force_slave_reads value to db_charmer_force_slave_reads" do
Blah.db_magic :slave => :slave01, :force_slave_reads => false
Blah.db_charmer_force_slave_reads.should be_false
Blah.db_magic :slave => :slave01, :force_slave_reads => true
Blah.db_charmer_force_slave_reads.should be_true
end
end
it "should set up a hook to propagate db_magic params to all the children models" do
class ParentFoo < ActiveRecord::Base
db_magic :foo => :bar
end
class ChildFoo < ParentFoo; end
ChildFoo.db_charmer_opts.should == ParentFoo.db_charmer_opts
end
context "with :sharded parameter" do
class ShardTestingFoo < ActiveRecord::Base
db_magic :sharded => { :key => :id, :sharded_connection => :texts }
end
it "should add shard_for method to the model" do
ShardTestingFoo.should respond_to(:shard_for)
end
end
end
end
================================================
FILE: test-project/spec/unit/active_record/master_slave_routing_spec.rb
================================================
require 'spec_helper'
describe "ActiveRecord slave-enabled models" do
before do
class User < ActiveRecord::Base
db_magic :connection => :user_master, :slave => :slave01
end
end
describe "in finder method" do
[ :last, :first, :all ].each do |meth|
describe meth do
it "should go to the slave if called on the first level connection" do
User.on_slave.connection.should_receive(:select_all).and_return([])
User.send(meth)
end
it "should not change connection if called in an on_db block" do
stub_columns_for_rails31 User.on_db(:logs).connection
User.on_db(:logs).connection.should_receive(:select_all).and_return([])
User.on_slave.connection.should_not_receive(:select_all)
User.on_db(:logs).send(meth)
end
it "should not change connection when it's already been changed by on_slave call" do
pending "rails3: not sure if we need this spec" if DbCharmer.rails3?
User.on_slave do
User.on_slave.connection.should_receive(:select_all).and_return([])
User.should_not_receive(:on_db)
User.send(meth)
end
end
it "should not change connection if called in a transaction" do
User.on_db(:user_master).connection.should_receive(:select_all).and_return([])
User.on_slave.connection.should_not_receive(:select_all)
User.transaction { User.send(meth) }
end
end
end
it "should go to the master if called find with :lock => true option" do
User.on_db(:user_master).connection.should_receive(:select_all).and_return([])
User.on_slave.connection.should_not_receive(:select_all)
User.find(:first, :lock => true)
end
it "should not go to the master if no :lock => true option passed" do
User.on_db(:user_master).connection.should_not_receive(:select_all)
User.on_slave.connection.should_receive(:select_all).and_return([])
User.find(:first)
end
it "should correctly pass all find params to the underlying code" do
User.delete_all
u1 = User.create(:login => 'foo')
u2 = User.create(:login => 'bar')
User.find(:all, :conditions => { :login => 'foo' }).should == [ u1 ]
User.find(:all, :limit => 1).size.should == 1
User.find(:first, :conditions => { :login => 'bar' }).should == u2
end
end
describe "in calculation method" do
[ :count, :minimum, :maximum, :average ].each do |meth|
describe meth do
it "should go to the slave if called on the first level connection" do
User.on_slave.connection.should_receive(:select_value).and_return(1)
User.send(meth, :id).should == 1
end
it "should not change connection if called in an on_db block" do
User.on_db(:logs).connection.should_receive(:select_value).and_return(1)
User.on_slave.connection.should_not_receive(:select_value)
User.on_db(:logs).send(meth, :id).should == 1
end
it "should not change connection when it's already been changed by an on_slave call" do
pending "rails3: not sure if we need this spec" if DbCharmer.rails3?
User.on_slave do
User.on_slave.connection.should_receive(:select_value).and_return(1)
User.should_not_receive(:on_db)
User.send(meth, :id).should == 1
end
end
it "should not change connection if called in a transaction" do
User.on_db(:user_master).connection.should_receive(:select_value).and_return(1)
User.on_slave.connection.should_not_receive(:select_value)
User.transaction { User.send(meth, :id).should == 1 }
end
end
end
end
describe "in data manipulation methods" do
it "should go to the master by default" do
User.on_db(:user_master).connection.should_receive(:delete)
User.delete_all
end
it "should go to the master even in slave-enabling chain calls" do
User.on_db(:user_master).connection.should_receive(:delete)
User.on_slave.delete_all
end
it "should go to the master even in slave-enabling block calls" do
User.on_db(:user_master).connection.should_receive(:delete)
User.on_slave { |u| u.delete_all }
end
end
describe "in instance method" do
describe "reload" do
it "should always be done on the master" do
User.delete_all
u = User.create
User.on_db(:user_master).connection.should_receive(:select_all).and_return([{}])
User.on_slave.connection.should_not_receive(:select_all)
User.on_slave { u.reload }
end
end
end
end
================================================
FILE: test-project/spec/unit/active_record/migration/multi_db_migrations_spec.rb
================================================
require 'spec_helper'
class SpecMigration < ActiveRecord::Migration
def self.up
execute "UPDATE log_records SET level = 'debug'"
end
def self.down
execute "UPDATE log_records SET level = 'blah'"
end
end
class SpecMultiDbMigration < ActiveRecord::Migration
db_magic :connection => :logs
def self.up
execute "UPDATE log_records SET level = 'debug'"
end
def self.down
execute "UPDATE log_records SET level = 'blah'"
end
end
class SpecMultiDbMigration2 < ActiveRecord::Migration
def self.up
execute "UPDATE log_records SET level = 'yo'"
on_db(:logs) { execute "UPDATE log_records SET level = 'debug'" }
end
def self.down
execute "UPDATE log_records SET level = 'bar'"
on_db(:logs) { execute "UPDATE log_records SET level = 'blah'" }
end
end
class SpecMultiDbMigration3 < ActiveRecord::Migration
db_magic :connection => [:logs, :default]
def self.up
execute "UPDATE log_records SET level = 'hoho'"
end
def self.down
execute "UPDATE log_records SET level = 'blah'"
end
end
class SpecMultiDbMigration4 < ActiveRecord::Migration
db_magic :connections => [:logs, :default]
def self.up
execute "UPDATE log_records SET level = 'hoho'"
end
def self.down
execute "UPDATE log_records SET level = 'blah'"
end
end
class SpecMultiDbMigration5 < ActiveRecord::Migration
db_magic :connections => [:logs, :default]
def up
execute "UPDATE log_records SET level = 'hoho'"
end
def down
execute "UPDATE log_records SET level = 'blah'"
end
end
class SpecMultiDbMigration6 < ActiveRecord::Migration
def change
on_db(:logs) do
create_table :logs_rails32_test do |t|
t.text :t
end
end
end
end
describe "Multi-db migractions" do
before(:all) do
DbCharmer.connections_should_exist = true
end
after(:all) do
DbCharmer.connections_should_exist = false
end
def connection_with_name(name)
DbCharmer::ConnectionFactory.connect(name).abstract_connection_class.retrieve_connection
end
describe "w/o any magic calls" do
it "should send all up requests to the default connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'debug'")
SpecMigration.migrate(:up)
end
it "should send all down requests to the default connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
SpecMigration.migrate(:down)
end
describe "after AR::Migration db_magic call" do
it "should use default migration config" do
ActiveRecord::Migration.db_magic :connection => :logs
ActiveRecord::Base.connection.should_not_receive(:execute)
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'debug'")
SpecMigration.migrate(:up)
ActiveRecord::Migration.db_magic :connection => :default
end
end
end
describe "with db_magic calls" do
it "should send all up requests to specified connection" do
ActiveRecord::Base.connection.should_not_receive(:execute)
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'debug'")
SpecMultiDbMigration.migrate(:up)
end
it "should send all down requests to specified connection" do
ActiveRecord::Base.connection.should_not_receive(:execute)
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
SpecMultiDbMigration.migrate(:down)
end
describe "after AR::Migration db_magic call" do
it "should use specified connection and ignore global migration config" do
ActiveRecord::Migration.db_magic :connection => :slave01
ActiveRecord::Base.connection.should_not_receive(:execute)
connection_with_name(:slave01).should_not_receive(:execute)
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'debug'")
SpecMultiDbMigration.migrate(:up)
ActiveRecord::Migration.db_magic :connection => :default
end
end
end
describe "with on_db blocks" do
it "should send specified up requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'yo'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'debug'")
SpecMultiDbMigration2.migrate(:up)
end
it "should send secified down requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'bar'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
SpecMultiDbMigration2.migrate(:down)
end
end
describe "with db_magic calls" do
it "should send all up requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'hoho'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'hoho'")
SpecMultiDbMigration3.migrate(:up)
end
it "should send all down requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
SpecMultiDbMigration3.migrate(:down)
end
end
describe "with db_magic calls" do
it "should send all up requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'hoho'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'hoho'")
SpecMultiDbMigration4.migrate(:up)
end
it "should send all down requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
SpecMultiDbMigration4.migrate(:down)
end
end
if DbCharmer.rails31?
describe 'with db_magic calls in instance methods' do
it "should send all up requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'hoho'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'hoho'")
SpecMultiDbMigration5.migrate(:up)
end
it "should send all down requests to specified connection" do
ActiveRecord::Base.connection.should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
connection_with_name(:logs).should_receive(:execute).with("UPDATE log_records SET level = 'blah'")
SpecMultiDbMigration5.migrate(:down)
end
end
describe 'with db_magic calls in recorder' do
it "should send all up requests to specified connection" do
ActiveRecord::Base.connection.should_not_receive(:execute)
connection_with_name(:logs).should_receive(:execute).with(/CREATE TABLE/)
SpecMultiDbMigration6.migrate(:up)
end
it "should send all down requests to specified connection" do
ActiveRecord::Base.connection.should_not_receive(:execute)
connection_with_name(:logs).should_receive(:execute).with(/DROP TABLE/)
SpecMultiDbMigration6.migrate(:down)
end
end
end
end
================================================
FILE: test-project/spec/unit/active_record/named_scope/named_scope_spec.rb
================================================
require 'spec_helper'
describe "Named scopes" do
fixtures :users, :posts
before(:all) do
Post.switch_connection_to(nil)
User.switch_connection_to(nil)
end
describe "prefixed by on_db" do
it "should work on the proxy" do
Post.on_db(:slave01).windows_posts.should == Post.windows_posts
end
it "should actually run queries on the specified db" do
Post.on_db(:slave01).connection.should_receive(:select_all).once.and_return([])
Post.on_db(:slave01).windows_posts.all
# Post.windows_posts.all
end
it "should work with long scope chains" do
Post.on_db(:slave01).connection.should_not_receive(:select_all)
Post.on_db(:slave01).connection.should_receive(:select_value).and_return(5)
Post.on_db(:slave01).windows_posts.count.should == 5
end
it "should work with associations" do
users(:bill).posts.on_db(:slave01).windows_posts.all.should == users(:bill).posts.windows_posts
end
end
describe "postfixed by on_db" do
it "should work on the proxy" do
Post.windows_posts.on_db(:slave01).should == Post.windows_posts
end
it "should actually run queries on the specified db" do
Post.on_db(:slave01).connection.object_id.should_not == Post.connection.object_id
Post.on_db(:slave01).connection.should_receive(:select_all).and_return([])
Post.windows_posts.on_db(:slave01).all
Post.windows_posts.all
end
it "should work with long scope chains" do
Post.on_db(:slave01).connection.should_not_receive(:select_all)
Post.on_db(:slave01).connection.should_receive(:select_value).and_return(5)
Post.windows_posts.on_db(:slave01).count.should == 5
end
it "should work with associations" do
users(:bill).posts.windows_posts.on_db(:slave01).all.should == users(:bill).posts.windows_posts
end
end
end
================================================
FILE: test-project/spec/unit/active_record/relation_spec.rb
================================================
require 'spec_helper'
if DbCharmer.rails3?
describe "ActiveRecord::Relation for a model with db_magic" do
before do
class RelTestModel < ActiveRecord::Base
db_magic :connection => nil
self.table_name = :users
end
end
it "should be created with correct default connection" do
rel = RelTestModel.on_db(:user_master).where("1=1")
rel.db_charmer_connection.object_id.should == RelTestModel.on_db(:user_master).connection.object_id
end
it "should switch the default connection when on_db called" do
rel = RelTestModel.where("1=1")
rel_master = rel.on_db(:user_master)
rel_master.db_charmer_connection.object_id.should_not == rel.db_charmer_connection.object_id
end
it "should keep default connection value when relation is cloned in chained calls" do
rel = RelTestModel.on_db(:user_master).where("1=1")
rel.where("2=2").db_charmer_connection.object_id.should == rel.db_charmer_connection.object_id
end
it "should execute select queries on the default connection" do
rel = RelTestModel.on_db(:user_master).where("1=1")
RelTestModel.on_db(:user_master).connection.should_receive(:select_all).and_return([])
RelTestModel.connection.should_not_receive(:select_all)
rel.first
end
it "should execute delete queries on the default connection" do
rel = RelTestModel.on_db(:user_master).where("1=1")
RelTestModel.on_db(:user_master).connection.should_receive(:delete)
RelTestModel.connection.should_not_receive(:delete)
rel.delete_all
end
it "should execute update_all queries on the default connection" do
rel = RelTestModel.on_db(:user_master).where("1=1")
RelTestModel.on_db(:user_master).connection.should_receive(:update)
RelTestModel.connection.should_not_receive(:update)
rel.update_all("login = login + 'new'")
end
it "should execute update queries on the default connection" do
rel = RelTestModel.on_db(:user_master).where("1=1")
user = RelTestModel.create!(:login => 'login')
RelTestModel.on_db(:user_master).connection.should_receive(:update)
RelTestModel.connection.should_not_receive(:update)
rel.update(user.id, :login => "foobar")
end
it "should return correct connection" do
rel = RelTestModel.on_db(:user_master).where("1=1")
rel.connection.object_id.should == rel.db_charmer_connection.object_id
end
end
end
================================================
FILE: test-project/spec/unit/connection_factory_spec.rb
================================================
require 'spec_helper'
describe DbCharmer::ConnectionFactory do
context "in generate_abstract_class method" do
it "should fail if requested connection config does not exists" do
lambda { DbCharmer::ConnectionFactory.generate_abstract_class('foo') }.should raise_error(ArgumentError)
end
it "should not fail if requested connection config does not exists and should_exist = false" do
lambda { DbCharmer::ConnectionFactory.generate_abstract_class('foo', false) }.should_not raise_error
end
it "should fail if requested connection config does not exists and should_exist = true" do
lambda { DbCharmer::ConnectionFactory.generate_abstract_class('foo', true) }.should raise_error(ArgumentError)
end
it "should generate abstract connection classes" do
klass = DbCharmer::ConnectionFactory.generate_abstract_class('foo', false)
klass.superclass.should be(ActiveRecord::Base)
end
it "should work with weird connection names" do
klass = DbCharmer::ConnectionFactory.generate_abstract_class('foo.bar@baz#blah', false)
klass.superclass.should be(ActiveRecord::Base)
end
end
context "in generate_empty_abstract_ar_class method" do
it "should generate an abstract connection class" do
klass = DbCharmer::ConnectionFactory.generate_empty_abstract_ar_class('::MyFooAbstractClass')
klass.superclass.should be(ActiveRecord::Base)
end
end
context "in establish_connection method" do
it "should generate an abstract class" do
klass = mock('AbstractClass')
conn = mock('connection1')
klass.stub!(:retrieve_connection).and_return(conn)
DbCharmer::ConnectionFactory.should_receive(:generate_abstract_class).and_return(klass)
DbCharmer::ConnectionFactory.establish_connection(:foo).should be(conn)
end
it "should create and return a connection proxy for the abstract class" do
klass = mock('AbstractClass')
DbCharmer::ConnectionFactory.should_receive(:generate_abstract_class).and_return(klass)
DbCharmer::ConnectionProxy.should_receive(:new).with(klass, :foo)
DbCharmer::ConnectionFactory.establish_connection(:foo)
end
end
context "in establish_connection_to_db method" do
it "should generate an abstract class" do
klass = mock('AbstractClass')
conn = mock('connection2')
klass.stub!(:establish_connection)
klass.stub!(:retrieve_connection).and_return(conn)
DbCharmer::ConnectionFactory.should_receive(:generate_empty_abstract_ar_class).and_return(klass)
DbCharmer::ConnectionFactory.establish_connection_to_db(:foo, :username => :foo).should be(conn)
end
it "should create and return a connection proxy for the abstract class" do
klass = mock('AbstractClass')
klass.stub!(:establish_connection)
DbCharmer::ConnectionFactory.should_receive(:generate_empty_abstract_ar_class).and_return(klass)
DbCharmer::ConnectionProxy.should_receive(:new).with(klass, :foo)
DbCharmer::ConnectionFactory.establish_connection_to_db(:foo, :username => :foo)
end
end
context "in connect method" do
before do
DbCharmer::ConnectionFactory.reset!
end
it "should return a connection proxy" do
DbCharmer::ConnectionFactory.connect(:logs).should be_kind_of(ActiveRecord::ConnectionAdapters::AbstractAdapter)
end
# should_receive is evil on a singletone classes
# it "should memoize proxies" do
# conn = mock('connection3')
# DbCharmer::ConnectionFactory.should_receive(:establish_connection).with('foo', false).once.and_return(conn)
# DbCharmer::ConnectionFactory.connect(:foo)
# DbCharmer::ConnectionFactory.connect(:foo)
# end
end
context "in connect_to_db method" do
before do
DbCharmer::ConnectionFactory.reset!
@conf = {
:adapter => 'mysql',
:username => "db_charmer_ro",
:database => "db_charmer_sandbox_test",
:connection_name => 'sanbox_ro'
}
end
it "should return a connection proxy" do
DbCharmer::ConnectionFactory.connect_to_db(@conf[:connection_name], @conf).should be_kind_of(ActiveRecord::ConnectionAdapters::AbstractAdapter)
end
# should_receive is evil on a singletone classes
# it "should memoize proxies" do
# conn = mock('connection4')
# DbCharmer::ConnectionFactory.should_receive(:establish_connection_to_db).with(@conf[:connection_name], @conf).once.and_return(conn)
# DbCharmer::ConnectionFactory.connect_to_db(@conf[:connection_name], @conf)
# DbCharmer::ConnectionFactory.connect_to_db(@conf[:connection_name], @conf)
# end
end
end
================================================
FILE: test-project/spec/unit/connection_proxy_spec.rb
================================================
require 'spec_helper'
describe DbCharmer::ConnectionProxy do
before(:each) do
class ProxyTest; end
@conn = mock('connection')
@proxy = DbCharmer::ConnectionProxy.new(ProxyTest, :foo)
end
it "should retrieve connection from an underlying class" do
ProxyTest.should_receive(:retrieve_connection).and_return(@conn)
@proxy.inspect
end
it "should be a blankslate for the connection" do
ProxyTest.stub!(:retrieve_connection).and_return(@conn)
@proxy.should be(@conn)
end
it "should proxy methods with a block parameter" do
module MockConnection
def self.foo
raise "No block given!" unless block_given?
yield
end
end
ProxyTest.stub!(:retrieve_connection).and_return(MockConnection)
res = @proxy.foo { :foo }
res.should == :foo
end
it "should proxy all calls to the underlying class connections" do
ProxyTest.stub!(:retrieve_connection).and_return(@conn)
@conn.should_receive(:foo)
@proxy.foo
end
end
================================================
FILE: test-project/spec/unit/db_charmer_spec.rb
================================================
require 'spec_helper'
describe DbCharmer do
after do
DbCharmer.current_controller = nil
DbCharmer.connections_should_exist = false
end
it "should define version constants" do
DbCharmer::Version::STRING.should match(/^\d+\.\d+\.\d+/)
end
it "should have connections_should_exist accessors" do
DbCharmer.connections_should_exist.should_not be_nil
DbCharmer.connections_should_exist = :foo
DbCharmer.connections_should_exist.should == :foo
end
it "should have connections_should_exist? method" do
DbCharmer.connections_should_exist = true
DbCharmer.connections_should_exist?.should be_true
DbCharmer.connections_should_exist = false
DbCharmer.connections_should_exist?.should be_false
DbCharmer.connections_should_exist = "shit"
DbCharmer.connections_should_exist?.should be_true
DbCharmer.connections_should_exist = nil
DbCharmer.connections_should_exist?.should be_false
end
it "should have current_controller accessors" do
DbCharmer.respond_to?(:current_controller).should be_true
DbCharmer.current_controller = :foo
DbCharmer.current_controller.should == :foo
DbCharmer.current_controller = nil
end
context "in force_slave_reads? method" do
it "should return true if force_slave_reads=true" do
DbCharmer.force_slave_reads?.should be_false
DbCharmer.force_slave_reads do
DbCharmer.force_slave_reads?.should be_true
end
DbCharmer.force_slave_reads?.should be_false
end
it "should return false if no controller defined and global force_slave_reads=false" do
DbCharmer.current_controller = nil
DbCharmer.force_slave_reads?.should be_false
end
it "should consult with the controller about forcing slave reads if possible" do
DbCharmer.current_controller = mock("controller")
DbCharmer.current_controller.should_receive(:force_slave_reads?).and_return(true)
DbCharmer.force_slave_reads?.should be_true
DbCharmer.current_controller.should_receive(:force_slave_reads?).and_return(false)
DbCharmer.force_slave_reads?.should be_false
end
end
context "in with_controller method" do
it "should fail if no block given" do
lambda { DbCharmer.with_controller(:foo) }.should raise_error(ArgumentError)
end
it "should switch controller while running the block" do
DbCharmer.current_controller = nil
DbCharmer.current_controller.should be_nil
DbCharmer.with_controller(:foo) do
DbCharmer.current_controller.should == :foo
end
DbCharmer.current_controller.should be_nil
end
it "should ensure current controller is reverted to nil in case of errors" do
lambda {
DbCharmer.with_controller(:foo) { raise "fuck" }
}.should raise_error
DbCharmer.current_controller.should be_nil
end
end
end
================================================
FILE: test-project/spec/unit/multi_db_proxy_spec.rb
================================================
require 'spec_helper'
describe "ActiveRecord model with db_magic" do
before do
class Blah < ActiveRecord::Base
self.table_name = :posts
db_magic :connection => nil
end
end
describe "(instance)" do
before do
@blah = Blah.new
end
describe "in on_db method" do
describe "with a block" do
it "should switch connection to specified one and yield the block" do
Blah.db_charmer_connection_proxy.should be_nil
@blah.on_db(:logs) do
Blah.db_charmer_connection_proxy.should_not be_nil
end
end
it "should switch connection back after the block finished its work" do
Blah.db_charmer_connection_proxy.should be_nil
@blah.on_db(:logs) {}
Blah.db_charmer_connection_proxy.should be_nil
end
it "should manage connection level values" do
Blah.db_charmer_connection_level.should == 0
@blah.on_db(:logs) do |m|
m.class.db_charmer_connection_level.should == 1
end
Blah.db_charmer_connection_level.should == 0
end
end
describe "as a chain call" do
it "should switch connection for all chained calls" do
Blah.db_charmer_connection_proxy.should be_nil
@blah.on_db(:logs).should_not be_nil
end
it "should switch connection for non-chained calls" do
Blah.db_charmer_connection_proxy.should be_nil
@blah.on_db(:logs).to_s
Blah.db_charmer_connection_proxy.should be_nil
end
it "should restore connection" do
User.first
User.connection.object_id.should == User.on_master.connection.object_id
User.on_db(:slave01).first
User.connection.object_id.should == User.on_master.connection.object_id
end
it "should restore connection after error" do
pending "Disabled in RSpec prior to version 2 because of lack of .any_instance support" unless Object.respond_to?(:any_instance)
User.on_db(:slave01).first
User.first
ActiveRecord::Base.connection_handler.clear_all_connections!
ActiveRecord::ConnectionAdapters::MysqlAdapter.any_instance.stub(:connect) { raise Mysql::Error, 'Connection error' }
expect { User.on_db(:slave01).first }.to raise_error(Mysql::Error)
ActiveRecord::ConnectionAdapters::MysqlAdapter.any_instance.unstub(:connect)
User.connection.connection_name.should == User.on_master.connection.connection_name
end
end
end
end
describe "(class)" do
describe "in on_db method" do
describe "with a block" do
it "should switch connection to specified one and yield the block" do
Blah.db_charmer_connection_proxy.should be_nil
Blah.on_db(:logs) do
Blah.db_charmer_connection_proxy.should_not be_nil
end
end
it "should switch connection back after the block finished its work" do
Blah.db_charmer_connection_proxy.should be_nil
Blah.on_db(:logs) {}
Blah.db_charmer_connection_proxy.should be_nil
end
it "should manage connection level values" do
Blah.db_charmer_connection_level.should == 0
Blah.on_db(:logs) do |m|
m.db_charmer_connection_level.should == 1
end
Blah.db_charmer_connection_level.should == 0
end
end
describe "as a chain call" do
it "should switch connection for all chained calls" do
Blah.db_charmer_connection_proxy.should be_nil
Blah.on_db(:logs).should_not be_nil
end
it "should switch connection for non-chained calls" do
Blah.db_charmer_connection_proxy.should be_nil
Blah.on_db(:logs).to_s
Blah.db_charmer_connection_proxy.should be_nil
end
end
end
describe "in on_slave method" do
before do
Blah.db_magic :slaves => [ :slave01 ]
end
it "should use one tof the model's slaves if no slave given" do
Blah.on_slave.db_charmer_connection_proxy.object_id.should == Blah.coerce_to_connection_proxy(:slave01).object_id
end
it "should use given slave" do
Blah.on_slave(:logs).db_charmer_connection_proxy.object_id.should == Blah.coerce_to_connection_proxy(:logs).object_id
end
it 'should support block calls' do
Blah.on_slave do |m|
m.db_charmer_connection_proxy.object_id.should == Blah.coerce_to_connection_proxy(:slave01).object_id
end
end
end
describe "in on_master method" do
before do
Blah.db_magic :slaves => [ :slave01 ]
end
it "should run queries on the master" do
Blah.on_master.db_charmer_connection_proxy.should be_nil
end
end
end
end
================================================
FILE: test-project/spec/unit/with_remapped_databases_spec.rb
================================================
require 'spec_helper'
describe "DbCharmer#with_remapped_databases" do
before(:all) do
DbCharmer.connections_should_exist = false
end
let(:logs_connection) { DbCharmer::ConnectionFactory.connect(:logs) }
let(:slave_connection) { DbCharmer::ConnectionFactory.connect(:slave01) }
let(:master_connection) { Avatar.connection }
before :each do
class User < ActiveRecord::Base
db_magic :connection => :slave01
end
end
def should_have_connection(model_class, connection)
model_class.connection.object_id.should == connection.object_id
end
it "should remap the right connection" do
should_have_connection(LogRecord, logs_connection)
DbCharmer.with_remapped_databases(:logs => :slave01) do
should_have_connection(LogRecord, slave_connection)
end
should_have_connection(LogRecord, logs_connection)
end
it "should not remap other connections" do
should_have_connection(Avatar, master_connection)
should_have_connection(User, slave_connection)
DbCharmer.with_remapped_databases(:logs => :slave01) do
should_have_connection(Avatar, master_connection)
should_have_connection(User, slave_connection)
end
should_have_connection(Avatar, master_connection)
should_have_connection(User, slave_connection)
end
it "should allow remapping multiple databases" do
should_have_connection(Avatar, master_connection)
should_have_connection(LogRecord, logs_connection)
DbCharmer.with_remapped_databases(:master => :logs, :logs => :slave01) do
should_have_connection(Avatar, logs_connection)
should_have_connection(LogRecord, slave_connection)
end
should_have_connection(Avatar, master_connection)
should_have_connection(LogRecord, logs_connection)
end
it "should remap the master connection when asked to, but not other connections" do
should_have_connection(Avatar, master_connection)
should_have_connection(User, slave_connection)
should_have_connection(LogRecord, logs_connection)
DbCharmer.with_remapped_databases(:master => :slave01) do
should_have_connection(Avatar, slave_connection)
should_have_connection(User, slave_connection)
should_have_connection(LogRecord, logs_connection)
end
should_have_connection(Avatar, master_connection)
should_have_connection(User, slave_connection)
should_have_connection(LogRecord, logs_connection)
end
it "should not override connections that are explicitly specified" do
DbCharmer.with_remapped_databases(:logs => :slave01) do
should_have_connection(LogRecord, slave_connection)
should_have_connection(LogRecord.on_db(:master), master_connection)
LogRecord.on_db(:master) do
should_have_connection(LogRecord, master_connection)
end
should_have_connection(LogRecord.on_db(:logs), logs_connection)
LogRecord.on_db(:logs) do
should_have_connection(LogRecord, logs_connection)
end
should_have_connection(LogRecord, slave_connection)
end
end
it "should successfully run selects on the right database" do
# We need this call to make sure rails would fetch columns info from the logs server before we mess its connection up
LogRecord.all
# Remap LogRecord connection to slave01 and make sure selects would go there (even though we do not have the table there)
DbCharmer.with_remapped_databases(:logs => :slave01) do
logs_connection.should_not_receive(:select_all)
slave_connection.should_receive(:select_all).and_return([])
stub_columns_for_rails31 slave_connection
LogRecord.all.should be_empty
end
end
def unhijack!(klass)
if klass.respond_to?(:connection_with_magic)
klass.class_eval <<-END
class << self
undef_method(:connection_with_magic)
alias_method(:connection, :connection_without_magic)
undef_method(:connection_without_magic)
undef_method(:connection_pool_with_magic)
alias_method(:connection_pool, :connection_pool_without_magic)
undef_method(:connection_pool_without_magic)
end
END
end
raise "Unable to unhijack #{klass.name}" if klass.respond_to?(:connection_with_magic)
end
it "should hijack connections only when necessary" do
unhijack!(Category)
Category.respond_to?(:connection_with_magic).should be_false
DbCharmer.with_remapped_databases(:logs => :slave01) do
Category.respond_to?(:connection_with_magic).should be_false
end
Category.respond_to?(:connection_with_magic).should be_false
DbCharmer.with_remapped_databases(:master => :slave01) do
Category.respond_to?(:connection_with_magic).should be_true
should_have_connection(Category, slave_connection)
end
end
end
================================================
FILE: test-project-2.x/Gemfile
================================================
source 'http://rubygems.org'
gem 'rails', '2.3.18'
gem 'rake', '0.9.2.2'
gem 'mysql'
gem 'rspec', '1.3.2'
gem 'rspec-rails', '1.3.4'
# Load DbCharmer as a gem
gem 'db-charmer', :path => '..', :require => 'db_charmer'
================================================
FILE: test-project-2.x/Rakefile
================================================
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'tasks/rails'
================================================
FILE: test-project-2.x/config/boot.rb
================================================
# We only have test environment here
ENV['RAILS_ENV'] = 'test'
# Don't change this file!
# Configure your app in config/environment.rb and config/environments/*.rb
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
module Rails
class << self
def boot!
unless booted?
preinitialize
pick_boot.run
end
end
def booted?
defined? Rails::Initializer
end
def pick_boot
(vendor_rails? ? VendorBoot : GemBoot).new
end
def vendor_rails?
File.exist?("#{RAILS_ROOT}/vendor/rails")
end
def preinitialize
load(preinitializer_path) if File.exist?(preinitializer_path)
end
def preinitializer_path
"#{RAILS_ROOT}/config/preinitializer.rb"
end
end
class Boot
def run
load_initializer
Rails::Initializer.class_eval do
def load_gems
@bundler_loaded ||= Bundler.require :default, Rails.env
end
end
Rails::Initializer.run(:set_load_path)
end
end
class VendorBoot < Boot
def load_initializer
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
Rails::Initializer.run(:install_gem_spec_stubs)
Rails::GemDependency.add_frozen_gem_path
end
end
class GemBoot < Boot
def load_initializer
self.class.load_rubygems
load_rails_gem
require 'initializer'
end
def load_rails_gem
if version = self.class.gem_version
gem 'rails', version
else
gem 'rails'
end
rescue Gem::LoadError => load_error
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
exit 1
end
class << self
def rubygems_version
Gem::RubyGemsVersion rescue nil
end
def gem_version
if defined? RAILS_GEM_VERSION
RAILS_GEM_VERSION
elsif ENV.include?('RAILS_GEM_VERSION')
ENV['RAILS_GEM_VERSION']
else
parse_gem_version(read_environment_rb)
end
end
def load_rubygems
require 'rubygems'
min_version = '1.3.1'
unless rubygems_version >= min_version
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
exit 1
end
rescue LoadError
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
exit 1
end
def parse_gem_version(text)
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
end
private
def read_environment_rb
File.read("#{RAILS_ROOT}/config/environment.rb")
end
end
end
end
# All that for this:
Rails.boot!
================================================
FILE: test-project-2.x/config/database.yml.example
================================================
common: &common
adapter: mysql
encoding: utf8
reconnect: false
pool: 1
username: root
password:
#----------------------------------------------------------------
test:
<<: *common
database: db_charmer_sandbox_test
# logs database
logs:
<<: *common
database: db_charmer_logs_test
# slave database
slave01:
<<: *common
username: db_charmer_ro
database: db_charmer_sandbox_test
user_master:
<<: *common
database: db_charmer_sandbox_test
# shard mapping db
social_shard_info:
<<: *common
database: db_charmer_sandbox_test
# for migrations only
social_shard01:
<<: *common
database: db_charmer_events_test_shard01
# for migrations only
social_shard02:
<<: *common
database: db_charmer_events_test_shard02
#----------------------------------------------------------------
test22:
<<: *common
database: db_charmer_sandbox22_test
# logs database
logs:
<<: *common
database: db_charmer_logs22_test
# slave database
slave01:
<<: *common
username: db_charmer_ro
database: db_charmer_sandbox22_test
user_master:
<<: *common
database: db_charmer_sandbox22_test
# shard mapping db
social_shard_info:
<<: *common
database: db_charmer_sandbox22_test
# for migrations only
social_shard01:
<<: *common
database: db_charmer_events22_test_shard01
# for migrations only
social_shard02:
<<: *common
database: db_charmer_events22_test_shard02
================================================
FILE: test-project-2.x/config/environment.rb
================================================
# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.3.18' unless defined? RAILS_GEM_VERSION
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
Rails::Initializer.run do |config|
config.time_zone = 'UTC'
end
================================================
FILE: test-project-2.x/config/environments/test.rb
================================================
# Settings specified here will take precedence over those in config/environment.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_view.cache_template_loading = true
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
================================================
FILE: test-project-2.x/config/initializers/backtrace_silencers.rb
================================================
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
# You can also remove all the silencers if you're trying do debug a problem that might steem from framework code.
# Rails.backtrace_cleaner.remove_silencers!
================================================
FILE: test-project-2.x/config/initializers/db_charmer.rb
================================================
DbCharmer.connections_should_exist = false # Since we are not in production
DbCharmer.enable_controller_magic!
================================================
FILE: test-project-2.x/config/initializers/inflections.rb
================================================
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format
# (all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
================================================
FILE: test-project-2.x/config/initializers/mime_types.rb
================================================
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
# Mime::Type.register_alias "text/html", :iphone
================================================
FILE: test-project-2.x/config/initializers/new_rails_defaults.rb
================================================
# Be sure to restart your server when you modify this file.
# These settings change the behavior of Rails 2 apps and will be defaults
# for Rails 3. You can remove this initializer when Rails 3 is released.
if defined?(ActiveRecord)
# Include Active Record class name as root for JSON serialized output.
ActiveRecord::Base.include_root_in_json = true
# Store the full class name (including module namespace) in STI type column.
ActiveRecord::Base.store_full_sti_class = true
end
# Use ISO 8601 format for JSON serialized times and dates.
ActiveSupport.use_standard_json_time_format = true
# Don't escape HTML entities in JSON, leave that for the #json_escape helper.
# if you're including raw json in an HTML page.
ActiveSupport.escape_html_entities_in_json = false
================================================
FILE: test-project-2.x/config/initializers/session_store.rb
================================================
# Be sure to restart your server when you modify this file.
# Your secret key for verifying cookie session data integrity.
# If you change this key, all old sessions will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
ActionController::Base.session = {
:key => '_db_charmer_sandbox_session',
:secret => '9b67feed7aa8a2741d9f0ac6efde543d726f7a017c8a635346be733f287fd479fbd8521c1e8a06e91af7920de1fb50b942bdf24b6ecee1569ed947c13f6697af'
}
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rake db:sessions:create")
# ActionController::Base.session_store = :active_record_store
================================================
FILE: test-project-2.x/config/initializers/sharding.rb
================================================
# Range-based shards for testing
TEXTS_SHARDING_RANGES = {
0...100 => :shard1,
100..200 => :shard2,
:default => :shard3
}
DbCharmer::Sharding.register_connection(
:name => :texts,
:method => :range,
:ranges => TEXTS_SHARDING_RANGES
)
#------------------------------------------------
# Db blocks map sharding for testing
SOCIAL_SHARDING = DbCharmer::Sharding.register_connection(
:name => :social,
:method => :db_block_map,
:block_size => 10,
:map_table => :event_shards_map,
:shards_table => :event_shards_info,
:connection => :social_shard_info
)
================================================
FILE: test-project-2.x/config/locales/en.yml
================================================
# Sample localization file for English. Add more files in this directory for other locales.
# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
hello: "Hello world"
================================================
FILE: test-project-2.x/config/preinitializer.rb
================================================
begin
require "rubygems"
require "bundler"
rescue LoadError
raise "Could not load the bundler gem. Install it with `gem install bundler`."
end
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.24")
raise RuntimeError, "Your bundler version is too old for Rails 2.3." +
"Run `gem install bundler` to upgrade."
end
begin
# Set up load paths for all bundled gems
ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
Bundler.setup
rescue Bundler::GemNotFound
raise RuntimeError, "Bundler couldn't find some gems." +
"Did you run `bundle install`?"
end
================================================
FILE: test-project-2.x/config/routes.rb
================================================
ActionController::Routing::Routes.draw do |map|
# Resource routes
map.resources :posts
map.resources :cars
# Install the default routes as the lowest priority.
# Note: These default routes make all actions in every controller accessible via GET requests. You should
# consider removing or commenting them out if you're using named routes and resources.
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
================================================
FILE: test-project-2.x/script/console
================================================
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'
require 'commands/console'
================================================
FILE: test-project-2.x/spec/spec.opts
================================================
--colour
--format specdoc
================================================
FILE: test-project-2.x/spec/spec_helper.rb
================================================
# This file is copied to ~/spec when you run 'ruby script/generate rspec'
# from the project root directory.
ENV["RAILS_ENV"] = 'test'
require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment'))
require 'spec/autorun'
require 'spec/rails'
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
Spec::Runner.configure do |config|
# If you're not using ActiveRecord you should remove these
# lines, delete config/database.yml and disable :active_record
# in your config/boot.rb
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
# == Fixtures
#
# You can declare fixtures for each example_group like this:
# describe "...." do
# fixtures :table_a, :table_b
#
# Alternatively, if you prefer to declare them only once, you can
# do so right here. Just uncomment the next line and replace the fixture
# names with your fixtures.
#
# config.global_fixtures = :table_a, :table_b
#
# If you declare global fixtures, be aware that they will be declared
# for all of your examples, even those that don't use them.
#
# You can also declare which fixtures to use (for example fixtures for test/fixtures):
#
# config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
#
# == Mock Framework
#
# RSpec uses its own mocking framework by default. If you prefer to
# use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
#
# == Notes
#
# For more information take a look at Spec::Runner::Configuration and Spec::Runner
end