rails_event_store icon indicating copy to clipboard operation
rails_event_store copied to clipboard

AfterCommitAsyncDispatcher does not pass event to custom scheduler

Open grncdr opened this issue 2 years ago • 4 comments

Every layer from Broker->Scheduler passes a triple of (subscriber, event, event_record), but the very last step hides the event object and only passes the serializ(ed/able) record to custom schedulers:

https://github.com/RailsEventStore/rails_event_store/blob/e7ede7d516355a2b1fa65fe3f4a70393232dd937/rails_event_store/lib/rails_event_store/after_commit_async_dispatcher.rb#L9-L11

This is a problem for me because I'd like to run a predicate on the event in my custom scheduler before enqueueing a job. My specific use case involves a fairly frequent event, where only a tiny percentage are actually relevant to a given subscriber.

How I'm working around this

Monkey patch AfterCommitAsyncDispatcher in an initializer:

class RailsEventStore::AfterCommitAsyncDispatcher
  def call(subscriber, event, record)
    run { @scheduler.call(subscriber, event, record) } 
  end
end

Suggested patch

The existing API contract is obviously for 2 arguments only. I know arity checking is usually frowned upon, but I feel like it's an acceptable trade-off here:

class AfterCommitAsyncDispatcher
  def initialize(scheduler:)
    @scheduler = scheduler
    @scheduler_arity = scheduler.method(:call).arity
  end

  def call(subscriber, event, record)
    run { @scheduler_arity === 3 ? @scheduler.call(subscriber, event, record) : @scheduler.call(subscriber, record) } 
  end
end

What do you think? Should this be changed? Would the change be breaking or something that could happen in the 2.x series?

grncdr avatar Aug 22 '22 11:08 grncdr

On the topic:

  • passing the event instance for the purpose of making decisions — makes sense to me
  • changing scheduler method arity — that's not going to happen in 2.x, but to be accepted in 3.x
  • arity checking like any other conditional — acceptable hack if intended short term and for the greater good of smoother transition in next version; it would be ideal if we can decide on scheduler's call arity when initializing dispatcher

Aside: is there any reason why you'd monkeypatch RailsEventStore::AfterCommitAsyncDispatcher instead of passing your own project-specific dispatcher to RailsEventStore::Client initializer?

mostlyobvious avatar Sep 12 '22 17:09 mostlyobvious

Aside: is there any reason why you'd monkeypatch RailsEventStore::AfterCommitAsyncDispatcher instead of passing your own project-specific dispatcher to RailsEventStore::Client initializer?

Actually no 😅. I am passing a project-specific scheduler to the AfterCommitAsyncDispatcher, but I never considered subclassing the dispatcher. I guess I was hoping that the change would be upstreamed "soon" and I could delete my monkey patch. 🙃

grncdr avatar Sep 12 '22 18:09 grncdr

Just ran into this while trying to introduce jitter (defined on event) into scheduler 🙃

mostlyobvious avatar Sep 27 '22 14:09 mostlyobvious

Hi folks! I ran into this exact problem today & ended up reimplementing AfterCommitAsyncDispatcher to instead delegate to a child dispatcher.

If the change to the scheduler API discussed here ends up in 3.0 that would work for my use-case, but I wonder if it might be possible to ship something before that by making this a purely additive change. I.e., adding something like an AfterCommitAsyncDelegatingDispatcher (name tbd) which behaves exactly like AfterCommitAsyncDispatcher, except that it calls a dispatcher rather than a scheduler?

The AfterCommitAsyncDispatcher would then just be an AfterCommitAsyncDelegatingDispatcher whose child dispatcher just wraps the scheduler interface & drops its event argument.

If this is a desirable change I'm happy to submit a PR. Just let me know.

matthew-healy avatar Aug 04 '23 13:08 matthew-healy