promiscuous 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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