cassandra_migrations 0.0.1.pre3 → 0.0.1.pre4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations
4
+ module Cassandra
5
+ module KeyspaceOperations
6
+
7
+ def create_keyspace!
8
+ begin
9
+ execute(
10
+ "CREATE KEYSPACE #{Config.keyspace} \
11
+ WITH replication = { \
12
+ 'class':'#{Config.replication['class']}', \
13
+ 'replication_factor': #{Config.replication['replication_factor']} \
14
+ }"
15
+ )
16
+ use(Config.keyspace)
17
+ rescue Exception => exception
18
+ drop_keyspace!
19
+ raise exception
20
+ end
21
+ end
22
+
23
+ def drop_keyspace!
24
+ begin
25
+ execute("DROP KEYSPACE #{Config.keyspace}")
26
+ rescue Cql::QueryError
27
+ raise Errors::UnexistingKeyspaceError, keyspace
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations
4
+ module Cassandra
5
+ module Queries
6
+
7
+ def write!(table, hash)
8
+ columns = []
9
+ values = []
10
+
11
+ hash.each do |k,v|
12
+ columns << k.to_s
13
+ values << (v.is_a?(String) ? "'#{v.to_s}'" : v.to_s)
14
+ end
15
+
16
+ execute("INSERT INTO #{table} (#{columns.join(', ')}) VALUES (#{values.join(', ')})")
17
+ end
18
+
19
+ def select(table, options={})
20
+ query_string = "SELECT #{options[:projection] || '*'} FROM #{table}"
21
+
22
+ if options[:selection]
23
+ query_string << " WHERE #{options[:selection]}"
24
+ end
25
+
26
+ if options[:order_by]
27
+ query_string << " ORDER BY #{options[:order_by]}"
28
+ end
29
+
30
+ if options[:limit]
31
+ query_string << " LIMIT #{options[:limit]}"
32
+ end
33
+
34
+ execute(query_string)
35
+ end
36
+
37
+ def delete!(table, selection, options={})
38
+ execute("DELETE #{options[:projection]} FROM #{table} WHERE #{selection}")
39
+ end
40
+
41
+ def truncate!(table)
42
+ execute("TRUNCATE #{table}")
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -2,102 +2,63 @@
2
2
 
3
3
  require 'yaml'
4
4
  require 'cql'
5
- require 'cassandra_migrations/cassandra/query'
6
- require 'cassandra_migrations/cassandra/errors'
7
- require 'cassandra_migrations/cassandra/migrator'
5
+ require 'cassandra_migrations/cassandra/queries'
6
+ require 'cassandra_migrations/cassandra/keyspace_operations'
8
7
 
9
- module CassandraMigrations::Cassandra
10
- extend Query
11
-
12
- mattr_accessor :client
13
- mattr_accessor :config
8
+
9
+ module CassandraMigrations
10
+ module Cassandra
11
+ extend Queries
12
+ extend KeyspaceOperations
14
13
 
15
- def self.start!
16
- connect_to_server unless client
14
+ mattr_accessor :client
17
15
 
18
- # setup keyspace use
19
- begin
20
- use(config['keyspace'])
21
- rescue Cql::QueryError # keyspace does not exist
22
- raise Errors::UnexistingKeyspaceError, config['keyspace']
16
+ def self.start!
17
+ # setup keyspace use
18
+ use(Config.keyspace)
23
19
  end
24
- end
25
-
26
- def self.restart!
27
- self.client = nil
28
- self.config = nil
29
- start!
30
- end
31
-
32
- def self.shutdown!
33
- if client
34
- client.close
20
+
21
+ def self.restart!
22
+ raise Errors::ClientNotStartedError unless client
23
+
24
+ client.close if client && client.connected?
35
25
  self.client = nil
26
+ start!
36
27
  end
37
-
38
- self.config = nil
39
- end
40
-
41
- def self.create_keyspace!
42
- connect_to_server unless client
43
28
 
44
- begin
45
- execute(
46
- "CREATE KEYSPACE #{config['keyspace']} \
47
- WITH replication = { \
48
- 'class':'#{config['replication']['class']}', \
49
- 'replication_factor': #{config['replication']['replication_factor']} \
50
- }"
51
- )
52
- use(config['keyspace'])
53
- execute("CREATE TABLE metadata (data_name varchar PRIMARY KEY, data_value varchar)")
54
- write("metadata", {:data_name => 'version', :data_value => '0'})
55
- rescue Exception => exception
56
- drop!
57
- raise exception
29
+ def self.shutdown!
30
+ raise Errors::ClientNotStartedError unless client
31
+
32
+ client.close if client.connected?
33
+ self.client = nil
58
34
  end
59
- end
60
-
61
- def self.drop!
62
- connect_to_server unless client
63
35
 
64
- begin
65
- execute("DROP KEYSPACE #{config['keyspace']}")
66
- rescue Cql::QueryError
67
- raise Errors::UnexistingKeyspaceError, config['keyspace']
36
+ def self.use(keyspace)
37
+ connect_to_server unless client
38
+
39
+ begin
40
+ client.use(keyspace)
41
+ rescue Cql::QueryError # keyspace does not exist
42
+ raise Errors::UnexistingKeyspaceError, keyspace
43
+ end
68
44
  end
69
- end
70
-
71
- def self.use(keyspace)
72
- raise Errors::ClientNotStartedError unless client
73
- client.use(keyspace)
74
- end
75
45
 
76
- def self.execute(cql)
77
- raise Errors::ClientNotStartedError unless client
78
- client.execute(cql)
79
- end
80
-
81
- private
82
-
83
- def self.connect_to_server
84
- load_config
46
+ def self.execute(cql)
47
+ connect_to_server unless client
48
+ client.execute(cql)
49
+ end
85
50
 
86
- Rails.logger.try(:info, "Connecting to Cassandra on #{config['host']}:#{config['port']}")
51
+ private
87
52
 
88
- begin
89
- self.client = Cql::Client.new(:host => config['host'], :port => config['port'])
90
- client.connect
91
- rescue Cql::Io::ConnectionError => e
92
- raise Errors::ConnectionError, e.message
93
- end
94
- end
95
-
96
- def self.load_config
97
- begin
98
- self.config = YAML.load_file(Rails.root.join("config", "cassandra.yml"))[Rails.env]
99
- rescue Errno::ENOENT
100
- raise Errors::MissingConfigurationError
53
+ def self.connect_to_server
54
+ Rails.logger.try(:info, "Connecting to Cassandra on #{Config.host}:#{Config.port}")
55
+
56
+ begin
57
+ self.client = Cql::Client.new(:host => Config.host, :port => Config.port)
58
+ client.connect
59
+ rescue Cql::Io::ConnectionError => e
60
+ raise Errors::ConnectionError, e.message
61
+ end
101
62
  end
102
63
  end
103
- end
64
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations
4
+ module Config
5
+
6
+ mattr_accessor :config
7
+
8
+ def self.method_missing(method_sym, *arguments, &block)
9
+ load_config unless config
10
+ config[method_sym.to_s]
11
+ end
12
+
13
+ private
14
+
15
+ def self.load_config
16
+ begin
17
+ self.config = YAML.load_file(Rails.root.join("config", "cassandra.yml"))[Rails.env]
18
+ rescue Errno::ENOENT
19
+ raise Errors::MissingConfigurationError
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- module CassandraMigrations::Cassandra
3
+ module CassandraMigrations
4
4
  module Errors
5
5
 
6
6
  class CassandraError < StandardError
@@ -39,4 +39,4 @@ module CassandraMigrations::Cassandra
39
39
  end
40
40
 
41
41
  end
42
- end
42
+ end
@@ -1,9 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
- module CassandraMigrations::Cassandra
3
+ module CassandraMigrations
4
4
  module Migrator
5
5
 
6
- def self.up_to_latest
6
+ METADATA_TABLE = 'cassandra_migrations_metadata'
7
+
8
+ def self.up_to_latest!
7
9
  current_version = read_current_version
8
10
 
9
11
  new_migrations = get_all_migration_names.sort.select do |migration_name|
@@ -17,7 +19,7 @@ module CassandraMigrations::Cassandra
17
19
  new_migrations.size
18
20
  end
19
21
 
20
- def self.rollback(count=1)
22
+ def self.rollback!(count=1)
21
23
  current_version = read_current_version
22
24
 
23
25
  executed_migrations = get_all_migration_names.sort.reverse.select do |migration_name|
@@ -39,7 +41,13 @@ module CassandraMigrations::Cassandra
39
41
  end
40
42
 
41
43
  def self.read_current_version
42
- CassandraMigrations::Cassandra.select("metadata", :selection => "data_name='version'", :projection => 'data_value').first['data_value'].to_i
44
+ begin
45
+ Cassandra.select(METADATA_TABLE, :selection => "data_name='version'", :projection => 'data_value').first['data_value'].to_i
46
+ rescue Cql::QueryError # table cassandra_migrations_metadata does not exist
47
+ Cassandra.execute("CREATE TABLE #{METADATA_TABLE} (data_name varchar PRIMARY KEY, data_value varchar)")
48
+ Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => '0'})
49
+ return 0
50
+ end
43
51
  end
44
52
 
45
53
  private
@@ -51,7 +59,7 @@ private
51
59
  get_class_from_migration_name(migration_name).up
52
60
 
53
61
  # update version
54
- CassandraMigrations::Cassandra.write("metadata", {:data_name => 'version', :data_value => get_version_from_migration_name(migration_name).to_s})
62
+ Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => get_version_from_migration_name(migration_name).to_s})
55
63
  end
56
64
 
57
65
  def self.down(migration_name, previous_migration_name=nil)
@@ -62,9 +70,9 @@ private
62
70
 
63
71
  # downgrade version
64
72
  if previous_migration_name
65
- CassandraMigrations::Cassandra.write("metadata", {:data_name => 'version', :data_value => get_version_from_migration_name(previous_migration_name).to_s})
73
+ Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => get_version_from_migration_name(previous_migration_name).to_s})
66
74
  else
67
- CassandraMigrations::Cassandra.write("metadata", {:data_name => 'version', :data_value => '0'})
75
+ Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => '0'})
68
76
  end
69
77
  end
70
78
 
@@ -80,4 +88,4 @@ private
80
88
  migration_name.match(/([0-9]{14})_.+\.rb$/).captures.first.to_i
81
89
  end
82
90
  end
83
- end
91
+ end
@@ -1,17 +1,30 @@
1
1
  # encoding : utf-8
2
2
 
3
- # Em produção (como usamos o Passenger), vários processos ruby são criados através do fork
4
- # do original. No UNIX, quando um fork é realizado, is file descriptors (arquivos, conexões a base de dados,
5
- # sockets, e etc) são copiados em estado aberto para o processo filho. Se não reabrirmos a conexão com o cassandra
6
- # no processo filho, quando ela for ser usada ela estará 'locked' pelo processo pai, o que resultará em deadlock.
3
+ # In production (when Passenger is used with smart spawn), many ruby processes are created
4
+ # by forking the original spawner. Since the child process is a different process, it shares
5
+ # no memory with its father. Because of that we have to connect to cassandra again.
6
+ # It is also common that file descriptors are unintentionally shared when the process forks,
7
+ # that's why we go through the safe path and check if the client exists in order to restart it
8
+ # (production tests have shown that the client is nil when forked)
7
9
 
8
- # Mais explicações em: http://www.modrails.com/documentation/Users%20guide%20Apache.html#spawning_methods_explained
9
- if defined?(PhusionPassenger)
10
- PhusionPassenger.on_event(:starting_worker_process) do |forked|
11
- if forked
12
- CassandraMigrations::Cassandra.restart!
10
+ # More explanations in: http://www.modrails.com/documentation/Users%20guide%20Apache.html#spawning_methods_explained
11
+
12
+ module CassandraMigrations
13
+
14
+ if defined?(PhusionPassenger)
15
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
16
+ if forked
17
+ if Cassandra.client
18
+ Rails.logger.info "Passenger process forked: reconnecting to Cassandra..."
19
+ Cassandra.restart!
20
+ else
21
+ Rails.logger.info "Passenger process forked: connecting to Cassandra..."
22
+ Cassandra.start!
23
+ end
24
+ end
13
25
  end
26
+ else
27
+ Cassandra.start!
14
28
  end
15
- else
16
- CassandraMigrations::Cassandra.start!
29
+
17
30
  end
@@ -10,26 +10,26 @@ namespace :cassandra do
10
10
  task :create do
11
11
  begin
12
12
  CassandraMigrations::Cassandra.start!
13
- puts "Keyspace #{CassandraMigrations::Cassandra.config['keyspace']} already exists!"
14
- rescue CassandraMigrations::Cassandra::Errors::UnexistingKeyspaceError
13
+ puts "Keyspace #{CassandraMigrations::Config.keyspace} already exists!"
14
+ rescue CassandraMigrations::Errors::UnexistingKeyspaceError
15
15
  CassandraMigrations::Cassandra.create_keyspace!
16
- puts "Created keyspace #{CassandraMigrations::Cassandra.config['keyspace']}"
16
+ puts "Created keyspace #{CassandraMigrations::Config.keyspace}"
17
17
  end
18
18
  end
19
19
 
20
20
  desc 'Drop keyspace in config/cassandra.yml for the current environment'
21
21
  task :drop do
22
22
  begin
23
- CassandraMigrations::Cassandra.drop!
24
- puts "Dropped keyspace #{CassandraMigrations::Cassandra.config['keyspace']}"
25
- rescue CassandraMigrations::Cassandra::Errors::UnexistingKeyspaceError
26
- puts "Keyspace #{CassandraMigrations::Cassandra.config['keyspace']} does not exist already... cannot be dropped"
23
+ CassandraMigrations::Cassandra.drop_keyspace!
24
+ puts "Dropped keyspace #{CassandraMigrations::Config.keyspace}"
25
+ rescue CassandraMigrations::Errors::UnexistingKeyspaceError
26
+ puts "Keyspace #{CassandraMigrations::Config.keyspace} does not exist... cannot be dropped"
27
27
  end
28
28
  end
29
29
 
30
30
  desc 'Migrate the keyspace to the latest version'
31
31
  task :migrate => :start do
32
- migrations_up_count = CassandraMigrations::Cassandra::Migrator.up_to_latest
32
+ migrations_up_count = CassandraMigrations::Migrator.up_to_latest!
33
33
 
34
34
  if migrations_up_count == 0
35
35
  puts "Already up-to-date"
@@ -40,9 +40,9 @@ namespace :cassandra do
40
40
 
41
41
  desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n)'
42
42
  task :rollback => :start do
43
- steps = ENV['STEP'] ? ENV['STEP'].to_i : 1
43
+ steps = (ENV['STEP'] ? ENV['STEP'].to_i : 1)
44
44
 
45
- migrations_down_count = CassandraMigrations::Cassandra::Migrator.rollback(steps)
45
+ migrations_down_count = CassandraMigrations::Migrator.rollback!(steps)
46
46
 
47
47
  if steps == migrations_down_count
48
48
  puts "Rolled back #{steps} version(s)."
@@ -68,7 +68,7 @@ namespace :cassandra do
68
68
 
69
69
  desc 'Retrieves the current schema version number'
70
70
  task :version => :start do
71
- puts "Current version: #{Cassandra::Migrator.read_current_version}"
71
+ puts "Current version: #{CassandraMigrations::Migrator.read_current_version}"
72
72
  end
73
73
 
74
74
  end
@@ -1,6 +1,6 @@
1
-
2
- module CassandraMigrations
3
- end
4
-
1
+ require 'cassandra_migrations/config'
2
+ require 'cassandra_migrations/errors'
5
3
  require 'cassandra_migrations/cassandra'
4
+ require 'cassandra_migrations/migrator'
5
+
6
6
  require 'cassandra_migrations/railtie' if defined?(Rails)
@@ -14,7 +14,7 @@ describe CassandraMigrations::Cassandra do
14
14
  end
15
15
 
16
16
  after do
17
- CassandraMigrations::Cassandra.shutdown!
17
+ CassandraMigrations::Cassandra.shutdown! if CassandraMigrations::Cassandra.client
18
18
  end
19
19
 
20
20
  describe ".start!" do
@@ -45,18 +45,16 @@ describe CassandraMigrations::Cassandra do
45
45
 
46
46
  it "should use host and port configurations to create cassandra client" do
47
47
  cql_client_mock = Cql::Client.new
48
- cql_client_mock.should_receive(:start!)
48
+ cql_client_mock.should_receive(:connect)
49
+ cql_client_mock.stub(:use)
49
50
  Cql::Client.should_receive(:new).with(:host => '127.0.0.1', :port => 9042).and_return(cql_client_mock)
50
51
 
51
- begin
52
- CassandraMigrations::Cassandra.start!
53
- rescue
54
- end
52
+ CassandraMigrations::Cassandra.start!
55
53
  end
56
54
 
57
55
  it "should raise exception if not able to connect to cassandra host" do
58
56
  cql_client_mock = Cql::Client.new
59
- cql_client_mock.stub(:start!).and_raise Cql::Io::ConnectionError
57
+ cql_client_mock.stub(:connect).and_raise Cql::Io::ConnectionError
60
58
  Cql::Client.stub(:new).and_return cql_client_mock
61
59
 
62
60
  expect do
@@ -68,7 +66,9 @@ describe CassandraMigrations::Cassandra do
68
66
  CassandraMigrations::Cassandra.should_receive(:use).with('cassandra_migrations_development')
69
67
  CassandraMigrations::Cassandra.start!
70
68
  end
71
-
69
+ end
70
+
71
+ describe '.use' do
72
72
  it "should raise exception if configured keyspace does not exist" do
73
73
  expect do
74
74
  CassandraMigrations::Cassandra.start!
@@ -85,4 +85,4 @@ describe CassandraMigrations::Cassandra do
85
85
  end
86
86
  end
87
87
 
88
- end
88
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cassandra_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre3
4
+ version: 0.0.1.pre4
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.0.0.pre5
21
+ version: 1.0.0.pre7
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: 1.0.0.pre5
29
+ version: 1.0.0.pre7
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rake
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -99,17 +99,20 @@ extensions: []
99
99
  extra_rdoc_files: []
100
100
  files:
101
101
  - lib/cassandra_migrations.rb
102
- - lib/cassandra_migrations/cassandra/migrator.rb
103
- - lib/cassandra_migrations/cassandra/query.rb
104
- - lib/cassandra_migrations/cassandra/errors.rb
105
- - lib/cassandra_migrations/railtie/tasks.rake
106
- - lib/cassandra_migrations/railtie/initializer.rb
102
+ - lib/cassandra_migrations/migrator.rb
107
103
  - lib/cassandra_migrations/cassandra.rb
108
104
  - lib/cassandra_migrations/railtie.rb
109
- - spec/cassandra_migrations/cassandra_spec.rb
105
+ - lib/cassandra_migrations/cassandra/keyspace_operations.rb
106
+ - lib/cassandra_migrations/cassandra/queries.rb
107
+ - lib/cassandra_migrations/config.rb
108
+ - lib/cassandra_migrations/railtie/initializer.rb
109
+ - lib/cassandra_migrations/railtie/tasks.rake
110
+ - lib/cassandra_migrations/errors.rb
110
111
  - spec/cassandra_migrations_spec.rb
112
+ - spec/cassandra_migrations/cassandra_spec.rb
111
113
  homepage: https://github.com/hsgubert/cassandra_migrations
112
- licenses: []
114
+ licenses:
115
+ - MIT
113
116
  post_install_message:
114
117
  rdoc_options: []
115
118
  require_paths:
@@ -133,5 +136,5 @@ signing_key:
133
136
  specification_version: 3
134
137
  summary: Cassandra schema management for a multi-environment developer.
135
138
  test_files:
136
- - spec/cassandra_migrations/cassandra_spec.rb
137
139
  - spec/cassandra_migrations_spec.rb
140
+ - spec/cassandra_migrations/cassandra_spec.rb
@@ -1,44 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module CassandraMigrations::Cassandra
4
- module Query
5
-
6
- def write(table, hash)
7
- columns = []
8
- values = []
9
-
10
- hash.each do |k,v|
11
- columns << k.to_s
12
- values << "'#{v.to_s}'"
13
- end
14
-
15
- execute("INSERT INTO #{table} (#{columns.join(', ')}) VALUES (#{values.join(', ')})")
16
- end
17
-
18
- def select(table, options={})
19
- query_string = "SELECT #{options[:projection] || '*'} FROM #{table}"
20
-
21
- if options[:selection]
22
- query_string << " WHERE #{options[:selection]}"
23
- end
24
-
25
- if options[:order_by]
26
- query_string << " ORDER BY #{options[:order_by]}"
27
- end
28
-
29
- if options[:limit]
30
- query_string << " LIMIT #{options[:limit]}"
31
- end
32
-
33
- execute(query_string)
34
- end
35
-
36
- def delete(table, selection, options={})
37
- execute("DELETE #{options[:projection]} FROM #{table} WHERE #{selection}")
38
- end
39
-
40
- def truncate(table)
41
- execute("TRUNCATE #{table}")
42
- end
43
- end
44
- end