Fix Psych loading errors on wisper-sidekiq
One of my favorite rubygems is Wisper, a simple library that lets you add pub/sub style broadcasting and listeners to your app. (Been a fan since it came out in 2014, almost ten years ago!)
I tried to use wisper again recently, specifically with the wisper-sidekiq
companion gem, which allows your subscribers to execute asynchronously as Sidekiq jobs.
Unfortunately, I immediately ran into an issue with Psych complaining about my parameters (ActiveRecord model instances) not being allowed by the YAML loading subsystem. If you're running into this same kind of issue, you'll know because you get exceptions that look like Tried to load unspecified class: Account (Psych::DisallowedClass)
Sidestepping the question of whether you should stick to only sending primitive object parameters (strings, integers, etc) as arguments to Sidekiq jobs, here is the monkeypatch solution to solving the problem, tested with wisper-sidekiq version 1.3.0.
Chuck this override into an initializer file.
module Wisper
class SidekiqBroadcaster
class Worker
include ::Sidekiq::Worker
def perform(yml)
(subscriber, event, args) = ::YAML.unsafe_load(yml)
subscriber.public_send(event, *args)
end
end
end
end
The fix is the replacement of YAML.load
with YAML.unsafe_load
on line 16.
But Obie, isn't this dangerous? No. Objects passed in broadcast
events are almost certainly not coming from the outside world in any way, shape, or form, so the reasons that you would typically be interested in blocking YAML loading from processing arbitrary objects do not apply.
But Obie, if your queues back up, won't you have stale data sitting in Redis as parameters to your jobs? For my particular use case this is not a concern, but for yours it might be. Either way it's not the job of the sidekiq-wisper library to enforce this constraint... and indeed, since the library predates the safety additions to YAML.load I'm not even sure that the author intended for the constraint to exist.
Now what would really be cool, and I wish I had time to implement this myself, is if sidekiq-wisper would automatically turn activerecord params into globalid identifiers and query them for you on the consumer side, the way that sidekiq does. Somebody should definitely implement something like that!