Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I think that the json_safe? method should return true for symbols, to avoid the "Job arguments to **** must be native JSON types" error #5252

Closed
ndbroadbent opened this issue Mar 21, 2022 · 3 comments

Comments

@ndbroadbent
Copy link

ndbroadbent commented Mar 21, 2022

Ruby version: 2.7.1
Rails version: 6.0.4.6
Sidekiq / Pro / Enterprise version(s): Sidekiq 6.4.0

I had to hunt down why the Job arguments to #{item["class"]} must be native JSON types error was happening in lib/sidekiq/job_util.rb. It turned out that json_safe?(item) was returning false because I passing in a symbol as the first job argument. I have some Shrine uploader jobs that use symbols for the action arg (:promote, :delete):

class TemplateUploader < Shrine
  Attacher.promote do |data|
    ShrineJob.perform_async(:promote, data)
  end
  Attacher.delete do |data|
    ShrineJob.perform_async(:delete, data)
  end

  # ...

I believe it's generally safe to convert symbols to strings, so I was wondering if json_safe? could be a bit more lenient. Here's the original method:

    def json_safe?(item)
      JSON.parse(JSON.dump(item["args"])) == item["args"]
    end

What do you think about this change?

    def json_safe?(item)
      stringified_args = item["args"].map { |arg| arg.is_a?(Symbol) ? arg.to_s : arg }
      JSON.parse(JSON.dump(item["args"])) == stringified_args
    end

(Could even go a little further to ignore symbols in hash keys: arg.is_a?(Hash) ? arg.with_indifferent_access : arg)

I don't feel too strongly about it, but it would have just saved me a little bit of time. Would you accept a PR with this change? (No worries if not, I'm happy to switch them to strings.)

@ndbroadbent ndbroadbent changed the title I think that the json_safe? method should return true for symbols, to avoid the "Job arguments to ShrineJob must be native JSON types" error I think that the json_safe? method should return true for symbols, to avoid the "Job arguments to **** must be native JSON types" error Mar 21, 2022
@ndbroadbent
Copy link
Author

I've just noticed another place where this is happening in my code:

      InternalSlackNotificationJob.perform_async(
        channel: '#customers',
        text: slack_message,
        attachments: [
          {
            title: "View Account: #{account.name_or_email_or_uid}",
            title_link: Rails.application.routes.url_helpers.admin_account_url(account),
          },
        ]
      )

I'm actually using symbols as hash keys all over my app for this InternalSlackNotificationJob class, so it will be a bit annoying for me to change all of these into strings.

What do you think about this change?

    def json_safe?(item)
      stringified_args = item["args"].map do |arg|
        case arg
        when Symbol
          arg.to_s
        when Hash
          arg.with_indifferent_access
        else
          arg
        end
      end
      JSON.parse(JSON.dump(item["args"])) == stringified_args
    end

@ndbroadbent
Copy link
Author

ndbroadbent commented Mar 22, 2022

Sorry for the noise, I read through the discussion in #5071. I can see that symbols are intentionally banned for this reason (with strict args):

def perform(hash)
    id = hash[:id] # will never exist, but hash['id'] will!

I suppose I will need to use Sidekiq.strict_args!(false) for now. But I think it would be more user-friendly to just call with_indifferent_access behind the scenes for any arguments that are hashes, so we can actually pass in a hash with symbols as keys, and then continue to use symbols to reference values inside #perform. Just my two cents! But I'm happy with Sidekiq.strict_args!(false) since you mentioned that it will always be supported for backwards compatibility. Thanks!


EDIT: Just another 2 cents... I was thinking some more, and symbols are such a big part of Ruby that it would be even nicer if you could use some different marshaling format that actually preserved the difference between symbols and strings. It feels really weird to use strings as args or hash keys when the rest of my app uses symbols everywhere else. It feels like the bug here is using JSON, which doesn't fully support Ruby data types (since Sidekiq is Ruby on Rails job processor, and symbols are a big part of Ruby code.)

Maybe a future Sidekiq version could use msgpack with an extension type for symbols. This would also be great for dates and times.

Anyway, that's enough from me. Hope you're having a good week!

@adamniedzielski
Copy link
Contributor

adamniedzielski commented Mar 22, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants