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

ActiveSupport::TimeWithZone serialization format in 5.x.x is not backward compatible with 4.x.x #26296

Closed
kwent opened this issue Aug 27, 2016 · 3 comments

Comments

@kwent
Copy link

kwent commented Aug 27, 2016

Expected behavior

We have multiple apps using rails 5.0.0.1 and rails 4.2.7.1 and sharing the same database.

5.0.0.1 is serializing time zone differently than 4.2.7.1 introduced by this commit.

Our app using rails 4.2.x.x are not able to deserialize the new YAML written by our rails 5.0.0.1 applications.

Would be nice if the next 4.2.x.x is able to deserialize the new format.

Backtrace

NoMethodError: undefined method `period_for_utc' for nil:NilClass
- 40 non-project frames
1
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.7.1/lib/active_support/time_with_zone.rb" line 67 in period
2
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.7.1/lib/active_support/time_with_zone.rb" line 53 in time
3
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.7.1/lib/active_support/time_with_zone.rb" line 365 in respond_to_missing?
4
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.7.1/lib/active_support/time_with_zone.rb" line 357 in respond_to?
5
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.7.1/lib/active_support/time_with_zone.rb" line 357 in respond_to?
6
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 364 in init_with
7
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 357 in revive
8
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 207 in visit_Psych_Nodes_Mapping
9
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 15 in visit
10
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 5 in accept
11
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 31 in accept
12
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 314 in block in register_empty
13
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 314 in each
14
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 314 in register_empty
15
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 140 in visit_Psych_Nodes_Sequence
16
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 15 in visit
17
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 5 in accept
18
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 31 in accept
19
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 321 in block in revive_hash
20
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 319 in each
21
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 319 in each_slice
22
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 319 in revive_hash
23
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 161 in visit_Psych_Nodes_Mapping
24
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 15 in visit
25
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 5 in accept
26
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 31 in accept
27
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 295 in visit_Psych_Nodes_Document
28
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 15 in visit
29
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/visitor.rb" line 5 in accept
30
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/visitors/to_ruby.rb" line 31 in accept
31
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych/nodes/node.rb" line 37 in to_ruby
32
File "/var/app/20160825T221638Z/vendor/ruby-2.2.4/lib/ruby/2.2.0/psych.rb" line 246 in load
33
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/coders/yaml_column.rb" line 26 in load
34
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/type/serialized.rb" line 19 in type_cast_from_database
35
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/attribute.rb" line 104 in type_cast
36
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/attribute.rb" line 42 in original_value
37
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/attribute.rb" line 37 in value
38
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/attribute_set.rb" line 31 in fetch_value
39
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/attribute_methods/read.rb" line 93 in _read_attribute
40
File "/var/app/20160825T221638Z/vendor/bundle/ruby/2.2.0/gems/activerecord-4.2.7.1/lib/active_record/attribute_methods.rb" line 50 in __temp__16574696475646f5368616e6765637

System configuration

Rails version: 4.2.7.1

Ruby version: 2.2.4

@maclover7
Copy link
Contributor

r? @pixeltrix

@pixeltrix
Copy link
Contributor

@kwent as you say your problem is that you've got AS::TWZ instances that are serialized by a Rails 5.0 application being read by a Rails 4.2 application as the other way round it's not a problem since they get read as standard Ruby UTC Time instances.

I think you're going to have to patch either your Rails 5.0 application or Rails 4.2 application to make the values compatible with each other. Fixing it so Rails 4.2 can read them might seem possible but you're still going to have issues because those instances will be serialized in the 4.2 format because we can't change how they're being written in a patch release.

Put the following code in an initializer in your Rails 4.2 applications and everything should work:

# https://github.com/rails/rails/issues/26296
ActiveSupport::TimeZone.class_eval do
  def init_with(coder) #:nodoc:
    initialize(coder['name'])
  end

  def encode_with(coder) #:nodoc:
    coder.tag ="!ruby/object:#{self.class}"
    coder.map = { 'name' => tzinfo.name }
  end
end

ActiveSupport::TimeWithZone.class_eval do
  def init_with(coder) #:nodoc:
    initialize(coder['utc'], coder['zone'], coder['time'])
  end

  def encode_with(coder) #:nodoc:
    coder.tag = '!ruby/object:ActiveSupport::TimeWithZone'
    coder.map = { 'utc' => utc, 'zone' => time_zone, 'time' => time }
  end
end

Usually in these kinds of situations we'll do forward migration, e.g. when we introduced JSON session serialization in 4.1 we offered a hybrid that would read existing serializations and convert them but not a backwards migration where a JSON session created in 4.1 would work with a 4.0 application.

HTH and sorry for any bother caused.

@kwent
Copy link
Author

kwent commented Aug 29, 2016

Well i did the opposite. I patched my 5.0.0.1 Rails app to write this object as Rails 4.2 would do by copying and add this file to my initializer. Works too !

https://github.com/rails/rails/blob/4-2-stable/activesupport/lib/active_support/time_with_zone.rb

Thanks for the feedback.

Regards,

pixeltrix added a commit that referenced this issue Nov 7, 2016
It is helpful to be able to run apps concurrently written
in successive versions of Rails to aid migration, e.g. run
Rails 4.2 and 5.0 variants of your application at the same
time to carry out A/B testing.

To do this serialization formats need to be cross compatible
and the change in 3aa26cf didn't meet this criteria because
the Psych loader checks for the existence of init_with before
setting the instance variables and the wrapping behavior of
ActiveSupport::TimeWithZone tries to see if the Time instance
responds to init_with before the @time variable is set.

To fix this we backported just the init_with behavior from
the change in 3aa26cf. If the revived instance is then
written out to YAML again it will revert to the default
Rails 4.2 behavior of converting it to a UTC timestamp string.

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

No branches or pull requests

3 participants