pub_sub_model_sync 0.6.0 → 1.0.beta

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.
data/docker-compose.yaml CHANGED
@@ -5,7 +5,7 @@ services:
5
5
  build: .
6
6
  command: sh -c 'bundle exec rspec'
7
7
  volumes:
8
- - .:/myapp
8
+ - .:/app
9
9
  - bundler_gems:/usr/local/bundle/
10
10
 
11
11
  volumes:
Binary file
@@ -10,8 +10,10 @@ require 'pub_sub_model_sync/subscriber_concern'
10
10
  require 'pub_sub_model_sync/message_publisher'
11
11
  require 'pub_sub_model_sync/publisher_concern'
12
12
  require 'pub_sub_model_sync/runner'
13
+ require 'pub_sub_model_sync/transaction'
13
14
  require 'pub_sub_model_sync/connector'
14
15
  require 'pub_sub_model_sync/message_processor'
16
+ require 'pub_sub_model_sync/run_subscriber'
15
17
 
16
18
  require 'pub_sub_model_sync/publisher'
17
19
  require 'pub_sub_model_sync/subscriber'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  class Base
5
- delegate :config, :log, to: self
5
+ delegate :config, :log, :debug?, to: self
6
6
 
7
7
  class << self
8
8
  def config
@@ -12,6 +12,10 @@ module PubSubModelSync
12
12
  def log(message, kind = :info)
13
13
  config.log message, kind
14
14
  end
15
+
16
+ def debug?
17
+ config.debug
18
+ end
15
19
  end
16
20
 
17
21
  # @param errors (Array(Class|String))
@@ -3,12 +3,12 @@
3
3
  module PubSubModelSync
4
4
  class Config
5
5
  cattr_accessor(:subscribers) { [] }
6
- cattr_accessor(:publishers) { [] }
7
6
  cattr_accessor(:service_name) { :google }
8
7
 
9
8
  # customizable callbacks
10
9
  cattr_accessor(:debug) { false }
11
10
  cattr_accessor :logger # LoggerInst
11
+ cattr_accessor(:transactions_use_buffer) { true }
12
12
 
13
13
  cattr_accessor(:on_before_processing) { ->(_payload, _info) {} } # return :cancel to skip
14
14
  cattr_accessor(:on_success_processing) { ->(_payload, _info) {} }
@@ -16,16 +16,15 @@ module PubSubModelSync
16
16
  cattr_accessor(:on_before_publish) { ->(_payload) {} } # return :cancel to skip
17
17
  cattr_accessor(:on_after_publish) { ->(_payload) {} }
18
18
  cattr_accessor(:on_error_publish) { ->(_exception, _info) {} }
19
- cattr_accessor(:disabled_callback_publisher) { ->(_model, _action) { false } }
20
19
 
21
20
  # google service
22
- cattr_accessor :project, :credentials, :topic_name, :subscription_name
21
+ cattr_accessor :project, :credentials, :topic_name, :subscription_name, :default_topic_name
23
22
 
24
23
  # rabbitmq service
25
- cattr_accessor :bunny_connection, :topic_name, :subscription_name
24
+ cattr_accessor :bunny_connection, :topic_name, :subscription_name, :default_topic_name
26
25
 
27
26
  # kafka service
28
- cattr_accessor :kafka_connection, :topic_name, :subscription_name
27
+ cattr_accessor :kafka_connection, :topic_name, :subscription_name, :default_topic_name
29
28
 
30
29
  def self.log(msg, kind = :info)
31
30
  msg = "PS_MSYNC ==> #{msg}"
@@ -37,8 +36,17 @@ module PubSubModelSync
37
36
  end
38
37
 
39
38
  def self.subscription_key
40
- subscription_name ||
41
- (Rails.application.class.parent_name rescue '') # rubocop:disable Style/RescueModifier
39
+ klass = Rails.application.class
40
+ app_name = klass.respond_to?(:module_parent_name) ? klass.module_parent_name : klass.parent_name
41
+ subscription_name || app_name
42
+ end
43
+
44
+ class << self
45
+ alias default_topic_name_old default_topic_name
46
+
47
+ def default_topic_name
48
+ default_topic_name_old || Array(topic_name).first
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -28,12 +28,12 @@ module PubSubModelSync
28
28
  private
29
29
 
30
30
  def run_subscriber(subscriber)
31
- subscriber = subscriber.dup
31
+ processor = PubSubModelSync::RunSubscriber.new(subscriber, payload)
32
32
  return unless processable?(subscriber)
33
33
 
34
34
  errors = [ActiveRecord::ConnectionTimeoutError, 'deadlock detected', 'could not serialize access']
35
- retry_error(errors, qty: 2) do
36
- subscriber.process!(payload)
35
+ retry_error(errors, qty: 5) do
36
+ processor.call
37
37
  res = config.on_success_processing.call(payload, { subscriber: subscriber })
38
38
  log "processed message with: #{payload.inspect}" if res != :skip_log
39
39
  end
@@ -54,8 +54,7 @@ module PubSubModelSync
54
54
 
55
55
  def filter_subscribers
56
56
  config.subscribers.select do |subscriber|
57
- subscriber.settings[:from_klass].to_s == payload.klass.to_s &&
58
- subscriber.settings[:from_action].to_s == payload.action.to_s
57
+ subscriber.from_klass == payload.klass && subscriber.action == payload.action && payload.mode == subscriber.mode
59
58
  end
60
59
  end
61
60
  end
@@ -4,7 +4,7 @@ module PubSubModelSync
4
4
  class MessagePublisher < PubSubModelSync::Base
5
5
  class << self
6
6
  class MissingPublisher < StandardError; end
7
- attr_accessor :transaction_key
7
+ attr_accessor :current_transaction
8
8
 
9
9
  def connector
10
10
  @connector ||= PubSubModelSync::Connector.new
@@ -16,70 +16,53 @@ module PubSubModelSync
16
16
  # be processed in the correct order, despite of the multiple threads. This thanks to the fact
17
17
  # that Pub/Sub services will always send messages with the same `ordering_key` into the same
18
18
  # worker/thread.
19
- # @param key (String): This key will be used as the ordering_key for all payload
20
- # inside this transaction.
21
- def transaction(key, &block)
22
- parent_key = init_transaction(key)
23
- begin
24
- block.call
25
- ensure
26
- end_transaction(parent_key)
27
- end
19
+ # @see Transaction.new(...)
20
+ # @param key (String|Nil)
21
+ # @param block (Yield) block to be executed
22
+ def transaction(key, settings = {}, &block)
23
+ t = init_transaction(key, settings)
24
+ block.call
25
+ t.deliver_all
26
+ rescue
27
+ t.rollback
28
+ raise
29
+ ensure
30
+ t.clean_publisher
28
31
  end
29
32
 
30
33
  # Starts a new transaction
31
- # @return (String) returns parent transaction key
32
- def init_transaction(key)
33
- parent_key = transaction_key
34
- self.transaction_key = transaction_key.presence || key
35
- parent_key
36
- end
37
-
38
- # Restores to the last transaction key
39
- def end_transaction(parent_key)
40
- self.transaction_key = parent_key
34
+ # @param key (@transaction_key)
35
+ # @return (Transaction)
36
+ def init_transaction(key, settings = {})
37
+ new_transaction = PubSubModelSync::Transaction.new(key, settings)
38
+ if current_transaction
39
+ current_transaction.add_transaction(new_transaction)
40
+ else
41
+ self.current_transaction = new_transaction
42
+ end
43
+ new_transaction
41
44
  end
42
45
 
43
- # Publishes any value to pubsub
44
- # @param klass (String): Class name
45
- # @param data (Hash): Data to be delivered
46
- # @param action (:symbol): action name
47
- # @param headers (Hash, optional): header settings (More in Payload.headers)
46
+ # Publishes a class level notification via pubsub
47
+ # @refer PublisherConcern.ps_class_publish
48
48
  # @return Payload
49
49
  def publish_data(klass, data, action, headers: {})
50
- attrs = { klass: klass.to_s, action: action.to_sym }
50
+ attrs = { klass: klass.to_s, action: action.to_sym, mode: :klass }
51
51
  payload = PubSubModelSync::Payload.new(data, attrs, headers)
52
52
  publish(payload)
53
53
  end
54
54
 
55
- # Publishes custom model action
56
- # @param model (ActiveRecord): Model object owner of the data
57
- # @param data (Hash): Data to be delivered
58
- # @param action (:symbol): action name
59
- # @param as_klass (String, optional): Class name (default model class name)
60
- # @param headers (Hash, optional): header settings (More in Payload.headers)
61
- # @return Payload
62
- def publish_model_data(model, data, action, as_klass: nil, headers: {})
63
- headers = PubSubModelSync::Publisher.headers_for(model, action).merge(headers)
64
- publish_data(as_klass || model.class.name, data, action, headers: headers)
65
- end
66
-
67
- # Publishes model info to pubsub
68
- # @param model (ActiveRecord model)
69
- # @param action (Sym): Action name
70
- # @param custom_data (Hash, optional): If present custom_data will be used as the payload data.
71
- # @param custom_headers (Hash): Refer Payload.headers
72
- # @return Payload
73
- def publish_model(model, action, custom_data: nil, custom_headers: {})
74
- return if model.ps_skip_sync?(action)
55
+ # @param model (ActiveRecord::Base)
56
+ # @param action (Symbol: @see PublishConcern::ps_publish)
57
+ # @param settings (Hash: @see Publisher.new.settings)
58
+ def publish_model(model, action, settings = {})
59
+ return if model.ps_skip_publish?(action)
75
60
 
76
- publisher = model.class.ps_publisher(action)
77
- error_msg = "No publisher found for: \"#{[model.class.name, action]}\""
78
- raise(MissingPublisher, error_msg) unless publisher
61
+ publisher = PubSubModelSync::Publisher.new(model, action, settings)
62
+ payload = publisher.payload
79
63
 
80
- payload = publisher.payload(model, action, custom_data: custom_data, custom_headers: custom_headers)
81
- transaction(payload.headers[:ordering_key]) do # catch and group all :ps_before_sync syncs
82
- publish(payload) { model.ps_after_sync(action, payload) } if ensure_model_publish(model, action, payload)
64
+ transaction(payload.headers[:ordering_key]) do # catch and group all :ps_before_publish syncs
65
+ publish(payload) { model.ps_after_publish(action, payload) } if ensure_model_publish(model, action, payload)
83
66
  end
84
67
  end
85
68
 
@@ -88,15 +71,20 @@ module PubSubModelSync
88
71
  # @return Payload
89
72
  # Raises error if exist
90
73
  def publish!(payload, &block)
74
+ payload.headers[:ordering_key] = ordering_key_for(payload)
91
75
  return unless ensure_publish(payload)
92
76
 
93
- log("Publishing message: #{[payload]}")
94
- connector.publish(payload)
95
- config.on_after_publish.call(payload)
77
+ current_transaction ? current_transaction.add_payload(payload) : connector_publish(payload)
96
78
  block&.call
97
79
  payload
98
80
  end
99
81
 
82
+ def connector_publish(payload)
83
+ connector.publish(payload)
84
+ log("Published message: #{[payload]}")
85
+ config.on_after_publish.call(payload)
86
+ end
87
+
100
88
  # Similar to :publish! method
101
89
  # Notifies error via :on_error_publish instead of raising error
102
90
  # @return Payload
@@ -109,18 +97,20 @@ module PubSubModelSync
109
97
  private
110
98
 
111
99
  def ensure_publish(payload)
112
- payload.headers[:ordering_key] = @transaction_key if @transaction_key.present?
113
- forced_ordering_key = payload.headers[:forced_ordering_key]
114
- payload.headers[:ordering_key] = forced_ordering_key if forced_ordering_key
115
100
  cancelled = config.on_before_publish.call(payload) == :cancel
116
101
  log("Publish cancelled by config.on_before_publish: #{payload}") if config.debug && cancelled
117
102
  !cancelled
118
103
  end
119
104
 
105
+ def ordering_key_for(payload)
106
+ current_transaction&.key ||= payload.headers[:ordering_key]
107
+ payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]
108
+ end
109
+
120
110
  def ensure_model_publish(model, action, payload)
121
- res_before = model.ps_before_sync(action, payload)
111
+ res_before = model.ps_before_publish(action, payload)
122
112
  cancelled = res_before == :cancel
123
- log("Publish cancelled by model.ps_before_sync: #{payload}") if config.debug && cancelled
113
+ log("Publish cancelled by model.ps_before_publish: #{payload}") if config.debug && cancelled
124
114
  !cancelled
125
115
  end
126
116
 
@@ -3,10 +3,10 @@
3
3
  module PubSubModelSync
4
4
  class Payload
5
5
  class MissingInfo < StandardError; end
6
- attr_reader :data, :attributes, :headers
6
+ attr_reader :data, :info, :headers
7
7
 
8
8
  # @param data (Hash: { any value }):
9
- # @param attributes (Hash: { klass*: string, action*: :sym }):
9
+ # @param info (Hash: { klass*: string, action*: :sym, mode?: :klass|:model }):
10
10
  # @param headers (Hash):
11
11
  # key (String): identifier of the payload, default:
12
12
  # klass/action: when class message
@@ -19,9 +19,9 @@ module PubSubModelSync
19
19
  # message (default first topic)
20
20
  # forced_ordering_key (String, optional): Will force to use this value as the ordering_key,
21
21
  # even withing transactions. Default nil.
22
- def initialize(data, attributes, headers = {})
22
+ def initialize(data, info, headers = {})
23
23
  @data = data
24
- @attributes = attributes
24
+ @info = { mode: :model }.merge(info)
25
25
  @headers = headers
26
26
  build_headers
27
27
  validate!
@@ -29,15 +29,19 @@ module PubSubModelSync
29
29
 
30
30
  # @return Hash: payload data
31
31
  def to_h
32
- { data: data, attributes: attributes, headers: headers }
32
+ { data: data, info: info, headers: headers }
33
33
  end
34
34
 
35
35
  def klass
36
- attributes[:klass].to_s
36
+ info[:klass].to_s
37
37
  end
38
38
 
39
39
  def action
40
- attributes[:action]
40
+ info[:action].to_sym
41
+ end
42
+
43
+ def mode
44
+ info[:mode].to_sym
41
45
  end
42
46
 
43
47
  # Process payload data
@@ -69,10 +73,10 @@ module PubSubModelSync
69
73
  end
70
74
 
71
75
  # convert payload data into Payload
72
- # @param data [Hash]: payload data (:data, :attributes, :headers)
76
+ # @param data [Hash]: payload data (:data, :info, :headers)
73
77
  def self.from_payload_data(data)
74
78
  data = data.deep_symbolize_keys
75
- new(data[:data], data[:attributes], data[:headers])
79
+ new(data[:data], data[:info], data[:headers])
76
80
  end
77
81
 
78
82
  private
@@ -85,7 +89,7 @@ module PubSubModelSync
85
89
  end
86
90
 
87
91
  def validate!
88
- raise MissingInfo if !attributes[:klass] || !attributes[:action]
92
+ raise MissingInfo if !info[:klass] || !info[:action]
89
93
  end
90
94
  end
91
95
  end
@@ -1,31 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- class Publisher
5
- attr_accessor :attrs, :actions, :klass, :as_klass, :headers
6
-
7
- # @param headers (Hash): refer Payload.headers
8
- def initialize(attrs, klass, actions = nil, as_klass: nil, headers: {})
9
- @attrs = attrs
10
- @klass = klass
11
- @actions = actions || %i[create update destroy]
12
- @as_klass = as_klass || klass
13
- @headers = headers
4
+ class Publisher < PubSubModelSync::Base
5
+ attr_accessor :model, :action, :data, :mapping, :headers, :as_klass
6
+
7
+ # @param model (ActiveRecord::Base)
8
+ # @param action (@see PublishConcern::ps_publish)
9
+ # @param settings (@see PublishConcern::ps_publish): { data:, mapping:, headers:, as_klass: }
10
+ def initialize(model, action, settings = {})
11
+ @model = model
12
+ @action = action
13
+ @data = settings[:data] || {}
14
+ @mapping = settings[:mapping] || []
15
+ @headers = settings[:headers] || {}
16
+ @as_klass = settings[:as_klass] || model.class.name
14
17
  end
15
18
 
16
- # Builds the payload with model information defined for :action (:create|:update|:destroy)
17
- # @param custom_headers (Hash, default {}): refer Payload.headers
18
- def payload(model, action, custom_data: nil, custom_headers: {})
19
- payload_headers = self.class.headers_for(model, action)
20
- .merge(headers)
21
- .merge(custom_headers)
22
- data = custom_data || payload_data(model)
23
- PubSubModelSync::Payload.new(data, payload_attrs(model, action), payload_headers)
24
- end
25
-
26
- def self.headers_for(model, action)
27
- key = [model.class.name, action, model.id].join('/')
28
- { ordering_key: ordering_key_for(model), key: key }
19
+ # @return (Payload)
20
+ def payload
21
+ values = compute_value(data)
22
+ values = mapping_data.merge(values)
23
+ PubSubModelSync::Payload.new(values, settings_data, headers_data)
29
24
  end
30
25
 
31
26
  def self.ordering_key_for(model)
@@ -34,18 +29,29 @@ module PubSubModelSync
34
29
 
35
30
  private
36
31
 
37
- def payload_data(model)
38
- @attrs.map do |prop|
32
+ def headers_data
33
+ klass_name = model.class.name
34
+ key = [klass_name, action, model.id || SecureRandom.uuid].join('/')
35
+ def_data = { ordering_key: self.class.ordering_key_for(model), key: key }
36
+ def_data.merge(compute_value(headers))
37
+ end
38
+
39
+ def compute_value(value)
40
+ res = value
41
+ res = model.send(value, action) if value.is_a?(Symbol) # method name
42
+ res = value.call(model, action) if value.is_a?(Proc)
43
+ res
44
+ end
45
+
46
+ def settings_data
47
+ { klass: as_klass, action: action }
48
+ end
49
+
50
+ def mapping_data
51
+ mapping.map do |prop|
39
52
  source, target = prop.to_s.split(':')
40
53
  [target || source, model.send(source.to_sym)]
41
54
  end.to_h.symbolize_keys
42
55
  end
43
-
44
- def payload_attrs(model, action)
45
- {
46
- klass: (as_klass || model.class.name).to_s,
47
- action: action.to_sym
48
- }
49
- end
50
56
  end
51
57
  end
@@ -2,90 +2,83 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  module PublisherConcern
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- base.send(:ps_init_transaction_callbacks)
8
- end
5
+ extend ActiveSupport::Concern
9
6
 
10
- # Before initializing sync service (callbacks: after create/update/destroy)
11
- def ps_skip_callback?(_action)
12
- false
7
+ included do
8
+ extend ClassMethods
9
+ ps_init_transaction_callbacks if self <= ActiveRecord::Base
13
10
  end
14
11
 
15
12
  # before preparing data to sync
16
- def ps_skip_sync?(_action)
13
+ def ps_skip_publish?(_action)
17
14
  false
18
15
  end
16
+ alias ps_skip_sync? ps_skip_publish? # @deprecated
19
17
 
20
18
  # before delivering data (return :cancel to cancel sync)
21
- def ps_before_sync(_action, _payload); end
19
+ def ps_before_publish(_action, _payload); end
20
+ alias ps_before_sync ps_before_publish # @deprecated
22
21
 
23
22
  # after delivering data
24
- def ps_after_sync(_action, _payload); end
23
+ def ps_after_publish(_action, _payload); end
24
+ alias ps_after_sync ps_after_publish # @deprecated
25
25
 
26
- # To perform sync on demand
27
- # @param action (Sym): CRUD action name
28
- # @param custom_data (nil|Hash) If present custom_data will be used as the payload data. I.E.
29
- # data generator will be ignored
30
- # @param custom_headers (Hash, optional): refer Payload.headers
31
- def ps_perform_sync(action = :create, custom_data: nil, custom_headers: {})
26
+ # Delivers a notification via pubsub
27
+ # @param action (Sym|String) Sample: create|update|save|destroy|<any_other_key>
28
+ # @param mapping? (Array<String>) If present will generate data using the mapping and added to the payload.
29
+ # Sample: ["id", "full_name:name"]
30
+ # @param data? (Hash|Symbol|Proc)
31
+ # Hash: Data to be added to the payload
32
+ # Symbol: Method name to be called to retrieve payload data (must return a hash value, receives :action name)
33
+ # Proc: Block to be called to retrieve payload data
34
+ # @param headers? (Hash|Symbol|Proc): (All available attributes in Payload.headers)
35
+ # Hash: Data that will be merged with default header values
36
+ # Symbol: Method name that will be called to retrieve header values (must return a hash, receives :action name)
37
+ # Proc: Block to be called to retrieve header values
38
+ # @param as_klass? (String): Output class name used instead of current class name
39
+ def ps_publish(action, data: {}, mapping: [], headers: {}, as_klass: self.class.name)
32
40
  p_klass = PubSubModelSync::MessagePublisher
33
- p_klass.publish_model(self, action, custom_data: custom_data, custom_headers: custom_headers)
41
+ p_klass.publish_model(self, action, data: data, mapping: mapping, headers: headers, as_klass: as_klass)
34
42
  end
43
+ delegate :ps_class_publish, to: :class
35
44
 
36
45
  module ClassMethods
37
- # Permit to configure to publish crud actions (:create, :update, :destroy)
38
- # @param headers (Hash, optional): Refer Payload.headers
39
- def ps_publish(attrs, actions: %i[create update destroy], as_klass: nil, headers: {})
40
- klass = PubSubModelSync::Publisher
41
- publisher = klass.new(attrs, name, actions, as_klass: as_klass, headers: headers)
42
- PubSubModelSync::Config.publishers << publisher
43
- actions.each do |action|
44
- ps_register_callback(action.to_sym)
45
- end
46
- end
47
-
48
- # Klass level notification
49
- # @deprecated this method was deprecated in favor of:
50
- # PubSubModelSync::MessagePublisher.publish_data(...)
46
+ # Publishes a class level notification via pubsub
47
+ # @param data (Hash): Data of the notification
48
+ # @param action (Symbol): action name of the notification
49
+ # @param as_klass (String, default current class name): Class name of the notification
50
+ # @param headers (Hash, optional): header settings (More in Payload.headers)
51
51
  def ps_class_publish(data, action:, as_klass: nil, headers: {})
52
52
  klass = PubSubModelSync::MessagePublisher
53
53
  klass.publish_data((as_klass || name).to_s, data, action.to_sym, headers: headers)
54
54
  end
55
55
 
56
- # Publisher info for specific action
57
- def ps_publisher(action = :create)
58
- PubSubModelSync::Config.publishers.find do |publisher|
59
- publisher.klass == name && publisher.actions.include?(action)
56
+ # @param crud_actions (Symbol|Array<Symbol>): :create, :update, :destroy
57
+ # @param method_name (Symbol, optional) method to be called
58
+ def ps_on_crud_event(crud_actions, method_name = nil, &block)
59
+ crud_actions = Array(crud_actions)
60
+ callback = ->(action) { method_name ? send(method_name, action) : instance_exec(action, &block) }
61
+ commit_name = respond_to?(:before_commit) ? :before_commit : :after_commit
62
+ crud_actions.each do |action|
63
+ send(commit_name, on: :create) { instance_exec(action, &callback) } if action == :create
64
+ send(commit_name, on: :update) { instance_exec(action, &callback) } if action == :update
65
+ after_destroy { instance_exec(action, &callback) } if action == :destroy
60
66
  end
61
67
  end
62
68
 
63
69
  private
64
70
 
65
- # TODO: skip all enqueued notifications after_rollback (when failed)
66
71
  # Initialize calls to start and end pub_sub transactions and deliver all them in the same order
67
72
  def ps_init_transaction_callbacks
68
73
  start_transaction = lambda do
69
- key = PubSubModelSync::Publisher.ordering_key_for(self)
70
- @ps_old_transaction_key = PubSubModelSync::MessagePublisher.init_transaction(key)
74
+ key = id ? PubSubModelSync::Publisher.ordering_key_for(self) : nil
75
+ @ps_transaction = PubSubModelSync::MessagePublisher.init_transaction(key)
71
76
  end
72
- end_transaction = -> { PubSubModelSync::MessagePublisher.end_transaction(@ps_old_transaction_key) }
73
77
  after_create start_transaction, prepend: true # wait for ID
74
78
  before_update start_transaction, prepend: true
75
79
  before_destroy start_transaction, prepend: true
76
- after_commit end_transaction
77
- after_rollback end_transaction
78
- end
79
-
80
- # Configure specific callback and execute publisher when called callback
81
- def ps_register_callback(action)
82
- after_commit(on: action) do |model|
83
- disabled = PubSubModelSync::Config.disabled_callback_publisher.call(model, action)
84
- if !disabled && !model.ps_skip_callback?(action)
85
- klass = PubSubModelSync::MessagePublisher
86
- klass.publish_model(model, action.to_sym)
87
- end
88
- end
80
+ after_commit { @ps_transaction.deliver_all }
81
+ after_rollback(prepend: true) { @ps_transaction.rollback }
89
82
  end
90
83
  end
91
84
  end