From 1231369750cb217c0c9929721ed67d68deb2e7f9 Mon Sep 17 00:00:00 2001 From: Adam Ruzicka Date: Tue, 18 Nov 2025 12:48:57 +0100 Subject: [PATCH 1/2] Only use JSON-native types with sidekiq --- lib/dynflow/executors/sidekiq/core.rb | 2 +- lib/dynflow/executors/sidekiq/serialization.rb | 8 +++++--- lib/dynflow/serializer.rb | 8 +++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/dynflow/executors/sidekiq/core.rb b/lib/dynflow/executors/sidekiq/core.rb index 0abbee0c..491c6f09 100644 --- a/lib/dynflow/executors/sidekiq/core.rb +++ b/lib/dynflow/executors/sidekiq/core.rb @@ -13,7 +13,7 @@ config[:semi_reliable_fetch] = true Sidekiq::ReliableFetch.setup_reliable_fetch!(config) end -::Sidekiq.strict_args!(false) +::Sidekiq.strict_args! module Dynflow module Executors diff --git a/lib/dynflow/executors/sidekiq/serialization.rb b/lib/dynflow/executors/sidekiq/serialization.rb index 9603dbb0..27b56aa9 100644 --- a/lib/dynflow/executors/sidekiq/serialization.rb +++ b/lib/dynflow/executors/sidekiq/serialization.rb @@ -6,18 +6,20 @@ module Sidekiq # Module to prepend the Sidekiq job to handle the serialization module Serialization def self.serialize(value) - Dynflow.serializer.dump(value) + JSON.dump(Dynflow.serializer.dump(value)) end def self.deserialize(value) - value = Utils::IndifferentHash.new(value) if value.is_a? Hash - Dynflow.serializer.load(value) + object = JSON.load(value) + object = Utils::IndifferentHash.new(object) if object.is_a? Hash + Dynflow.serializer.load(object) end module WorkerExtension # Overriding the Sidekiq entry method to perform additional serialization preparation module ClassMethods def client_push(opts) + opts = Utils::IndifferentHash.new(opts) opts['args'] = opts['args'].map { |a| Serialization.serialize(a) } super(opts) end diff --git a/lib/dynflow/serializer.rb b/lib/dynflow/serializer.rb index b243a242..95e6671c 100644 --- a/lib/dynflow/serializer.rb +++ b/lib/dynflow/serializer.rb @@ -24,6 +24,8 @@ def dump(object, options = {}) case object when ::Array object.collect { |v| dump(v) } + when ::Symbol + generate_other(object) else super end @@ -38,7 +40,9 @@ def parse_other(other, options = {}) end if (type_name = other[ARBITRARY_TYPE_KEY] || other[ARBITRARY_TYPE_KEY.to_s]) - if type_name == 'Time' && (time_str = other['value']) + if type_name == 'Symbol' && (val_str = other['value']) + return val_str.to_sym + elsif type_name == 'Time' && (time_str = other['value']) return Serializable.send(:string_to_time, time_str) end type = Utils.constantize(type_name) rescue nil @@ -53,6 +57,8 @@ def parse_other(other, options = {}) def generate_other(object, options = {}) hash = case + when object.is_a?(Symbol) + { ARBITRARY_TYPE_KEY => 'Symbol', 'value' => object.to_s } when object.respond_to?(:to_h) object.to_h when object.respond_to?(:to_hash) From a043bca70ead49d5c2956492828b6ddb0ba361df Mon Sep 17 00:00:00 2001 From: Adam Ruzicka Date: Tue, 18 Nov 2025 14:32:23 +0100 Subject: [PATCH 2/2] Dump and load during serialization This has two effects - it casts the input to only contain JSON-native objects and removes amiguity on the receiving end. With the previous implementation, we couldn't reliably distinguish whether what we're accessing was previously dumped into a json by dynflow or if it is a user-provided json-encoded string. --- lib/dynflow/executors/sidekiq/serialization.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/dynflow/executors/sidekiq/serialization.rb b/lib/dynflow/executors/sidekiq/serialization.rb index 27b56aa9..5c05c300 100644 --- a/lib/dynflow/executors/sidekiq/serialization.rb +++ b/lib/dynflow/executors/sidekiq/serialization.rb @@ -6,13 +6,12 @@ module Sidekiq # Module to prepend the Sidekiq job to handle the serialization module Serialization def self.serialize(value) - JSON.dump(Dynflow.serializer.dump(value)) + JSON.parse(JSON.dump(Dynflow.serializer.dump(value))) end def self.deserialize(value) - object = JSON.load(value) - object = Utils::IndifferentHash.new(object) if object.is_a? Hash - Dynflow.serializer.load(object) + value = Utils::IndifferentHash.new(value) if value.is_a? Hash + Dynflow.serializer.load(value) end module WorkerExtension