ar-octopus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.mkdn ADDED
@@ -0,0 +1,49 @@
1
+ <h1> Octopus - Easy Database Sharding for ActiveRecord</h1>
2
+
3
+ <p> Octopus is a better way to do Database Sharding in ActiveRecord. Sharding allows multiple databases in the same rails application. While there are several projects that implement Sharding (e.g. DbCharmer, DataFabric), each project has its own limitations. The main goal of octopus project is to provide a nice and clean way of doing Database Sharding.</p>
4
+
5
+ <h2>Feature list: </h2>
6
+ <p> The design of the api is made to be simple as possible. Octopus is focusing in the end user, giving the power of multiple databases, but with reliable code and flexibility. Octopus is focused on Rails 3, but will be soon compatible with Rails 2.x.</p>
7
+
8
+ <p> Octopus supports: </p>
9
+
10
+ - Sharding (with multiple shards, and grouped shards).
11
+ - Replication (Master/slave support, with multiple slaves).
12
+ - Moving data between shards with migrations.
13
+ - Tools to manage database configurations. (soon)
14
+
15
+ <p> To see the complete list of features and syntax, please check out our <a href="http://wiki.github.com/tchandy/octopus/"> Wiki</a>
16
+
17
+ <h2>Thanks</h2>
18
+
19
+ This project is sponsored by the <a href="http://www.rubysoc.org">Ruby Summer of Code</a>,
20
+ and my mentors <a href="http://github.com/mperham">Mike Perham</a> and <a href="http://github.com/amitagarwal">Amit Agarwal</a>.
21
+
22
+
23
+ <h3>If you are writing code that smells using octopus, the EVIL Octopus is going behind you, be careful.</h3>
24
+ <pre>
25
+ _________________
26
+ ___ | I'm so EVIL! |
27
+ .-' `'. |______________ |
28
+ / \ /
29
+ | ; /
30
+ | | / ___.--,
31
+ _.._ |0) ~ (0) | _.---'`__.-( (_.
32
+ __.--'`_.. '.__.\ '--. \_.-' ,.--'` `""`
33
+ ( ,.--'` ',__ /./; ;, '.__.'` __
34
+ _`) ) .---.__.' / | |\ \__..--"" """--.,_
35
+ `---' .'.''-._.-'`_./ /\ '. \ _.-~~~````~~~-._`-.__.'
36
+ | | .' _.-' | | \ \ '. `~---`
37
+ \ \/ .' \ \ '. '-._)
38
+ \/ / \ \ `=.__`~-.
39
+ / /\ `) ) / / `"".`\
40
+ , _.-'.'\ \ / / ( ( / /
41
+ `--~` ) ) .-'.' '.'. | (
42
+ (/` ( (` ) ) '-;
43
+ ` '-; (-'
44
+ </pre>
45
+
46
+
47
+ <h2>Copyright</h2>
48
+
49
+ Copyright (c) 2010 Thiago Pradi, released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,106 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+ require 'rubygems'
3
+ require 'rake'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "ar-octopus"
9
+ gem.summary = "Easy Database Sharding for ActiveRecord"
10
+ gem.description = "This gem allows you to use sharded databases with ActiveRecord. this also provides a interface for replication and for running migrations with multiples shards."
11
+ gem.email = "tchandy@gmail.com"
12
+ gem.homepage = "http://github.com/tchandy/octopus"
13
+ gem.authors = ["Thiago Pradi", "Mike Perham", "Amit Agarwal"]
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ gem.version = "0.0.1"
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "octopus #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
47
+
48
+ namespace :db do
49
+ desc 'Build the MySQL test databases'
50
+ task :build_databases do
51
+ mysql_user = ENV['MYSQL_USER'] || "root"
52
+ postgres_user = ENV['POSTGRES_USER'] || "postgres"
53
+ (1..5).each do |idx|
54
+ %x( echo "create DATABASE octopus_shard#{idx} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci " | mysql --user=#{mysql_user})
55
+ end
56
+
57
+ %x( createdb -E UTF8 -U #{postgres_user} octopus_shard1 )
58
+ end
59
+
60
+ desc 'Drop the MySQL test databases'
61
+ task :drop_databases do
62
+ mysql_user = ENV['MYSQL_USER'] || "root"
63
+ postgres_user = ENV['POSTGRES_USER'] || "postgres"
64
+ (1..5).each do |idx|
65
+ %x( mysqladmin --user=#{mysql_user} -f drop octopus_shard#{idx} )
66
+ end
67
+
68
+ %x( dropdb -U #{postgres_user} octopus_shard1 )
69
+ end
70
+
71
+ desc 'Create tables on mysql databases'
72
+ task :create_tables do
73
+ Dir.chdir(File.expand_path(File.dirname(__FILE__) + "/spec"))
74
+ require "database_connection"
75
+ require "octopus"
76
+ [:master, :brazil, :canada, :russia, :alone_shard, :postgresql_shard].each do |shard_symbol|
77
+ ActiveRecord::Base.using(shard_symbol).connection.create_table(:users) do |u|
78
+ u.string :name
79
+ end
80
+
81
+ ActiveRecord::Base.using(shard_symbol).connection.create_table(:clients) do |u|
82
+ u.string :country
83
+ u.string :name
84
+ end
85
+
86
+ ActiveRecord::Base.using(shard_symbol).connection.create_table(:cats) do |u|
87
+ u.string :name
88
+ end
89
+
90
+ ActiveRecord::Base.using(shard_symbol).connection.create_table(:items) do |u|
91
+ u.string :name
92
+ u.integer :client_id
93
+ end
94
+
95
+ ActiveRecord::Base.using(shard_symbol).connection.create_table(:schema_migrations) do |u|
96
+ u.string :version, :unique => true, :null => false
97
+ end
98
+ end
99
+ end
100
+
101
+ desc 'Prepare the MySQL test databases'
102
+ task :prepare => [:drop_databases, :build_databases, :create_tables]
103
+ end
104
+
105
+
106
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/doc/api.textile ADDED
@@ -0,0 +1,98 @@
1
+ h1. Api Design
2
+
3
+ * The API design should be simple as possible, so they will just have one method to change the shards, the method using, which will be different in the context that him get called. In controller, they will send all queries in the action or controller to a specified shard. So, if you want to get all of your application sharded, you need to do a before_filter in the application controller.
4
+ * The other method will be sharded_by, which selects the shard using a attribute of the model. This method is just accessible from class method on ActiveRecord::Base.
5
+ * The using method also could be called from models, as a scope, or receiving a block, that will run all the methods inside a specific shard.
6
+ * Also, the using method could be called from migrations. For default, migrations should run only in the master database, if you want to run in a different database, you should specify where you wish that migration run. like this:
7
+ <pre>
8
+ # This will run in all, master and slaves
9
+ class AddNameFieldToUser < ActiveRecord::Migration
10
+ using(:all)
11
+ #or
12
+ using_all
13
+
14
+ def self.up
15
+ add_column :users, :name, :string
16
+ end
17
+
18
+ def self.down
19
+ remove_column :users, :name, :string
20
+ end
21
+ end
22
+
23
+
24
+ # This will run just in all slaves
25
+ class AddNameFieldToUser < ActiveRecord::Migration
26
+ using(:slaves)
27
+ or
28
+ using_slaves()
29
+
30
+ def self.up
31
+ add_column :users, :name, :string
32
+ end
33
+
34
+ def self.down
35
+ remove_column :users, :name, :string
36
+ end
37
+ end
38
+
39
+ # This will run in all shards, slaves or not
40
+ class AddNameFieldToUser < ActiveRecord::Migration
41
+ using(:shards)
42
+
43
+ def self.up
44
+ add_column :users, :name, :string
45
+ end
46
+
47
+ def self.down
48
+ remove_column :users, :name, :string
49
+ end
50
+ end
51
+
52
+ # This will run in specific shard
53
+ class AddNameFieldToUser < ActiveRecord::Migration
54
+ using(:canada)
55
+
56
+ def self.up
57
+ add_column :users, :name, :string
58
+ end
59
+
60
+ def self.down
61
+ remove_column :users, :name, :string
62
+ end
63
+ end
64
+
65
+ # This will run in the specified shards, this accepts two or more shards.
66
+ class AddNameFieldToUser < ActiveRecord::Migration
67
+ using(:canada, :brazil)
68
+
69
+ def self.up
70
+ add_column :users, :name, :string
71
+ end
72
+
73
+ def self.down
74
+ remove_column :users, :name, :string
75
+ end
76
+ end
77
+ </pre>
78
+ * The default behavior on finds will be: if you find a object from a shard, like:
79
+
80
+ # This will find all users in brazil shard and iterate over it, saving all of them in the brazil shard
81
+ <pre>
82
+ User.using(:brazil).find(:all).each do |user|
83
+ user.name = "Brazil"
84
+ user.save()
85
+ end
86
+ </pre>
87
+
88
+ # This will find all users in brazil shard and iterate over it, saving all of them in the canada shard
89
+ <pre>
90
+ User.using(:brazil).find(:all).each do |user|
91
+ user.name = "Brazil"
92
+ user.using(:canada).save()
93
+ end
94
+ </pre>
95
+
96
+
97
+
98
+
@@ -0,0 +1,74 @@
1
+ h1. Octopus Features List:
2
+
3
+ * Support for replicated databases, with one master and multiple slaves. Multi-master support could be added later. The writes will be sended to master and the reads to slave, but this could be modified.
4
+ * Support database sharding, with a nice and clean syntax like:
5
+ <pre>
6
+ User.using(:awesome_shard).all() or User.using_awesome_shard.all()
7
+
8
+ class User < ActiveRecord::Base
9
+ sharded_by :code
10
+
11
+ def awesome_method
12
+ #All queries inside the block will go to awesome_shard
13
+ using(:awesome_shard) do
14
+ Foo.all
15
+ Bar.all
16
+ end
17
+ end
18
+ end
19
+
20
+ class ApplicationController < ActionController::Base
21
+ around_filter :select_shard
22
+
23
+ def select_shard(&block)
24
+ using(current_user.city) do
25
+ yield
26
+ end
27
+ end
28
+ end
29
+ </pre>
30
+ * Running migrations between shards, example:
31
+ <pre>
32
+ class MyAwesomeMigration < ActiveRecord::Migration
33
+ using :awesome_shard
34
+
35
+ def self.up
36
+ create_table :users do |t|
37
+ t.string :name
38
+ end
39
+ end
40
+
41
+ def self.down
42
+ drop_table :users
43
+ end
44
+ end
45
+ </pre>
46
+ * The sharding config will be separated from the database.yml file, given a more cleaner and nice configuration file.
47
+ * An initial generator will be integrated in to the project, to help people using octopus, this will generate the config file: db/shards.yml, and a initializer to help the configuration. The example of this configuration is on this directory.
48
+ * Like the others implementation, the sharding will be selected using a Proxy class for ActiveRecord, where the class will require the connection, and the query will be executed in the selected shard.
49
+ * After this essentials feature, I will add a task to capistrano that allows you to generate the configuration and start the replication with one line command, reading the db/shards.yml configuration and running commands on the servers.
50
+ * In replication, all slaves will be trated as shards, but you will have to specify what should be used as slave. For default, replication will balance your read queries between the slaves, and your writes queries will goes to master. if you don't want this feature, just set the load_balancing to false, and specify what queries you want to goes to slaves, and what slave. with replication, you will have methods like:
51
+ <pre>
52
+ # This sends the queries to a random shard (support just read queries, not writes)
53
+ User.using_slaves().all()
54
+ # This sends all queries to master(read/write)
55
+ User.using_master().all()
56
+ # Same thing, but all users queries will be sent to master
57
+ class User < ActiveRecord::Base
58
+ using_master()
59
+ end
60
+ # Same thing, but all read queries will be sent to slaves
61
+ class User < ActiveRecord::Base
62
+ using_slaves()
63
+ end
64
+
65
+ #or
66
+
67
+ class User < ActiveRecord::Base
68
+ using(:master)
69
+ end
70
+
71
+ #Tip: you cannot name a shard as master or slaves, they are reserved words used for replication.
72
+ </pre>
73
+
74
+ * If you have multiple slaves, the load balancing will be did using round robin algorithm, sending the queries to the databases available. (This isn't the better algorithm, but it's easy to implement and works)
@@ -0,0 +1,70 @@
1
+ h1. Masochism
2
+
3
+ p. Features:
4
+ * Support Multiple Database Support
5
+ * The config is stored in database.yml
6
+ * You could have a master and multiples slaves, but you couldn't change on the fly the shard. Ex: User.using(:awesome_shard)
7
+
8
+ p. Pros:
9
+ * Easy to use
10
+ * Test Coverage
11
+
12
+ p. Cons:
13
+ * Outdated (Lastest commit in January 12, 2009)
14
+ * Don't support running migrations on different shards.
15
+ * Don't support changing the shard on the fly
16
+
17
+
18
+ h1. DataFabric
19
+
20
+ p. Features:
21
+ * Support Multiple Database Support
22
+ * The config is stored in database.yml
23
+ * You could have data that are just sharded, not replicated.
24
+ * Support on the fly sharding selecting, with blocks
25
+
26
+ p. Pros:
27
+ * Easy to use and config
28
+ * Test Coverage
29
+ * Support just sharded, not replicated data
30
+
31
+ p. Cons:
32
+ * Don't support running migrations between shards.
33
+ * Don't support changing the sharding on the model, example: User.using(:awesome_shard)
34
+
35
+
36
+
37
+ h1. DbCharmer
38
+
39
+ p. Features:
40
+ * Support Multiple Database Support
41
+ * The config is stored in database.yml
42
+ * You could have a master and multiples slaves
43
+ * You could change the shard on the fly, with this syntax: User.switch_connection_to(:awesome_shard)
44
+ * You could run migrations over shards
45
+ * You could specify configurations of shards using ruby code
46
+
47
+
48
+ p. Pros:
49
+ * Support replication and sharding
50
+ * Support migrations between shards
51
+ * Supports on the Fly changing on the model
52
+
53
+ p. Cons:
54
+ * Didn't have test coverage in the plugin project, the tests are in another project.
55
+ * Weird and complicated syntax.
56
+ * Code are much more complicated than in the others project.
57
+
58
+
59
+
60
+ h1. DataMapper Sharding
61
+
62
+ p. Features:
63
+ * Support Multiple Database Support
64
+ * Syntax: DataMapper.setup(:external, 'mysql://someother_host/dm_core_test'); repository(:external) { Person.first }
65
+
66
+ h1. Multi-DB (http://github.com/schoefmax/multi_db)
67
+
68
+ p. Features:
69
+ * Support replication, with multiple slaves
70
+ * Load balancing between slaves
data/doc/shards.yml ADDED
@@ -0,0 +1,76 @@
1
+ # The master database is the database settend in config/database.yml
2
+ # This supports both sharding and replication, you could select the shards, and for default just replicated
3
+ # database will be used, the master database is in database.yml and the slave will only be awesome_slave.
4
+ # Setting Load balancing for default will be true, octopus will send all your read queries to slaves, and writes queries to
5
+ # master. if you just want some queries to send to slaves, set it to false, and use the sintax User.using(:slave).
6
+ production:
7
+ replicated: true
8
+
9
+ shards:
10
+ awesome_slave:
11
+ adapter: mysql
12
+ username: user
13
+ password: pass
14
+ database: awesome_slave
15
+ host: 192.321.321.21
16
+
17
+ not_slave:
18
+ adapter: mysql
19
+ username: user
20
+ password: pass
21
+ database: awesome_slave
22
+ host: 192.321.321.18
23
+
24
+ # This is another example, not replicated.
25
+ production:
26
+ shards:
27
+ us:
28
+ adapter: mysql
29
+ username: user
30
+ password: pass
31
+ database: shard1
32
+ host: 192.321.321.19
33
+ canada:
34
+ adapter: mysql
35
+ username: user
36
+ password: pass
37
+ database: shard1
38
+ host: 192.321.321.17
39
+ brazil:
40
+ adapter: mysql
41
+ username: user
42
+ password: pass
43
+ database: shard1
44
+ host: 192.321.321.90
45
+
46
+ # This is a example using a group of shards:
47
+ production:
48
+ shards:
49
+ history_shards:
50
+ aug2009:
51
+ adapter: mysql
52
+ username: user
53
+ password: pass
54
+ database: aug2009
55
+ host: 192.321.321.21
56
+ oct2009:
57
+ adapter: mysql
58
+ username: user
59
+ password: pass
60
+ database: oct2009
61
+ host: 192.321.321.18
62
+
63
+ country_shards:
64
+ brazil:
65
+ adapter: mysql
66
+ username: user
67
+ password: pass
68
+ database: brazil
69
+ host: 192.321.321.21
70
+ slave: true
71
+ canada:
72
+ adapter: mysql
73
+ username: user
74
+ password: pass
75
+ database: canada
76
+ host: 192.321.321.18