promiscuous 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,14 +13,18 @@ module Promiscuous
13
13
  self.connection.stop
14
14
  end
15
15
 
16
- def self.publish(msg)
17
- Promiscuous.info "[publish] #{msg[:key]} -> #{msg[:payload]}"
18
- exchange = connection.exchange('promiscuous', :type => :topic, :durable => true)
19
- exchange.publish(msg[:payload], :key => msg[:key], :persistent => true)
16
+ def self.publish(options={})
17
+ Promiscuous.info "[publish] (#{options[:exchange_name]}) #{options[:key]} -> #{options[:payload]}"
18
+ exchange(options[:exchange_name]).
19
+ publish(options[:payload], :key => options[:key], :persistent => true)
20
20
  end
21
21
 
22
- def self.subscribe(options={}, &block)
23
- raise "Cannot subscribe with bunny"
22
+ def self.open_queue(options={}, &block)
23
+ raise "Cannot open queue with bunny"
24
+ end
25
+
26
+ def self.exchange(name)
27
+ connection.exchange(name, :type => :topic, :durable => true)
24
28
  end
25
29
  end
26
30
  end
@@ -7,10 +7,10 @@ module Promiscuous
7
7
  def self.disconnect
8
8
  end
9
9
 
10
- def self.publish(msg)
10
+ def self.publish(msg, options={})
11
11
  end
12
12
 
13
- def self.subscribe(options={}, &block)
13
+ def self.open_queue(options={}, &block)
14
14
  end
15
15
  end
16
16
  end
@@ -1,7 +1,7 @@
1
1
  module Promiscuous
2
2
  module AMQP
3
3
  module RubyAMQP
4
- mattr_accessor :channel, :queue_options
4
+ mattr_accessor :channel
5
5
 
6
6
  def self.connect
7
7
  require 'amqp'
@@ -17,12 +17,11 @@ module Promiscuous
17
17
  :user => uri.user,
18
18
  :pass => uri.password,
19
19
  :vhost => uri.path.empty? ? "/" : uri.path,
20
- }
20
+ }
21
21
  end
22
22
 
23
23
  connection = ::AMQP.connect(amqp_options)
24
24
  self.channel = ::AMQP::Channel.new(connection)
25
- self.queue_options = Promiscuous::Config.queue_options
26
25
  end
27
26
 
28
27
  def self.disconnect
@@ -30,23 +29,26 @@ module Promiscuous
30
29
  self.channel = nil
31
30
  end
32
31
 
33
- def self.subscribe(options={}, &block)
32
+ def self.open_queue(options={}, &block)
34
33
  queue_name = options[:queue_name]
35
34
  bindings = options[:bindings]
36
35
 
37
- queue = self.channel.queue(queue_name, self.queue_options)
38
- exchange = channel.topic('promiscuous', :durable => true)
36
+ queue = self.channel.queue(queue_name, Promiscuous::Config.queue_options)
39
37
  bindings.each do |binding|
40
- queue.bind(exchange, :routing_key => binding)
38
+ queue.bind(exchange(options[:exchange_name]), :routing_key => binding)
41
39
  Promiscuous.info "[bind] #{queue_name} -> #{binding}"
42
40
  end
43
- queue.subscribe(:ack => true, &block)
41
+ block.call(queue) if block
44
42
  end
45
43
 
46
- def self.publish(msg)
47
- Promiscuous.info "[publish] #{msg[:key]} -> #{msg[:payload]}"
48
- exchange = channel.topic('promiscuous', :durable => true)
49
- exchange.publish(msg[:payload], :routing_key => msg[:key], :persistent => true)
44
+ def self.publish(options={})
45
+ Promiscuous.info "[publish] (#{options[:exchange_name]}) #{options[:key]} -> #{options[:payload]}"
46
+ exchange(options[:exchange_name]).
47
+ publish(options[:payload], :routing_key => options[:key], :persistent => true)
48
+ end
49
+
50
+ def self.exchange(name)
51
+ channel.topic(name, :durable => true)
50
52
  end
51
53
  end
52
54
  end
@@ -3,11 +3,13 @@ module Promiscuous::AMQP
3
3
  autoload :RubyAMQP, 'promiscuous/amqp/rubyamqp'
4
4
  autoload :Null, 'promiscuous/amqp/null'
5
5
 
6
+ EXCHANGE = 'promiscuous'.freeze
7
+
6
8
  class << self
7
9
  def backend
8
10
  Promiscuous::Config.backend
9
11
  end
10
12
 
11
- delegate :connect, :disconnect, :publish, :subscribe, :to => :backend
13
+ delegate :connect, :disconnect, :publish, :open_queue, :to => :backend
12
14
  end
13
15
  end
@@ -1,7 +1,8 @@
1
1
  module Promiscuous::Common::Worker
2
2
  extend ActiveSupport::Concern
3
3
 
4
- def initialize
4
+ def initialize(options={})
5
+ self.options = options
5
6
  self.stop = false
6
7
  end
7
8
 
@@ -13,5 +14,5 @@ module Promiscuous::Common::Worker
13
14
  end
14
15
  end
15
16
 
16
- included { attr_accessor :stop }
17
+ included { attr_accessor :stop, :options }
17
18
  end
@@ -3,7 +3,9 @@ module Promiscuous::Publisher::AMQP
3
3
  include Promiscuous::Publisher::Envelope
4
4
 
5
5
  def publish
6
- Promiscuous::AMQP.publish(:key => to, :payload => payload.to_json)
6
+ exchange_name = Promiscuous::AMQP::EXCHANGE
7
+ exchange_name += ".#{options[:personality]}" if options[:personality]
8
+ Promiscuous::AMQP.publish(:exchange_name => exchange_name, :key => to, :payload => payload.to_json)
7
9
  end
8
10
 
9
11
  def payload
@@ -36,7 +36,12 @@ module Promiscuous::Publisher::Model
36
36
  self.class.promiscuous_publisher.new(:instance => self, :operation => operation).publish
37
37
  end
38
38
  end
39
- alias :promiscuous_sync :promiscuous_publish_update
39
+
40
+ def promiscuous_sync(options={})
41
+ options = options.merge({ :instance => self, :operation => :update, :defer => false })
42
+ self.class.promiscuous_publisher.new(options).publish
43
+ true
44
+ end
40
45
  end
41
46
  end
42
47
  end
@@ -49,7 +49,7 @@ class Promiscuous::Publisher::Worker
49
49
 
50
50
  def replicate_instance(instance)
51
51
  return if self.stop
52
- instance.class.promiscuous_publisher.new(:instance => instance, :operation => :update, :defer => false).publish
52
+ instance.promiscuous_sync
53
53
  rescue Exception => e
54
54
  # TODO set back the psp field
55
55
  raise Promiscuous::Publisher::Error.new(e, instance)
@@ -1,24 +1,5 @@
1
1
  namespace :promiscuous do
2
- # Note This rake task can be loaded without Rails
3
- desc 'Run the workers'
4
- task :replicate => :environment do |t|
5
- require 'eventmachine'
6
- require 'em-synchrony'
7
-
8
- EM.synchrony do
9
- trap_signals
10
- force_backend :rubyamqp
11
-
12
- Promiscuous::Loader.load_descriptors if defined?(Rails)
13
-
14
- Promiscuous::Worker.replicate
15
-
16
- msg = "Replicating with #{Promiscuous::Subscriber::AMQP.subscribers.count} subscribers" +
17
- " and #{Promiscuous::Publisher::Mongoid::Defer.klasses.count} publishers"
18
- Promiscuous.info msg
19
- $stderr.puts msg
20
- end
21
- end
2
+ # Note These rake tasks can be loaded without Rails
22
3
 
23
4
  def trap_signals
24
5
  %w(SIGTERM SIGINT).each do |signal|
@@ -30,9 +11,77 @@ namespace :promiscuous do
30
11
  end
31
12
  end
32
13
 
14
+ def print_status(msg)
15
+ Promiscuous.info msg
16
+ $stderr.puts msg
17
+ end
18
+
33
19
  def force_backend(backend)
34
20
  Promiscuous::AMQP.disconnect
35
21
  Promiscuous::Config.backend = backend
36
22
  Promiscuous::AMQP.connect
37
23
  end
24
+
25
+ def replicate(config_options={}, &block)
26
+ require 'eventmachine'
27
+ require 'em-synchrony'
28
+
29
+ EM.synchrony do
30
+ trap_signals
31
+ Promiscuous::Loader.load_descriptors if defined?(Rails)
32
+ force_backend :rubyamqp
33
+ block.call
34
+ end
35
+ end
36
+
37
+ desc 'Run the publisher worker'
38
+ task :publish => :environment do
39
+ replicate do
40
+ Promiscuous::Worker.replicate :only => :publish
41
+ print_status "Replicating with #{Promiscuous::Publisher::Mongoid::Defer.klasses.count} publishers"
42
+ end
43
+ end
44
+
45
+ desc 'Run the subscriber worker'
46
+ task :subscribe => :environment do
47
+ replicate do
48
+ Promiscuous::Worker.replicate :only => :subscribe
49
+ print_status "Replicating with #{Promiscuous::Subscriber::AMQP.subscribers.count} subscribers"
50
+ end
51
+ end
52
+
53
+ namespace :synchronized do
54
+ desc 'Synchronize a collection'
55
+ task :publish, [:criteria] => :environment do |t, args|
56
+ raise "Usage: rake promiscuous:synchronized:publish[Model]" unless args.criteria
57
+
58
+ criteria = eval(args.criteria)
59
+ count = criteria.count
60
+ print_status "Replicating #{args.criteria}..."
61
+
62
+ bar = ProgressBar.create(:format => '%t |%b>%i| %c/%C %e',
63
+ :title => 'Publishing',
64
+ :total => count)
65
+ criteria.each do |doc|
66
+ doc.promiscuous_sync :personality => :sync
67
+ bar.increment
68
+ end
69
+
70
+ print_status "Done. You may switch your subscriber worker back to regular mode, and delete the sync queues"
71
+ end
72
+
73
+ desc 'Subscribe to a collection synchronization'
74
+ task :subscribe => :environment do |t|
75
+ replicate do
76
+ # Create the regular queue if needed, so we don't lose messages.
77
+ Promiscuous::AMQP.open_queue(Promiscuous::Subscriber::Worker.new.queue_bindings)
78
+
79
+ print_status "WARNING: --- SYNC MODE ----"
80
+ print_status "WARNING: Make sure you are not running the regular subscriber worker (it's racy)"
81
+ print_status "WARNING: --- SYNC MODE ----"
82
+ Promiscuous::Worker.replicate :personality => :sync, :only => :subscribe
83
+ print_status "Replicating with #{Promiscuous::Subscriber::AMQP.subscribers.count} subscribers"
84
+ end
85
+ end
86
+ end
38
87
  end
@@ -2,31 +2,39 @@ class Promiscuous::Subscriber::Worker
2
2
  include Promiscuous::Common::Worker
3
3
 
4
4
  def replicate
5
- Promiscuous::AMQP.subscribe(subscribe_options) do |metadata, payload|
6
- # Note: This code always runs on the root Fiber,
7
- # so ordering is always preserved
8
- begin
9
- unless self.stop
10
- Promiscuous.info "[receive] #{payload}"
11
- self.unit_of_work { Promiscuous::Subscriber.process(JSON.parse(payload)) }
12
- metadata.ack
13
- end
14
- rescue Exception => e
15
- e = Promiscuous::Subscriber::Error.new(e, payload)
5
+ Promiscuous::AMQP.open_queue(queue_bindings) do |queue|
6
+ queue.subscribe :ack => true do |metadata, payload|
7
+ # Note: This code always runs on the root Fiber,
8
+ # so ordering is always preserved
9
+ begin
10
+ unless self.stop
11
+ Promiscuous.info "[receive] #{payload}"
12
+ self.unit_of_work { Promiscuous::Subscriber.process(JSON.parse(payload)) }
13
+ metadata.ack
14
+ end
15
+ rescue Exception => e
16
+ e = Promiscuous::Subscriber::Error.new(e, payload)
16
17
 
17
- # TODO Discuss with Arjun about having an error queue.
18
- self.stop = true
19
- Promiscuous::AMQP.disconnect
20
- Promiscuous.error "[receive] FATAL #{e} #{e.backtrace.join("\n")}"
21
- Promiscuous.error "[receive] FATAL #{e}"
22
- Promiscuous::Config.error_handler.try(:call, e)
18
+ self.stop = true
19
+ Promiscuous::AMQP.disconnect
20
+ Promiscuous.error "[receive] FATAL #{e} #{e.backtrace.join("\n")}"
21
+ Promiscuous.error "[receive] FATAL #{e}"
22
+ Promiscuous::Config.error_handler.try(:call, e)
23
+ end
23
24
  end
24
25
  end
25
26
  end
26
27
 
27
- def subscribe_options
28
+ def queue_bindings
28
29
  queue_name = "#{Promiscuous::Config.app}.promiscuous"
30
+ exchange_name = Promiscuous::AMQP::EXCHANGE
31
+
32
+ if options[:personality]
33
+ queue_name += ".#{options[:personality]}"
34
+ exchange_name += ".#{options[:personality]}"
35
+ end
36
+
29
37
  bindings = Promiscuous::Subscriber::AMQP.subscribers.keys
30
- {:queue_name => queue_name, :bindings => bindings}
38
+ {:exchange_name => exchange_name, :queue_name => queue_name, :bindings => bindings}
31
39
  end
32
40
  end
@@ -1,3 +1,3 @@
1
1
  module Promiscuous
2
- VERSION = '0.12.1'
2
+ VERSION = '0.13.0'
3
3
  end
@@ -2,9 +2,12 @@ module Promiscuous::Worker
2
2
  mattr_accessor :workers
3
3
  self.workers = []
4
4
 
5
- def self.replicate
6
- self.workers << Promiscuous::Publisher::Worker.new
7
- self.workers << Promiscuous::Subscriber::Worker.new
5
+ def self.replicate(options={})
6
+ publish = options[:only].nil? || options[:only] == :publish
7
+ subscribe = options[:only].nil? || options[:only] == :subscribe
8
+
9
+ self.workers << Promiscuous::Publisher::Worker.new(options) if publish
10
+ self.workers << Promiscuous::Subscriber::Worker.new(options) if subscribe
8
11
  self.workers.each { |w| w.replicate }
9
12
  end
10
13
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: promiscuous
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.13.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-11-12 00:00:00.000000000 Z
13
+ date: 2012-11-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -92,6 +92,22 @@ dependencies:
92
92
  - - ! '>='
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: ruby-progressbar
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
95
111
  description: Replicate your Mongoid/ActiveRecord models across your applications
96
112
  email:
97
113
  - nicolas@viennot.biz
@@ -101,7 +117,6 @@ extensions: []
101
117
  extra_rdoc_files: []
102
118
  files:
103
119
  - lib/promiscuous.rb
104
- - lib/promiscuous/amqp.rb
105
120
  - lib/promiscuous/amqp/bunny.rb
106
121
  - lib/promiscuous/amqp/null.rb
107
122
  - lib/promiscuous/amqp/rubyamqp.rb
@@ -116,7 +131,6 @@ files:
116
131
  - lib/promiscuous/observer.rb
117
132
  - lib/promiscuous/publisher.rb
118
133
  - lib/promiscuous/publisher/active_record.rb
119
- - lib/promiscuous/publisher/amqp.rb
120
134
  - lib/promiscuous/publisher/base.rb
121
135
  - lib/promiscuous/publisher/class.rb
122
136
  - lib/promiscuous/publisher/envelope.rb
@@ -129,15 +143,16 @@ files:
129
143
  - lib/promiscuous/publisher/lint/class.rb
130
144
  - lib/promiscuous/publisher/lint/polymorphic.rb
131
145
  - lib/promiscuous/publisher/mock.rb
132
- - lib/promiscuous/publisher/model.rb
133
146
  - lib/promiscuous/publisher/mongoid/embedded.rb
134
147
  - lib/promiscuous/publisher/mongoid/defer.rb
135
148
  - lib/promiscuous/publisher/mongoid/defer_embedded.rb
136
149
  - lib/promiscuous/publisher/mongoid/embedded_many.rb
137
150
  - lib/promiscuous/publisher/polymorphic.rb
138
- - lib/promiscuous/publisher/worker.rb
139
151
  - lib/promiscuous/publisher/attributes.rb
140
152
  - lib/promiscuous/publisher/mongoid.rb
153
+ - lib/promiscuous/publisher/amqp.rb
154
+ - lib/promiscuous/publisher/model.rb
155
+ - lib/promiscuous/publisher/worker.rb
141
156
  - lib/promiscuous/railtie.rb
142
157
  - lib/promiscuous/railtie/replicate.rake
143
158
  - lib/promiscuous/subscriber/active_record.rb
@@ -156,14 +171,15 @@ files:
156
171
  - lib/promiscuous/subscriber/observer.rb
157
172
  - lib/promiscuous/subscriber/polymorphic.rb
158
173
  - lib/promiscuous/subscriber/upsert.rb
159
- - lib/promiscuous/subscriber/worker.rb
160
174
  - lib/promiscuous/subscriber/amqp.rb
161
175
  - lib/promiscuous/subscriber/attributes.rb
162
176
  - lib/promiscuous/subscriber/base.rb
163
177
  - lib/promiscuous/subscriber/mongoid.rb
164
- - lib/promiscuous/worker.rb
178
+ - lib/promiscuous/subscriber/worker.rb
165
179
  - lib/promiscuous/subscriber.rb
166
180
  - lib/promiscuous/ephemeral.rb
181
+ - lib/promiscuous/amqp.rb
182
+ - lib/promiscuous/worker.rb
167
183
  - lib/promiscuous/version.rb
168
184
  - README.md
169
185
  homepage: http://github.com/crowdtap/promiscuous