mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
bc1f323338
Previously, BigDecimal was listed as not needing a serializer. However, when used with an adapter storing the job arguments as JSON, it would get serialized as a simple String, resulting in deserialization also producing a String (instead of a BigDecimal). By using a serializer, we ensure the round trip is safe. During upgrade deployments of applications with multiple replicas making use of BigDecimal job arguments with a queue adapter serializing to JSON, there exists a possible race condition, whereby a "new" replica enqueues a job with an argument serialized using `BigDecimalSerializer`, and an "old" replica fails to deserialize it (as it does not have `BigDecimalSerializer`). Therefore, to ensure safe upgrades, serialization will not use `BigDecimalSerializer` until `config.active_job.use_big_decimal_serializer` is enabled, which can be done safely after successful deployment of Rails 7.1. This option will be removed in Rails 7.2, when it will become the default.
70 lines
2.3 KiB
Ruby
70 lines
2.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "set"
|
|
|
|
module ActiveJob
|
|
# The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers
|
|
# and to add new ones. It also has helpers to serialize/deserialize objects.
|
|
module Serializers # :nodoc:
|
|
extend ActiveSupport::Autoload
|
|
|
|
autoload :ObjectSerializer
|
|
autoload :TimeObjectSerializer
|
|
autoload :SymbolSerializer
|
|
autoload :DurationSerializer
|
|
autoload :DateTimeSerializer
|
|
autoload :DateSerializer
|
|
autoload :TimeWithZoneSerializer
|
|
autoload :TimeSerializer
|
|
autoload :ModuleSerializer
|
|
autoload :RangeSerializer
|
|
autoload :BigDecimalSerializer
|
|
|
|
mattr_accessor :_additional_serializers
|
|
self._additional_serializers = Set.new
|
|
|
|
class << self
|
|
# Returns serialized representative of the passed object.
|
|
# Will look up through all known serializers.
|
|
# Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
|
|
def serialize(argument)
|
|
serializer = serializers.detect { |s| s.serialize?(argument) }
|
|
raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
|
|
serializer.serialize(argument)
|
|
end
|
|
|
|
# Returns deserialized object.
|
|
# Will look up through all known serializers.
|
|
# If no serializer found will raise <tt>ArgumentError</tt>.
|
|
def deserialize(argument)
|
|
serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY]
|
|
raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name
|
|
|
|
serializer = serializer_name.safe_constantize
|
|
raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer
|
|
|
|
serializer.deserialize(argument)
|
|
end
|
|
|
|
# Returns list of known serializers.
|
|
def serializers
|
|
self._additional_serializers
|
|
end
|
|
|
|
# Adds new serializers to a list of known serializers.
|
|
def add_serializers(*new_serializers)
|
|
self._additional_serializers += new_serializers.flatten
|
|
end
|
|
end
|
|
|
|
add_serializers SymbolSerializer,
|
|
DurationSerializer,
|
|
DateTimeSerializer,
|
|
DateSerializer,
|
|
TimeWithZoneSerializer,
|
|
TimeSerializer,
|
|
ModuleSerializer,
|
|
RangeSerializer,
|
|
BigDecimalSerializer
|
|
end
|
|
end
|