promiscuous 0.100.5 → 1.0.0.beta1

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/promiscuous.rb +5 -1
  3. data/lib/promiscuous/config.rb +6 -5
  4. data/lib/promiscuous/dsl.rb +0 -4
  5. data/lib/promiscuous/loader.rb +0 -5
  6. data/lib/promiscuous/mongoid.rb +15 -5
  7. data/lib/promiscuous/publisher.rb +1 -1
  8. data/lib/promiscuous/publisher/model/active_record.rb +6 -1
  9. data/lib/promiscuous/publisher/model/base.rb +8 -11
  10. data/lib/promiscuous/publisher/model/mock.rb +2 -2
  11. data/lib/promiscuous/publisher/model/mongoid.rb +3 -4
  12. data/lib/promiscuous/publisher/operation/active_record.rb +13 -69
  13. data/lib/promiscuous/publisher/operation/atomic.rb +15 -158
  14. data/lib/promiscuous/publisher/operation/base.rb +13 -381
  15. data/lib/promiscuous/publisher/operation/ephemeral.rb +12 -8
  16. data/lib/promiscuous/publisher/operation/mongoid.rb +22 -92
  17. data/lib/promiscuous/publisher/operation/non_persistent.rb +0 -9
  18. data/lib/promiscuous/publisher/operation/proxy_for_query.rb +8 -6
  19. data/lib/promiscuous/publisher/operation/transaction.rb +4 -56
  20. data/lib/promiscuous/publisher/transport.rb +14 -0
  21. data/lib/promiscuous/publisher/transport/batch.rb +138 -0
  22. data/lib/promiscuous/publisher/transport/persistence.rb +14 -0
  23. data/lib/promiscuous/publisher/transport/persistence/active_record.rb +33 -0
  24. data/lib/promiscuous/publisher/transport/persistence/mongoid.rb +22 -0
  25. data/lib/promiscuous/publisher/transport/worker.rb +36 -0
  26. data/lib/promiscuous/publisher/worker.rb +3 -12
  27. data/lib/promiscuous/redis.rb +5 -0
  28. data/lib/promiscuous/subscriber/message.rb +1 -29
  29. data/lib/promiscuous/subscriber/model/base.rb +3 -2
  30. data/lib/promiscuous/subscriber/model/mongoid.rb +16 -1
  31. data/lib/promiscuous/subscriber/model/observer.rb +0 -1
  32. data/lib/promiscuous/subscriber/operation.rb +9 -3
  33. data/lib/promiscuous/subscriber/unit_of_work.rb +7 -7
  34. data/lib/promiscuous/subscriber/worker/eventual_destroyer.rb +1 -1
  35. data/lib/promiscuous/version.rb +1 -1
  36. metadata +39 -35
  37. data/lib/promiscuous/dependency.rb +0 -78
  38. data/lib/promiscuous/error/dependency.rb +0 -116
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36eb13f57a962d958bcd78894b1a1991e1ee5a63
4
- data.tar.gz: ed7e75b9a906ca60da6d9e28d48c93ded541308c
3
+ metadata.gz: 9049ba8a421c1866cee586689ed5927acebc1663
4
+ data.tar.gz: 7dcb69b1cc9e47e3878fd6cd65c55481183069df
5
5
  SHA512:
6
- metadata.gz: 592f65e706d0ef1cfbf3a4bbc36a7494847003efa7e1fae07ec6bf2d1bd33e6db454e0e982843b77e1e660397187f25c25f4e4b4e5b03e81c5e78394e6db78bf
7
- data.tar.gz: 1842b7b3e6627868ee1312f123a1007342b8ac241a05368f77ab2c9bf67dcc4373daaef2b5f7ec1f9ea0ef56dabcf78ff4bff42415c1dfb25f8672b03b11d35e
6
+ metadata.gz: f0f7e2f082d0684b9c0e8ce7bb6b58a38eb1ec40d2108ab839c295384707e60e704a681ac015702c7ad26ffe6ff40879d38877c86e15512a9b872dc7aae5646e
7
+ data.tar.gz: a3f0fe71f03c4acca822c6a71164b4b88fd1381b0cd83309ff94a703e6cf60aece9e022e8e6b58083296359a4d81c26ba7da7a5a69eda92ca4fa0fb99330d364
data/lib/promiscuous.rb CHANGED
@@ -4,8 +4,12 @@ require 'multi_json'
4
4
 
5
5
  module Promiscuous
6
6
  def self.require_for(gem, file)
7
+ only_for(gem) { require file }
8
+ end
9
+
10
+ def self.only_for(gem, &block)
7
11
  require gem
8
- require file
12
+ block.call
9
13
  rescue LoadError
10
14
  end
11
15
 
@@ -4,8 +4,8 @@ module Promiscuous::Config
4
4
  :subscriber_exchanges, :queue_name, :queue_options, :redis_url,
5
5
  :redis_urls, :redis_stats_url, :stats_interval,
6
6
  :socket_timeout, :heartbeat, :hash_size,
7
- :prefetch, :recovery_timeout, :logger, :subscriber_threads,
8
- :version_field, :error_notifier, :recovery_on_boot,
7
+ :prefetch, :recovery_timeout, :recovery_interval, :logger, :subscriber_threads,
8
+ :version_field, :error_notifier, :transport_collection,
9
9
  :on_stats, :max_retries, :generation, :destroy_timeout, :destroy_check_interval
10
10
 
11
11
  def self.backend=(value)
@@ -52,15 +52,16 @@ module Promiscuous::Config
52
52
  self.heartbeat ||= 60
53
53
  self.hash_size ||= 2**20 # one million keys ~ 200Mb.
54
54
  self.prefetch ||= 1000
55
- self.recovery_timeout ||= 10
55
+ self.recovery_timeout ||= 10.seconds
56
+ self.recovery_interval ||= 5.seconds
56
57
  self.logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
57
58
  self.subscriber_threads ||= 10
58
59
  self.error_notifier ||= proc {}
59
60
  self.version_field ||= '_v'
60
- self.recovery_on_boot = true if self.recovery_on_boot.nil?
61
+ self.transport_collection ||= '_promiscuous'
61
62
  self.on_stats ||= proc { |rate, latency| }
62
63
  self.max_retries ||= defined?(Rails) ? Rails.env.production? ? 10 : 0 : 10
63
- self.generation ||= 1
64
+ self.generation ||= 0
64
65
  self.destroy_timeout ||= 1.hour
65
66
  self.destroy_check_interval ||= 10.minutes
66
67
  end
@@ -27,10 +27,6 @@ module Promiscuous::DSL
27
27
  @model_class.__send__(@mode, *fields, @options.merge(options))
28
28
  end
29
29
 
30
- def track_dependencies_of(field)
31
- @model_class.track_dependencies_of(field)
32
- end
33
-
34
30
  alias attribute attributes
35
31
  end
36
32
  end
@@ -6,11 +6,6 @@ module Promiscuous::Loader
6
6
  file = defined?(Rails) ? Rails.root.join(file_name) : File.join('.', file_name)
7
7
  load file if File.exists?(file)
8
8
  end
9
-
10
- # A one shot recovery on boot
11
- if Promiscuous::Config.recovery_on_boot
12
- Promiscuous::Publisher::Worker.new.try_recover
13
- end
14
9
  end
15
10
 
16
11
  def self.cleanup
@@ -1,7 +1,17 @@
1
- class Moped::BSON::ObjectId
2
- # No {"$oid": "123"}, it's horrible.
3
- # TODO Document this shit.
4
- def to_json(*args)
5
- "\"#{to_s}\""
1
+ if defined? Moped::BSON
2
+ class Moped::BSON::ObjectId
3
+ # No {"$oid": "123"}, it's horrible.
4
+ # TODO Document this shit.
5
+ def to_json(*args)
6
+ "\"#{to_s}\""
7
+ end
8
+ end
9
+ else
10
+ module BSON
11
+ class ObjectId
12
+ def as_json(options = {})
13
+ to_s
14
+ end
15
+ end
6
16
  end
7
17
  end
@@ -1,6 +1,6 @@
1
1
  module Promiscuous::Publisher
2
2
  extend Promiscuous::Autoload
3
- autoload :Model, :Operation, :MockGenerator, :Context, :Worker, :Bootstrap
3
+ autoload :Model, :Operation, :MockGenerator, :Context, :Worker, :Bootstrap, :Transport
4
4
 
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -11,7 +11,12 @@ module Promiscuous::Publisher::Model::ActiveRecord
11
11
 
12
12
  def belongs_to(*args, &block)
13
13
  super.tap do |association|
14
- publish(association.foreign_key) if self.in_publish_block?
14
+ fk = if association.is_a?(Hash)
15
+ association.values.first.foreign_key # ActiveRecord 4x
16
+ else
17
+ association.foreign_key # ActiveRecord 3x
18
+ end
19
+ publish(fk) if self.in_publish_block?
15
20
  end
16
21
  end
17
22
  end
@@ -36,14 +36,17 @@ module Promiscuous::Publisher::Model::Base
36
36
  value
37
37
  end
38
38
 
39
- def get_dependency
40
- @collection ||= @instance.class.promiscuous_collection_name
41
- Promiscuous::Dependency.new(@collection, :id, id)
42
- end
43
-
44
39
  def id
45
40
  @instance.id
46
41
  end
42
+
43
+ def sync(options={}, &block)
44
+ raise "Model cannot be dirty (have changes) when syncing" if @instance.changed?
45
+
46
+ # We can use the ephemeral because both are mongoid and ephemerals are atomic operations.
47
+ Promiscuous::Publisher::Operation::Ephemeral.new(:instance => @instance, :operation => :update).execute
48
+ end
49
+
47
50
  end
48
51
 
49
52
  class PromiscuousMethods
@@ -55,12 +58,6 @@ module Promiscuous::Publisher::Model::Base
55
58
  @promiscuous ||= self.class.const_get(:PromiscuousMethods).new(self)
56
59
  end
57
60
 
58
- def valid?(*args)
59
- # Validations are not dependencies
60
- # TODO we should yell if the user is trying to write
61
- without_promiscuous { super }
62
- end
63
-
64
61
  module ClassMethods
65
62
  # all methods are virtual
66
63
 
@@ -28,9 +28,9 @@ module Promiscuous::Publisher::Model::Mock
28
28
  op = Promiscuous::Publisher::Operation::Ephemeral.new(:instance => self, :operation => operation)
29
29
  # TODO FIX the mocks to populate app name, also we need to hook before the
30
30
  # json dump.
31
- payload = op.generate_payload
31
+ batch = op.create_transport_batch([op])
32
32
 
33
- Promiscuous::Subscriber::Message.new(payload).process
33
+ Promiscuous::Subscriber::Message.new(batch.payload).process
34
34
  end
35
35
 
36
36
  module ClassMethods
@@ -15,6 +15,8 @@ module Promiscuous::Publisher::Model::Mongoid
15
15
  raise "Please include Promiscuous::Publisher in the root class of #{self}"
16
16
  end
17
17
 
18
+ field Promiscuous::Config.version_field
19
+
18
20
  Promiscuous::Publisher::Model::Mongoid.collection_mapping[self.collection.name] = self
19
21
  end
20
22
 
@@ -23,9 +25,7 @@ module Promiscuous::Publisher::Model::Mongoid
23
25
 
24
26
  def sync(options={}, &block)
25
27
  raise "Use promiscuous.sync on the parent instance" if @instance.embedded?
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
28
+ super
29
29
  end
30
30
 
31
31
  def attribute(attr)
@@ -41,7 +41,6 @@ module Promiscuous::Publisher::Model::Mongoid
41
41
  end
42
42
 
43
43
  module ClassMethods
44
- # TODO DRY this up with the publisher side
45
44
  def self.publish_on(method, options={})
46
45
  define_method(method) do |name, *args, &block|
47
46
  super(name, *args, &block)
@@ -1,46 +1,4 @@
1
1
  class ActiveRecord::Base
2
- module PostgresSQL2PCExtensions
3
- extend ActiveSupport::Concern
4
-
5
- def prepare_db_transaction
6
- execute("PREPARE TRANSACTION '#{quote_string(@current_transaction_id)}'")
7
- end
8
-
9
- def commit_prepared_db_transaction(xid)
10
- # We might always be racing with another instance, these sort of errors
11
- # are spurious.
12
- execute("COMMIT PREPARED '#{quote_string(xid)}'")
13
- rescue Exception => e
14
- raise unless e.message =~ /^PG::UndefinedObject/
15
- end
16
-
17
- def rollback_prepared_db_transaction(xid, options={})
18
- execute("ROLLBACK PREPARED '#{quote_string(xid)}'")
19
- rescue Exception => e
20
- raise unless e.message =~ /^PG::UndefinedObject/
21
- end
22
-
23
- included do
24
- # We want to make sure that we never block the database by having
25
- # uncommitted transactions.
26
- Promiscuous::Publisher::Operation::Base.register_recovery_mechanism do
27
- connection = ActiveRecord::Base.connection
28
- db_name = connection.current_database
29
-
30
- # We wait twice the time of expiration, to allow a better recovery scenario.
31
- expire_duration = 2 * Promiscuous::Publisher::Operation::Base.lock_options[:expire]
32
-
33
- q = "SELECT gid FROM pg_prepared_xacts " +
34
- "WHERE database = '#{db_name}' " +
35
- "AND prepared < current_timestamp + #{expire_duration} * interval '1 second'"
36
-
37
- connection.exec_query(q, "Promiscuous Recovery").each do |tx|
38
- ActiveRecord::Base::PromiscuousTransaction.recover_transaction(connection, tx['gid'])
39
- end
40
- end
41
- end
42
- end
43
-
44
2
  class << self
45
3
  alias_method :connection_without_promiscuous, :connection
46
4
 
@@ -50,10 +8,6 @@ class ActiveRecord::Base
50
8
  connection.class.class_eval do
51
9
  attr_accessor :current_transaction_id
52
10
 
53
- if self.name == "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
54
- include ActiveRecord::Base::PostgresSQL2PCExtensions
55
- end
56
-
57
11
  def promiscuous_hook; end
58
12
 
59
13
  alias_method :begin_db_transaction_without_promiscuous, :begin_db_transaction
@@ -165,7 +119,7 @@ class ActiveRecord::Base
165
119
  query.non_instrumented { db_operation.call }
166
120
  query.instrumented do
167
121
  db_operation_and_select.tap do
168
- transaction_context.add_write_operation(self) if !@instances.empty?
122
+ transaction_context.add_write_operation(self) if @instances.present?
169
123
  end
170
124
  end
171
125
  end
@@ -178,7 +132,8 @@ class ActiveRecord::Base
178
132
  def operation_payloads
179
133
  @instances.map do |instance|
180
134
  instance.promiscuous.payload(:with_attributes => self.operation.in?([:create, :update])).tap do |payload|
181
- payload[:operation] = self.operation
135
+ payload[:operation] = self.operation
136
+ payload[:version] = instance.__send__(Promiscuous::Config.version_field)
182
137
  end
183
138
  end
184
139
  end
@@ -196,9 +151,13 @@ class ActiveRecord::Base
196
151
 
197
152
  def db_operation_and_select
198
153
  # XXX This is only supported by Postgres and should be in the postgres driver
199
-
200
- @connection.exec_insert("#{@connection.to_sql(@arel, @binds)} RETURNING *", @operation_name, @binds).tap do |result|
201
- @instances = result.map { |row| model.instantiate(row) }
154
+ @connection.transaction do
155
+ @connection.exec_insert("#{@connection.to_sql(@arel, @binds)} RETURNING *", @operation_name, @binds).tap do |result|
156
+ @instances = result.map do |row|
157
+ instance = model.instantiate(row)
158
+ instance
159
+ end
160
+ end
202
161
  end
203
162
  # TODO Use correct primary key
204
163
  @instances.first.id
@@ -234,6 +193,8 @@ class ActiveRecord::Base
234
193
 
235
194
  def db_operation_and_select
236
195
  # TODO this should be in the postgres driver (to also leverage the cache)
196
+ @arel.ast.values << Arel::Nodes::SqlLiteral.new("\"#{Promiscuous::Config.version_field}\" = COALESCE(\"#{Promiscuous::Config.version_field}\", 0) + 1")
197
+
237
198
  @connection.exec_query("#{@connection.to_sql(@arel, @binds)} RETURNING *", @operation_name, @binds).tap do |result|
238
199
  @instances = result.map { |row| model.instantiate(row) }
239
200
  end.rows.size
@@ -254,7 +215,6 @@ class ActiveRecord::Base
254
215
  end
255
216
 
256
217
  def db_operation_and_select
257
- # TODO We only need the tracked attributes really (most likely, we just need ID)
258
218
  # XXX This is only supported by Postgres.
259
219
  @connection.exec_query("#{@connection.to_sql(@arel, @binds)} RETURNING *", @operation_name, @binds).tap do |result|
260
220
  @instances = result.map { |row| model.instantiate(row) }
@@ -272,24 +232,8 @@ class ActiveRecord::Base
272
232
  end
273
233
 
274
234
  def execute_instrumented(query)
275
- query.prepare { @connection.prepare_db_transaction }
276
- query.instrumented { @connection.commit_prepared_db_transaction(@transaction_id) }
235
+ query.instrumented { @connection.commit_db_transaction_without_promiscuous }
277
236
  super
278
237
  end
279
-
280
- def self.recover_transaction(connection, transaction_id)
281
- op = new(:connection => connection, :transaction_id => transaction_id)
282
- # Getting the lock will trigger the real recovery mechanism
283
- if op.acquire_op_lock
284
- op.release_op_lock
285
- end
286
-
287
- # In the event where the recovery payload wasn't found, we must roll back.
288
- # If the operation was recoverable, but couldn't be recovered, an
289
- # exception would be thrown, so we won't roll it back by mistake.
290
- # If the operation was recovered, the roll back will result in an error,
291
- # which is fine.
292
- connection.rollback_prepared_db_transaction(transaction_id)
293
- end
294
238
  end
295
239
  end
@@ -2,176 +2,33 @@ class Promiscuous::Publisher::Operation::Atomic < Promiscuous::Publisher::Operat
2
2
  # XXX instance can be a selector representation.
3
3
  attr_accessor :instance
4
4
 
5
- def initialize(options={})
6
- super
7
- @instance = options[:instance]
8
- end
9
-
10
- def acquire_op_lock
11
- unless dependency_for_op_lock
12
- return unless reload_instance
13
- end
14
-
15
- loop do
16
- instance_dep = dependency_for_op_lock
17
-
18
- super
19
-
20
- return if operation == :create
21
-
22
- # We need to make sure that the lock we acquired matches our selector.
23
- # There is a bit of room for optimization if we know that we don't have
24
- # any tracked attributes on the model and our selector is already an id.
25
- return unless reload_instance
26
-
27
- # If reload_instance changed the current instance because the selector,
28
- # we need to unlock the old instance, lock this new instance, and
29
- # retry.
30
- return if instance_dep == dependency_for_op_lock
31
-
32
- # XXX What should we do if we are going in a live lock?
33
- # Sleep with some jitter?
34
- release_op_lock
35
- end
36
- end
37
-
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)
5
+ def instances
6
+ [@instance].compact
71
7
  end
72
8
 
73
9
  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?
10
+ if operation == :destroy
11
+ fetch_instance
78
12
  else
79
- acquire_op_lock
80
-
81
- if @instance.nil?
82
- # The selector missed the instance, bailing out.
83
- query.call_and_remember_result(:non_instrumented)
84
- return
85
- end
13
+ increment_version_in_document
86
14
  end
87
15
 
88
- # All the versions are updated and a marked as pending for publish in Redis
89
- # atomically in case we die before we could write the versions in the
90
- # database. Once incremented, concurrent queries that are reading our
91
- # instance will be serialized after our write, even through it may read our
92
- # old instance. This is a race that we tolerate.
93
- # XXX We also stash the document for create operations, so the recovery can
94
- # redo the create to avoid races when instances are getting partitioned.
95
- increment_dependencies
96
-
97
- # From this point, if we die, the one expiring our write locks must finish
98
- # the publish, either by sending a dummy, or by sending the real instance.
99
- # We could have die before or after the database query.
100
-
101
- # We save the versions in the database, as it is our source of truth.
102
- # This allow a reconstruction of redis in the face of failures.
103
- # We would also need to send a special message to the subscribers to reset
104
- # their read counters to the last write version since we would not be able
105
- # to restore the read counters (and we don't want to store them because
106
- # this would dramatically augment our footprint on the db).
107
- #
108
- # If we are doing a destroy operation, and redis dies right after, and
109
- # we happen to lost contact with rabbitmq, recovery is going to be complex:
110
- # we would need to do a diff from the dummy subscriber to see what
111
- # documents are missing on our side to be able to resend the destroy
112
- # message.
113
-
114
- do_database_query(query) unless @instance.nil?
115
- # We take a timestamp right after the write is performed because latency
116
- # measurements are performed on the subscriber.
117
- record_timestamp
118
-
119
- # This make sure that if the db operation failed because of a network issue
120
- # and we got recovered, we don't send anything as we could send a different
121
- # message than the recovery mechanism.
122
- ensure_op_still_locked
123
-
124
- generate_payload
125
-
126
- # As soon as we unlock the locks, the rescuer will not be able to assume
127
- # that the database instance is still pristine, and so we need to stash the
128
- # payload in redis. If redis dies, we don't care because it can be
129
- # reconstructed. Subscribers can see "compressed" updates.
130
- publish_payload_in_redis
131
-
132
- # TODO Performance: merge these 3 redis operations to speed things up.
133
- release_op_lock
134
-
135
- # If we die from this point on, a recovery worker can republish our payload
136
- # since we queued it in Redis.
137
-
138
- # We don't care if we lost the lock and got recovered, subscribers are
139
- # immune to duplicate messages.
140
- publish_payload_in_rabbitmq_async
141
- end
16
+ transport_batch = create_transport_batch([self])
17
+ transport_batch.prepare
142
18
 
143
- def operation_payloads
144
- @instance.nil? ? [] : [payload_for(@instance)]
145
- end
146
-
147
- def query_dependencies
148
- dependencies_for(@instance)
149
- end
19
+ query.call_and_remember_result(:instrumented)
150
20
 
151
- def fetch_instance
152
- # This method is overridden to use the original query selector.
153
- # Should return nil if the instance is not found.
154
- @instance.reload if @instance.respond_to?(:reload)
155
- @instance
156
- end
21
+ unless operation == :destroy
22
+ # Refresh the operation on the batch to include the updated instance
23
+ # reflecting the executed operation so that we publish the correct data.
24
+ transport_batch.clear
25
+ transport_batch.add query.operation.operation, query.operation.instances
26
+ end
157
27
 
158
- def reload_instance
159
- @instance = fetch_instance
28
+ transport_batch.publish
160
29
  end
161
30
 
162
31
  def increment_version_in_document
163
- # Overridden to increment version field in the query
164
- end
165
-
166
- def use_id_selector(options={})
167
- # Overridden to use the {:id => @instance.id} selector.
168
- # if the option use_atomic_version_selector is passed, the driver must add
169
- # the version_field selector.
170
- end
171
-
172
- def recoverable_failure?(exception)
173
- # Overridden to tell if the db exception is spurious, like a network
174
- # failure.
175
32
  raise
176
33
  end
177
34
  end