Giter VIP home page Giter VIP logo

rails-sharding's Introduction

Rails::Sharding

Build Status Code Climate Test Coverage Gem Version Dependency Status

Simple and robust sharding gem for Rails, including Migrations and ActiveRecord extensions

This gems allows you to easily create extra databases to your rails application, and freely allocate ActiveRecord instances to any of the databases.

Accessing shards is as simple as:

  # creating a user to a specific shard
  new_user = User.using_shard(:shard_group1, :shard1).create(username: 'x')

  # retrieving a user from a specific shard
  loaded_user = User.using_shard(:shard_group1, :shard1).where(username: 'x').first

You can also use the block syntax:

  Rails::Sharding.using_shard(:shard_group1, :shard1) do
    # All statements inside this block will go to the selected shard

    # Do some queries
    new_user = User.create(username: 'x')
    loaded_user = User.where(username: 'x').first
    billing_infos = loaded_user.billing_infos.all
  end

You can also pick and choose which models will be shardable. Non shardable models will be retrieved from the master database, even if inside a using_shard block.

Compatibility

Gem version 1.x.x:

  • Rails 5.0, 5.1 and 5.2
  • Databases: MySQL, MariaDB, Postgres

Gem version 0.x.x:

  • Rails 4.2
  • Databases: MySQL, MariaDB

Installation

Add this line to your application's Gemfile:

gem 'rails-sharding'

And then execute:

bundle

Creating Shards

To start with the rails-sharding gem, run the command

rails g rails_sharding:scaffold

This will generate a config/shards.yml.example like this:

default: &default
  adapter: mysql2
  encoding: utf8
  reconnect: false
  pool: 5
  username: <%= ENV["MYSQL_USERNAME"] %>
  password: <%= ENV["MYSQL_PASSWORD"] %>
  socket: /var/run/mysqld/mysqld.sock

development:
  shard_group1:
    shard1:
      <<: *default
      database: group1_shard1_development
    shard2:
      <<: *default
      database: group1_shard2_development
...

Rename it to config/shards.yml and change it to your database configuration. This example file defines a single shard group (named shard_group1) containing two shards (shard1 and shard2).

A shard group is a set of shards that should have the same schema.

When you're ready to create the shards run

rake shards:create

Migrating Shards

Go to the directory db/shards_migrations/shard_group1 and add all migrations that you want to run on the shards of shard_group1. By design, all shards in a same group should always have the same schema.

As of now, there is no generator for migrations. You can use the regular rails generator and move the migrations to the shards_migration folder.

For example, add the following migration to your db/shards_migrations/shard_group1:

# 20160808000000_create_users.rb
class CreateClients < ActiveRecord::Migration[5.0]
  def up
    create_table :users do |t|
      t.string :username, :limit => 100
      t.timestamps
    end
  end

  def down
    drop_table :users
  end
end

Then run:

rake shards:migrate

All the shards will be migrated, and one schema file will be dumped for each of the shards (just like rails would do for your master database). You can see the schema of the shards in db/shards_schemas/shard_group1/, and it will be something like:

ActiveRecord::Schema.define(version: 20160808000000) do

  create_table "users", force: :cascade do |t|
    t.string   "username",       limit: 100
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end

Other rake tasks

The rails-sharding gem offers several rake tasks analogous to the ones offered by ActiveRecord:

rake shards:create
rake shards:drop
rake shards:migrate
rake shards:migrate:down
rake shards:migrate:redo
rake shards:migrate:reset
rake shards:migrate:up
rake shards:rollback
rake shards:schema:dump
rake shards:schema:load
rake shards:test:load_schema
rake shards:test:prepare
rake shards:test:purge
rake shards:version

They work just the same as the tasks rake:db:... but they operate on all shards of all shard groups. If you want to run a rake task just to a specific shard group or shard you can use the SHARD_GROUP and SHARD options:

rake shards:migrate SHARD_GROUP=shard_group_1
rake shards:migrate SHARD_GROUP=shard_group_1 SHARD=shard1

Gem Options

Running the rails g rails_sharding:scaffold will create an initializer at config/initializers/rails-sharding.rb. You can pass additional configurations on this initializer to control the gem behavior. You can see below all available options and their default values:

# config/initializers/rails-sharding.rb

Rails::Sharding.setup do |config|
  # If true one connection will be established per shard (in every shard group) on startup.
  # If false the user must call Shards::ConnectionHandler.establish_connection(shard_group, shard_name) manually at least once before using each shard.
  config.establish_all_connections_on_setup = true

  # If true the method #using_shard will be mixed in ActiveRecord scopes. Put this to false if you don't want the gem to modify ActiveRecord
  config.extend_active_record_scope = true

  # If true the query logs of ActiveRecord will be tagged with the corresponding shard you're querying
  config.add_shard_tag_to_query_logs = true

  # Specifies where to find the definition of the shards configurations
  config.shards_config_file = 'config/shards.yml'

  # Specifies where to find the migrations for each shard group
  config.shards_migrations_dir = 'db/shards_migrations'

  # Specifies where to find the schemas for each shard group
  config.shards_schemas_dir = 'db/shards_schemas'
end

Wiki

Want to know more? How to integrate with RSpec, Capistrano, etc? Take a look at our wiki.

Development and Contributing

After checking out the repo:

  1. Run bundle to install gems

  2. Create your spec/fixtures/shards.yml based on the example on this same folder (you need MySQL and Postgres)

  3. Create your .env based on the example on this same folder.

  4. Run rake db:test:prepare to create the test shards.

  5. Run rspec to run the tests.

Bug reports and pull requests are welcome on GitHub at https://github.com/hsgubert/rails-sharding.

License

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

Acknowledgements

This gem was inspired on several other gems like: octopus, shard_handler and active_record_shards.

rails-sharding's People

Contributors

adomokos avatar gabrielmbarboza avatar hsgubert avatar mwallba avatar raphael-nogueira avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

rails-sharding's Issues

Dependency on Rails

Hello,

we are evaluating this gem and we noticed it has a dependency on Rails. We've also looked at ar-octopus, but that has no dependency on Rails, only on activerecord.

We are considering using it in a pure Ruby app and we are hesitant to use it due to this dependency.

Can you please describe why you have the dependency on Rails and not on ActiveRecord only? Would you consider a PR to narrow that dependency?

Thanks!

Explicit 'using_shard' in Sharded Associations

Something we enjoyed in Octopus was the seamless AR associations once you made it into the shard.

For example, this worked for us:

new_account = Account.using_shard(:mysql_group, :shard1).first
expect(new_account.users.first).to be == new_user

However, with rails-sharding, the shard has to be specified like this:

new_account = Account.using_shard(:mysql_group, :shard1).first
# See the using_shard(:mysql_group, :shard1) call on the users collection
expect(new_account.users.using_shard(:mysql_group, :shard1).first).to be == new_user

Would it be hard to improve this logic and "tag" the users AR relation with the currently used shard, so the explicit using_shard(:mysql_group, :shard1) would not be needed?

Incorrect Gemspec for Rails version?

I think this makes your gem require at minimum Rails 5.2:

spec.add_runtime_dependency 'rails', '~> 5.2.0'

Contrary to the README that says it should support 5.0, 5.1, 5.2.

Based on docs:

The specifier ~> has a special meaning, best shown by example. ~> 2.0.3 is identical to >= 2.0.3 and < 2.1. ~> 2.1 is identical to >= 2.1 and < 3.0. ~> 2.2.beta will match prerelease versions like 2.2.beta.12. ~> 0 is identical to >= 0.0 and < 1.0.

I think you want:

spec.add_runtime_dependency 'rails', '~> 5.0' 

undefined method `thread_mattr_accessor' for Rails::Sharding::ShardThreadRegistry:Class

Following a fresh install of rails-sharding v1.1.0, issuing the following command (taken from the readme):

rails g rails_sharding:scaffold

Results in the stack trace below.

Environment:

  • jruby 9.1.17.0 (2.3.3) 2018-04-20 d8b1ff9 Java HotSpot(TM) 64-Bit Server VM 25.121-b13 on 1.8.0_121-b13 +jit [darwin-x86_64]
  • bundler 1.16.1
  • rvm 1.29.4 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]
NoMethodError: undefined method `thread_mattr_accessor' for Rails::Sharding::ShardThreadRegistry:Class
                        method_missing at org/jruby/RubyBasicObject.java:1657
           <class:ShardThreadRegistry> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding/shard_thread_registry.rb:8
                     <module:Sharding> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding/shard_thread_registry.rb:3
                                <main> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding/shard_thread_registry.rb:2
                               require at org/jruby/RubyKernel.java:956
                      block in require at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292
                       load_dependency at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:258
                               require at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292
                                <main> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding/core.rb:1
                               require at org/jruby/RubyKernel.java:956
                      block in require at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292
                       load_dependency at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:258
                               require at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292
                                <main> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding/core.rb:5
                               require at org/jruby/RubyKernel.java:956
                       block in (root) at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding.rb:1
                                  each at org/jruby/RubyArray.java:1735
                                (root) at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/rails-sharding-1.1.0/lib/rails/sharding.rb:4
                                (root) at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/bundler-1.16.1/lib/bundler/runtime.rb:1
                                <main> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/bundler-1.16.1/lib/bundler/runtime.rb:95
                               require at org/jruby/RubyKernel.java:956
                               require at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/bundler-1.16.1/lib/bundler/runtime.rb:65
                               require at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/bundler-1.16.1/lib/bundler.rb:114
                                (root) at /Users/jason/IdeaProjects/player/config/application.rb:13
                                (root) at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/railties-5.1.6/lib/rails/command/actions.rb:1
  require_application_and_environment! at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/railties-5.1.6/lib/rails/command/actions.rb:15
                               perform at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/railties-5.1.6/lib/rails/commands/generate/generate_command.rb:19
                                   run at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/thor-0.20.0/lib/thor/command.rb:27
                                <main> at /Users/jason/.rvm/gems/jruby-9.1.17.0@player/gems/thor-0.20.0/lib/thor/invocation.rb:126
                               require at org/jruby/RubyKernel.java:956
                                <main> at bin/rails:4

Are there any plans to support rails 6?

I've seen the work done for this app and seems amazing! I am currently running a rails 5 app and have been wanting to upgrade to rails 6 but since I'm using Octopus I've not been able to. Also, there has been some issue with idle connections to the DB that i'm fairly certain is caused by Octopus. I also would like to understand if possible your insight about rails 6 sharding and some key differences with your gem. Would love to collaborate with some PRs in the future if this seems like the best option four our app.
https://edgeguides.rubyonrails.org/active_record_multiple_databases.html#horizontal-sharding

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.