promiscuous 0.91.0 → 0.92.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.
- checksums.yaml +4 -4
- data/lib/promiscuous.rb +1 -1
- data/lib/promiscuous/amqp.rb +1 -1
- data/lib/promiscuous/amqp/fake.rb +0 -3
- data/lib/promiscuous/amqp/file.rb +81 -0
- data/lib/promiscuous/amqp/null.rb +0 -3
- data/lib/promiscuous/cli.rb +35 -25
- data/lib/promiscuous/config.rb +8 -3
- data/lib/promiscuous/error.rb +1 -2
- data/lib/promiscuous/key.rb +1 -12
- data/lib/promiscuous/mongoid.rb +7 -0
- data/lib/promiscuous/publisher/context/base.rb +4 -4
- data/lib/promiscuous/publisher/context/middleware.rb +2 -23
- data/lib/promiscuous/publisher/model/ephemeral.rb +5 -1
- data/lib/promiscuous/publisher/model/mock.rb +9 -7
- data/lib/promiscuous/publisher/model/mongoid.rb +3 -1
- data/lib/promiscuous/publisher/operation.rb +1 -1
- data/lib/promiscuous/publisher/operation/atomic.rb +44 -32
- data/lib/promiscuous/publisher/operation/base.rb +14 -9
- data/lib/promiscuous/publisher/operation/ephemeral.rb +14 -0
- data/lib/promiscuous/publisher/operation/mongoid.rb +4 -12
- data/lib/promiscuous/subscriber/message_processor/base.rb +17 -1
- data/lib/promiscuous/subscriber/message_processor/regular.rb +94 -48
- data/lib/promiscuous/subscriber/model/active_record.rb +25 -0
- data/lib/promiscuous/subscriber/model/base.rb +17 -13
- data/lib/promiscuous/subscriber/model/mongoid.rb +20 -1
- data/lib/promiscuous/subscriber/model/observer.rb +4 -0
- data/lib/promiscuous/subscriber/operation/base.rb +14 -16
- data/lib/promiscuous/subscriber/operation/bootstrap.rb +7 -1
- data/lib/promiscuous/subscriber/operation/regular.rb +6 -0
- data/lib/promiscuous/subscriber/worker.rb +6 -2
- data/lib/promiscuous/subscriber/worker/eventual_destroyer.rb +85 -0
- data/lib/promiscuous/subscriber/worker/message.rb +9 -15
- data/lib/promiscuous/subscriber/worker/message_synchronizer.rb +24 -78
- data/lib/promiscuous/subscriber/worker/runner.rb +6 -2
- data/lib/promiscuous/subscriber/worker/stats.rb +11 -7
- data/lib/promiscuous/version.rb +1 -1
- metadata +66 -63
- data/lib/promiscuous/error/already_processed.rb +0 -5
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d772f9b9786ef7ee6787030113bf9ec5934717d5
         | 
| 4 | 
            +
              data.tar.gz: 163c9cb61d4b359f1e6766effef9baccdfd8c229
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 6cacc43f226549868f963abbad29e7ba7249a1bbc1c4eb3e0b8014275935ca37e7455a73b48e36d7c0c56906308ab334c104bbf136a7cd2c7345ee976161eead
         | 
| 7 | 
            +
              data.tar.gz: 6f59e694ed2c89e2ebe9a6c73995c4b6e4987ee1f43d171f55f3783b9698c4974aff8ef684d22dd8e652956d943081b952f8bb313d492016629b9564a1447727
         | 
    
        data/lib/promiscuous.rb
    CHANGED
    
    | @@ -13,7 +13,7 @@ module Promiscuous | |
| 13 13 | 
             
              require_for 'rails',   'promiscuous/railtie'
         | 
| 14 14 | 
             
              require_for 'resque',  'promiscuous/resque'
         | 
| 15 15 | 
             
              require_for 'sidekiq', 'promiscuous/sidekiq'
         | 
| 16 | 
            -
             | 
| 16 | 
            +
              require_for 'mongoid', 'promiscuous/mongoid'
         | 
| 17 17 |  | 
| 18 18 | 
             
              extend Promiscuous::Autoload
         | 
| 19 19 | 
             
              autoload :Common, :Publisher, :Subscriber, :Observer, :Worker, :Ephemeral,
         | 
    
        data/lib/promiscuous/amqp.rb
    CHANGED
    
    
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            class Promiscuous::AMQP::File
         | 
| 2 | 
            +
              def connect
         | 
| 3 | 
            +
              end
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def disconnect
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def connected?
         | 
| 9 | 
            +
                true
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def new_connection(options={})
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def publish(options={})
         | 
| 16 | 
            +
                options[:on_confirm].try(:call)
         | 
| 17 | 
            +
                raise NotImplemented
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              module Subscriber
         | 
| 21 | 
            +
                attr_accessor :lock, :prefetch_wait, :num_pending
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def subscribe(options={}, &block)
         | 
| 24 | 
            +
                  file_name, worker_index, num_workers = Promiscuous::Config.subscriber_amqp_url.split(':')
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  worker_index = worker_index.to_i
         | 
| 27 | 
            +
                  num_workers = num_workers.to_i
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  file = File.open(file_name, 'r')
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  @prefetch = Promiscuous::Config.prefetch
         | 
| 32 | 
            +
                  @num_pending = 0
         | 
| 33 | 
            +
                  @lock = Mutex.new
         | 
| 34 | 
            +
                  @prefetch_wait = ConditionVariable.new
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  @thread = Thread.new do
         | 
| 37 | 
            +
                    file.each_with_index do |line, i|
         | 
| 38 | 
            +
                      if num_workers > 0
         | 
| 39 | 
            +
                        next if ((i+worker_index) % num_workers) != 0
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      return if @stop
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                      @lock.synchronize do
         | 
| 45 | 
            +
                        @prefetch_wait.wait(@lock) until @num_pending < @prefetch
         | 
| 46 | 
            +
                        @num_pending += 1
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      block.call(Metadata.new(self), line.chomp)
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    @lock.synchronize do
         | 
| 53 | 
            +
                      @prefetch_wait.wait(@lock) until @num_pending == 0
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    # will shutdown the CLI gracefully
         | 
| 57 | 
            +
                    Process.kill("SIGTERM", Process.pid)
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def recover
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def disconnect
         | 
| 65 | 
            +
                  @stop = true
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                class Metadata
         | 
| 69 | 
            +
                  def initialize(sub)
         | 
| 70 | 
            +
                    @sub = sub
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  def ack
         | 
| 74 | 
            +
                    @sub.lock.synchronize do
         | 
| 75 | 
            +
                      @sub.num_pending -= 1
         | 
| 76 | 
            +
                      @sub.prefetch_wait.signal
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
    
        data/lib/promiscuous/cli.rb
    CHANGED
    
    | @@ -3,22 +3,28 @@ class Promiscuous::CLI | |
| 3 3 |  | 
| 4 4 | 
             
              def trap_debug_signals
         | 
| 5 5 | 
             
                Signal.trap 'SIGUSR2' do
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
                       | 
| 11 | 
            -
             | 
| 12 | 
            -
                      print_status  | 
| 6 | 
            +
                  # Using a thread because we cannot acquire mutexes in a trap context in
         | 
| 7 | 
            +
                  # ruby 2.0
         | 
| 8 | 
            +
                  Thread.new do
         | 
| 9 | 
            +
                    Thread.list.each do |thread|
         | 
| 10 | 
            +
                      next if Thread.current == thread
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                      print_status  '----[ Threads ]----' + '-' * (100-19)
         | 
| 13 | 
            +
                      if thread.backtrace
         | 
| 14 | 
            +
                        print_status "Thread #{thread} #{thread['label']}"
         | 
| 15 | 
            +
                        print_status thread.backtrace.join("\n")
         | 
| 16 | 
            +
                      else
         | 
| 17 | 
            +
                        print_status "Thread #{thread} #{thread['label']} -- no backtrace"
         | 
| 18 | 
            +
                      end
         | 
| 13 19 | 
             
                    end
         | 
| 14 | 
            -
                  end
         | 
| 15 20 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 21 | 
            +
                    if @worker && @worker.respond_to?(:message_synchronizer)
         | 
| 22 | 
            +
                      if blocked_messages = @worker.message_synchronizer.try(:blocked_messages)
         | 
| 23 | 
            +
                        print_status  '----[ Pending Dependencies ]----' + '-' * (100-32)
         | 
| 24 | 
            +
                        blocked_messages.reverse_each { |msg| print_status msg }
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
                      print_status  '-' * 100
         | 
| 20 27 | 
             
                    end
         | 
| 21 | 
            -
                    print_status  '-' * 100
         | 
| 22 28 | 
             
                  end
         | 
| 23 29 | 
             
                end
         | 
| 24 30 | 
             
              end
         | 
| @@ -26,14 +32,18 @@ class Promiscuous::CLI | |
| 26 32 | 
             
              def trap_exit_signals
         | 
| 27 33 | 
             
                %w(SIGTERM SIGINT).each do |signal|
         | 
| 28 34 | 
             
                  Signal.trap(signal) do
         | 
| 29 | 
            -
                     | 
| 30 | 
            -
                     | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
                      @stop | 
| 34 | 
            -
             | 
| 35 | 
            -
                       | 
| 36 | 
            -
             | 
| 35 | 
            +
                    # Using a thread because we cannot acquire mutexes in a trap context in
         | 
| 36 | 
            +
                    # ruby 2.0
         | 
| 37 | 
            +
                    Thread.new do
         | 
| 38 | 
            +
                      print_status "Exiting..."
         | 
| 39 | 
            +
                      if @stop
         | 
| 40 | 
            +
                        @worker.try(:show_stop_status)
         | 
| 41 | 
            +
                      else
         | 
| 42 | 
            +
                        @stop = true
         | 
| 43 | 
            +
                        @worker.try(:stop)
         | 
| 44 | 
            +
                        @worker = nil
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end.join
         | 
| 37 47 | 
             
                  end
         | 
| 38 48 | 
             
                end
         | 
| 39 49 | 
             
              end
         | 
| @@ -144,12 +154,12 @@ class Promiscuous::CLI | |
| 144 154 | 
             
                    Promiscuous::Config.no_deps = true
         | 
| 145 155 | 
             
                  end
         | 
| 146 156 |  | 
| 147 | 
            -
                  opts.on "- | 
| 148 | 
            -
                     | 
| 157 | 
            +
                  opts.on "-x", "--ignore-exceptions", "Ignore exceptions and continue to process messages" do
         | 
| 158 | 
            +
                    Promiscuous::Config.ignore_exceptions = true
         | 
| 149 159 | 
             
                  end
         | 
| 150 160 |  | 
| 151 | 
            -
                  opts.on "- | 
| 152 | 
            -
                     | 
| 161 | 
            +
                  opts.on "-l", "--require FILE", "File to require to load your app. Don't worry about it with rails" do |file|
         | 
| 162 | 
            +
                    options[:require] = file
         | 
| 153 163 | 
             
                  end
         | 
| 154 164 |  | 
| 155 165 | 
             
                  opts.on "-p", "--prefetch [NUM]", "Number of messages to prefetch" do |prefetch|
         | 
    
        data/lib/promiscuous/config.rb
    CHANGED
    
    | @@ -3,9 +3,10 @@ module Promiscuous::Config | |
| 3 3 | 
             
                             :publisher_amqp_url, :subscriber_amqp_url, :publisher_exchange,
         | 
| 4 4 | 
             
                             :subscriber_exchanges, :queue_name, :queue_options, :redis_url,
         | 
| 5 5 | 
             
                             :redis_urls, :redis_stats_url, :stats_interval,
         | 
| 6 | 
            -
                             :socket_timeout, :heartbeat, :no_deps, :hash_size, | 
| 6 | 
            +
                             :socket_timeout, :heartbeat, :no_deps, :hash_size,
         | 
| 7 7 | 
             
                             :prefetch, :recovery_timeout, :logger, :subscriber_threads,
         | 
| 8 | 
            -
                             :version_field, :error_notifier, :recovery_on_boot
         | 
| 8 | 
            +
                             :version_field, :error_notifier, :recovery_on_boot,
         | 
| 9 | 
            +
                             :on_stats, :ignore_exceptions, :consistency, :max_retries, :generation
         | 
| 9 10 |  | 
| 10 11 | 
             
              def self.backend=(value)
         | 
| 11 12 | 
             
                @@backend = value
         | 
| @@ -53,7 +54,6 @@ module Promiscuous::Config | |
| 53 54 | 
             
                self.heartbeat            ||= 60
         | 
| 54 55 | 
             
                self.no_deps              ||= false
         | 
| 55 56 | 
             
                self.hash_size            ||= 2**20 # one million keys ~ 200Mb.
         | 
| 56 | 
            -
                self.recovery             ||= false
         | 
| 57 57 | 
             
                self.prefetch             ||= self.bootstrap ? 10000000 : 1000
         | 
| 58 58 | 
             
                self.recovery_timeout     ||= 10
         | 
| 59 59 | 
             
                self.logger               ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
         | 
| @@ -61,6 +61,11 @@ module Promiscuous::Config | |
| 61 61 | 
             
                self.error_notifier       ||= proc {}
         | 
| 62 62 | 
             
                self.version_field        ||= '_v'
         | 
| 63 63 | 
             
                self.recovery_on_boot     = true if self.recovery_on_boot.nil?
         | 
| 64 | 
            +
                self.on_stats             ||= proc { |rate, latency| }
         | 
| 65 | 
            +
                self.ignore_exceptions    ||= false
         | 
| 66 | 
            +
                self.consistency          ||= :eventual
         | 
| 67 | 
            +
                self.max_retries          ||= 10
         | 
| 68 | 
            +
                self.generation           ||= 1
         | 
| 64 69 | 
             
              end
         | 
| 65 70 |  | 
| 66 71 | 
             
              def self.configure(&block)
         | 
    
        data/lib/promiscuous/error.rb
    CHANGED
    
    | @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            module Promiscuous::Error
         | 
| 2 2 | 
             
              extend Promiscuous::Autoload
         | 
| 3 3 | 
             
              autoload :Base, :Connection, :Publisher, :Subscriber, :Recovery,
         | 
| 4 | 
            -
                       :Dependency, :MissingContext, : | 
| 5 | 
            -
                       :LockUnavailable, :LostLock
         | 
| 4 | 
            +
                       :Dependency, :MissingContext, :LockUnavailable, :LostLock
         | 
| 6 5 | 
             
            end
         | 
    
        data/lib/promiscuous/key.rb
    CHANGED
    
    | @@ -1,21 +1,10 @@ | |
| 1 1 | 
             
            class Promiscuous::Key
         | 
| 2 | 
            -
              def initialize(role, nodes=[] | 
| 2 | 
            +
              def initialize(role, nodes=[])
         | 
| 3 3 | 
             
                @role = role
         | 
| 4 4 | 
             
                @nodes = nodes
         | 
| 5 | 
            -
                @no_join = no_join
         | 
| 6 5 | 
             
              end
         | 
| 7 6 |  | 
| 8 7 | 
             
              def join(*nodes)
         | 
| 9 | 
            -
                # --- backward compatiblity code ---
         | 
| 10 | 
            -
                # TODO remove code
         | 
| 11 | 
            -
                if nodes == ['global', nil, nil]
         | 
| 12 | 
            -
                  return self.class.new(@role, @nodes + nodes, :no_join)
         | 
| 13 | 
            -
                end
         | 
| 14 | 
            -
                if @no_join
         | 
| 15 | 
            -
                  return self.class.new(@role, @nodes)
         | 
| 16 | 
            -
                end
         | 
| 17 | 
            -
                # --- backward compatiblity code ---
         | 
| 18 | 
            -
             | 
| 19 8 | 
             
                self.class.new(@role, @nodes + nodes)
         | 
| 20 9 | 
             
              end
         | 
| 21 10 |  | 
| @@ -24,11 +24,11 @@ class Promiscuous::Publisher::Context::Base | |
| 24 24 | 
             
                end
         | 
| 25 25 | 
             
              end
         | 
| 26 26 |  | 
| 27 | 
            -
              attr_accessor :name, :read_operations, :extra_dependencies, : | 
| 27 | 
            +
              attr_accessor :name, :read_operations, :extra_dependencies, :current_user
         | 
| 28 28 |  | 
| 29 | 
            -
              def initialize( | 
| 30 | 
            -
                @name =  | 
| 31 | 
            -
                @ | 
| 29 | 
            +
              def initialize(name=nil, options={})
         | 
| 30 | 
            +
                @name = name.try(:to_s) || 'anonymous'
         | 
| 31 | 
            +
                @current_user = options[:current_user]
         | 
| 32 32 | 
             
                @read_operations = []
         | 
| 33 33 | 
             
                @extra_dependencies = []
         | 
| 34 34 | 
             
                @transaction_managers = {}
         | 
| @@ -4,12 +4,8 @@ class Promiscuous::Publisher::Context::Middleware < Promiscuous::Publisher::Cont | |
| 4 4 |  | 
| 5 5 | 
             
                def process_action(*args)
         | 
| 6 6 | 
             
                  full_name = "#{self.class.controller_path}/#{self.action_name}"
         | 
| 7 | 
            -
                   | 
| 8 | 
            -
                  Promiscuous::Publisher::Context::Middleware.with_context(full_name,  | 
| 9 | 
            -
                end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                def render(*args)
         | 
| 12 | 
            -
                  Promiscuous::Publisher::Context::Middleware.without_context { super }
         | 
| 7 | 
            +
                  current_user = self.current_user if self.respond_to?(:current_user)
         | 
| 8 | 
            +
                  Promiscuous::Publisher::Context::Middleware.with_context(full_name, :current_user => current_user) { super }
         | 
| 13 9 | 
             
                end
         | 
| 14 10 | 
             
              end
         | 
| 15 11 |  | 
| @@ -26,23 +22,6 @@ class Promiscuous::Publisher::Context::Middleware < Promiscuous::Publisher::Cont | |
| 26 22 | 
             
                Promiscuous.disabled = old_disabled
         | 
| 27 23 | 
             
              end
         | 
| 28 24 |  | 
| 29 | 
            -
              def self.without_context
         | 
| 30 | 
            -
                # This is different from the method without_promiscuous in convenience.rb
         | 
| 31 | 
            -
                # That's used for render() and things that are *not* supposed to write.
         | 
| 32 | 
            -
                # We actually force promiscuous to instrument queries, and make sure that
         | 
| 33 | 
            -
                # we don't do any writes we shouldn't.
         | 
| 34 | 
            -
                old_disabled, Promiscuous.disabled = Promiscuous.disabled?, false
         | 
| 35 | 
            -
                old_current, self.current = self.current, nil
         | 
| 36 | 
            -
                yield
         | 
| 37 | 
            -
              rescue Exception => e
         | 
| 38 | 
            -
                $promiscuous_last_exception = e if e.is_a? Promiscuous::Error::Base
         | 
| 39 | 
            -
                pretty_print_exception(e) unless e.is_a? ActionView::MissingTemplate
         | 
| 40 | 
            -
                raise e
         | 
| 41 | 
            -
              ensure
         | 
| 42 | 
            -
                self.current = old_current
         | 
| 43 | 
            -
                Promiscuous.disabled = old_disabled
         | 
| 44 | 
            -
              end
         | 
| 45 | 
            -
             | 
| 46 25 | 
             
              def self.pretty_print_exception(e)
         | 
| 47 26 | 
             
                return if $promiscuous_pretty_print_exception_once == :disable || ENV['RAILS_ENV'] == 'production'
         | 
| 48 27 | 
             
                return if e.is_a?(SystemExit)
         | 
| @@ -33,13 +33,17 @@ module Promiscuous::Publisher::Model::Ephemeral | |
| 33 33 | 
             
                operation = :update  unless self.new_record
         | 
| 34 34 | 
             
                operation = :destroy if     self.destroyed
         | 
| 35 35 |  | 
| 36 | 
            -
                 | 
| 36 | 
            +
                save_operation(operation)
         | 
| 37 37 |  | 
| 38 38 | 
             
                self.new_record = false
         | 
| 39 39 | 
             
                true
         | 
| 40 40 | 
             
              end
         | 
| 41 41 | 
             
              alias :save! :save
         | 
| 42 42 |  | 
| 43 | 
            +
              def save_operation(operation)
         | 
| 44 | 
            +
                Promiscuous::Publisher::Operation::Ephemeral.new(:instance => self, :operation => operation).execute
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 43 47 | 
             
              def update_attributes(attrs)
         | 
| 44 48 | 
             
                attrs.each { |attr, value| __send__("#{attr}=", value) }
         | 
| 45 49 | 
             
                save
         | 
| @@ -22,15 +22,17 @@ module Promiscuous::Publisher::Model::Mock | |
| 22 22 | 
             
                end
         | 
| 23 23 | 
             
              end
         | 
| 24 24 |  | 
| 25 | 
            -
               | 
| 26 | 
            -
                 | 
| 27 | 
            -
                include Promiscuous::Publisher::Model::Ephemeral::PromiscuousMethodsEphemeral
         | 
| 25 | 
            +
              def save_operation(operation)
         | 
| 26 | 
            +
                payload = nil
         | 
| 28 27 |  | 
| 29 | 
            -
                 | 
| 30 | 
            -
                   | 
| 31 | 
            -
                   | 
| 32 | 
            -
                   | 
| 28 | 
            +
                Promiscuous::Publisher::Context::Middleware.with_context("mocking #{self.class}") do
         | 
| 29 | 
            +
                  op = Promiscuous::Publisher::Operation::Ephemeral.new(:instance => self, :operation => operation)
         | 
| 30 | 
            +
                  # TODO FIX the mocks to populate app name, also we need to hook before the
         | 
| 31 | 
            +
                  # json dump.
         | 
| 32 | 
            +
                  payload = op.generate_payload
         | 
| 33 33 | 
             
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                Promiscuous::Subscriber::Worker::Message.new(payload).process
         | 
| 34 36 | 
             
              end
         | 
| 35 37 |  | 
| 36 38 | 
             
              module ClassMethods
         | 
| @@ -23,7 +23,9 @@ module Promiscuous::Publisher::Model::Mongoid | |
| 23 23 |  | 
| 24 24 | 
             
                def sync(options={}, &block)
         | 
| 25 25 | 
             
                  raise "Use promiscuous.sync on the parent instance" if @instance.embedded?
         | 
| 26 | 
            -
             | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # We can use the ephemeral because both are mongoid and ephemerals are atomic operations.
         | 
| 28 | 
            +
                  Promiscuous::Publisher::Operation::Ephemeral.new(:instance => @instance, :operation => :update).execute
         | 
| 27 29 | 
             
                end
         | 
| 28 30 |  | 
| 29 31 | 
             
                def attribute(attr)
         | 
| @@ -35,10 +35,47 @@ class Promiscuous::Publisher::Operation::Atomic < Promiscuous::Publisher::Operat | |
| 35 35 | 
             
                end
         | 
| 36 36 | 
             
              end
         | 
| 37 37 |  | 
| 38 | 
            -
              def  | 
| 39 | 
            -
                 | 
| 38 | 
            +
              def do_database_query(query)
         | 
| 39 | 
            +
                case operation
         | 
| 40 | 
            +
                when :create
         | 
| 41 | 
            +
                  # We don't stash the version in the document as we can't have races
         | 
| 42 | 
            +
                  # on the same document.
         | 
| 43 | 
            +
                when :update
         | 
| 44 | 
            +
                  increment_version_in_document
         | 
| 45 | 
            +
                  # We are now in the possession of an instance that matches the original
         | 
| 46 | 
            +
                  # selector. We need to make sure the db query will operate on it,
         | 
| 47 | 
            +
                  # instead of the original selector.
         | 
| 48 | 
            +
                  use_id_selector(:use_atomic_version_selector => true)
         | 
| 49 | 
            +
                  # We need to use an atomic versioned selector to make sure that
         | 
| 50 | 
            +
                  # if we lose the lock for a long period of time, we don't mess up
         | 
| 51 | 
            +
                  # the record. Perhaps the operation has been recovered a while ago.
         | 
| 52 | 
            +
                when :destroy
         | 
| 53 | 
            +
                  use_id_selector
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                # The driver is responsible to set instance to the appropriate value.
         | 
| 57 | 
            +
                query.call_and_remember_result(:instrumented)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                if query.failed?
         | 
| 60 | 
            +
                  # If we get an network failure, we should retry later.
         | 
| 61 | 
            +
                  return if recoverable_failure?(query.exception)
         | 
| 62 | 
            +
                  @instance = nil
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              def yell_about_missing_instance
         | 
| 67 | 
            +
                err = "Cannot find document. Database had a dataloss?. Proceeding anyways. #{@recovery_data}"
         | 
| 68 | 
            +
                e = Promiscuous::Error::Recovery.new(err)
         | 
| 69 | 
            +
                Promiscuous.warn "[recovery] #{e}"
         | 
| 70 | 
            +
                Promiscuous::Config.error_notifier.call(e)
         | 
| 71 | 
            +
              end
         | 
| 40 72 |  | 
| 41 | 
            -
             | 
| 73 | 
            +
              def execute_instrumented(query)
         | 
| 74 | 
            +
                if recovering?
         | 
| 75 | 
            +
                  # The DB died or something. We cannot find our instance any more :(
         | 
| 76 | 
            +
                  # this is a problem, but we need to publish.
         | 
| 77 | 
            +
                  yell_about_missing_instance if @instance.nil?
         | 
| 78 | 
            +
                else
         | 
| 42 79 | 
             
                  generate_read_dependencies
         | 
| 43 80 | 
             
                  acquire_op_lock
         | 
| 44 81 |  | 
| @@ -75,32 +112,7 @@ class Promiscuous::Publisher::Operation::Atomic < Promiscuous::Publisher::Operat | |
| 75 112 | 
             
                # documents are missing on our side to be able to resend the destroy
         | 
| 76 113 | 
             
                # message.
         | 
| 77 114 |  | 
| 78 | 
            -
                 | 
| 79 | 
            -
                when :create
         | 
| 80 | 
            -
                  # We don't stash the version in the document as we can't have races
         | 
| 81 | 
            -
                  # on the same document.
         | 
| 82 | 
            -
                when :update
         | 
| 83 | 
            -
                  stash_version_in_document(@committed_write_deps.first.version)
         | 
| 84 | 
            -
                  # We are now in the possession of an instance that matches the original
         | 
| 85 | 
            -
                  # selector. We need to make sure the db query will operate on it,
         | 
| 86 | 
            -
                  # instead of the original selector.
         | 
| 87 | 
            -
                  use_id_selector(:use_atomic_version_selector => true)
         | 
| 88 | 
            -
                  # We need to use an atomic versioned selector to make sure that
         | 
| 89 | 
            -
                  # if we lose the lock for a long period of time, we don't mess up
         | 
| 90 | 
            -
                  # the record. Perhaps the operation has been recovered a while ago.
         | 
| 91 | 
            -
                when :destroy
         | 
| 92 | 
            -
                  use_id_selector
         | 
| 93 | 
            -
                end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                # The driver is responsible to set instance to the appropriate value.
         | 
| 96 | 
            -
                query.call_and_remember_result(:instrumented)
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                if query.failed?
         | 
| 99 | 
            -
                  # If we get an network failure, we should retry later.
         | 
| 100 | 
            -
                  return if recoverable_failure?(query.exception)
         | 
| 101 | 
            -
                  @instance = nil
         | 
| 102 | 
            -
                end
         | 
| 103 | 
            -
             | 
| 115 | 
            +
                do_database_query(query) unless @instance.nil?
         | 
| 104 116 | 
             
                # We take a timestamp right after the write is performed because latency
         | 
| 105 117 | 
             
                # measurements are performed on the subscriber.
         | 
| 106 118 | 
             
                record_timestamp
         | 
| @@ -141,6 +153,7 @@ class Promiscuous::Publisher::Operation::Atomic < Promiscuous::Publisher::Operat | |
| 141 153 | 
             
              def fetch_instance
         | 
| 142 154 | 
             
                # This method is overridden to use the original query selector.
         | 
| 143 155 | 
             
                # Should return nil if the instance is not found.
         | 
| 156 | 
            +
                @instance.reload if @instance.respond_to?(:reload)
         | 
| 144 157 | 
             
                @instance
         | 
| 145 158 | 
             
              end
         | 
| 146 159 |  | 
| @@ -148,9 +161,8 @@ class Promiscuous::Publisher::Operation::Atomic < Promiscuous::Publisher::Operat | |
| 148 161 | 
             
                @instance = fetch_instance
         | 
| 149 162 | 
             
              end
         | 
| 150 163 |  | 
| 151 | 
            -
              def  | 
| 152 | 
            -
                # Overridden to  | 
| 153 | 
            -
                # instance[Promiscuous::Config.version_field] = version
         | 
| 164 | 
            +
              def increment_version_in_document
         | 
| 165 | 
            +
                # Overridden to increment version field in the query
         | 
| 154 166 | 
             
              end
         | 
| 155 167 |  | 
| 156 168 | 
             
              def use_id_selector(options={})
         |