1.7.1: Breakage with ActiveRecord serialization
Closed this issue · 14 comments
I have an AR model with a serialized attribute:
class TwitterAccount < ActiveRecord::Base
serialize :user_data, JSON
...
end
This works fine under Rails 3.2.12 using multi_json 1.6.1, but after upgrading to 1.7.1 I start seeing SystemStackError
s upon saving record instances, apparently from deep within AR's transaction error handling machinery. I initially encountered this error upon updating my gems for Rails 3.2.13, but have subsequently narrowed the issue down to multi_json.
Unfortunately the SystemStackErrors do not display a stacktrace, so I haven't succeeded in getting more detailed information about what is going wrong.
The user_data
is passed the attrs
of an object retrieved via the Twitter gem, and things work okay dumping that directly:
irb> JSON.dump(Twitter.user('pink').attrs)
=> "{\"id\":28706024,\"id_str\":\"28706024\",\"name\":\"P!nk\",\"screen_name\":\"Pink\",\"location\":\"los angeles\",\"description\":\"it's all happening\",\"url\":\"http://twitter.com/pink\",\"entities\":{\"url\":{\"urls\":[{\"url\":\"http://twitter.com/pink\",\"expanded_url\":null,\"indices\":[0,23]}]},\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":14080965,\"friends_count\":230,\"listed_count\":61769,\"created_at\":\"Sat Apr 04 01:16:34 +0000 2009\",\"favourites_count\":64,\"utc_offset\":-28800,\"time_zone\":\"Pacific Time (US & Canada)\",\"geo_enabled\":false,\"verified\":true,\"statuses_count\":4243,\"lang\":\"en\",\"status\":{\"created_at\":\"Tue Mar 19 02:34:32 +0000 2013\",\"id\":313840884678545408,\"id_str\":\"313840884678545408\",\"text\":\"“@ladybusiness: @Pink thought you might enjoy this... http://t.co/sow4lxgekZ” hahahaaha amazing. I catch like that sometimes\",\"source\":\"<a href=\\\"http://twitter.com/download/iphone\\\" rel=\\\"nofollow\\\">Twitter for iPhone</a>\",\"truncated\":false,\"in_reply_to_status_id\":313832518820438017,\"in_reply_to_status_id_str\":\"313832518820438017\",\"in_reply_to_user_id\":21823648,\"in_reply_to_user_id_str\":\"21823648\",\"in_reply_to_screen_name\":\"ladybusiness\",\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"retweet_count\":207,\"entities\":{\"hashtags\":[],\"urls\":[{\"url\":\"http://t.co/sow4lxgekZ\",\"expanded_url\":\"http://youtu.be/QYZfcFzcwxo\",\"display_url\":\"youtu.be/QYZfcFzcwxo\",\"indices\":[54,76]}],\"user_mentions\":[{\"screen_name\":\"ladybusiness\",\"name\":\"STVN MNZNO\",\"id\":21823648,\"id_str\":\"21823648\",\"indices\":[1,14]},{\"screen_name\":\"Pink\",\"name\":\"P!nk\",\"id\":28706024,\"id_str\":\"28706024\",\"indices\":[16,21]}]},\"favorited\":false,\"retweeted\":false,\"possibly_sensitive\":false,\"lang\":\"en\"},\"contributors_enabled\":false,\"is_translator\":false,\"profile_background_color\":\"DBE9ED\",\"profile_background_image_url\":\"http://a0.twimg.com/profile_background_images/178806023/grammys13.jpg\",\"profile_background_image_url_https\":\"https://si0.twimg.com/profile_background_images/178806023/grammys13.jpg\",\"profile_background_tile\":false,\"profile_image_url\":\"http://a0.twimg.com/profile_images/549375583/Pinkdeborahanderson_112_normal.jpg\",\"profile_image_url_https\":\"https://si0.twimg.com/profile_images/549375583/Pinkdeborahanderson_112_normal.jpg\",\"profile_link_color\":\"CC3366\",\"profile_sidebar_border_color\":\"DBE9ED\",\"profile_sidebar_fill_color\":\"E6F6F9\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"default_profile\":false,\"default_profile_image\":false,\"following\":true,\"follow_request_sent\":false,\"notifications\":false}"
So something subtle must be going on... any ideas?
-Steve
That's strange because rails use ActiveSupport::JSON
to serialize/deserialize data for models.
I couldn't reproduce your error using rails 3.2.13, MultiJson 1.7.1 and attributes from @pink
twitter user.
Can you provide more info? The data sample causes the error, the MultiJson adapter you use and the versions of related adapter gems.
Thanks for taking a look. I've been continuing to investigate, so give me a while to rule out clashes with other gems, and hopefully provide a minimal reproducible case.
(It might be worth noting that the database is postgresql, and the column type for user_data
is text
-- which DB did you test with?)
-Steve
I've tested with sqlite3 since it was minimal and easiest test-case I could build.
Hmm, I can reproduce this trivially, even without touching the database. Starting with ruby 1.9.3 and Rails 3.2.13:
rails new foobar
cd foobar
echo 'gem "twitter"' >> Gemfile
bundler install
Then create a config/initializers/twitter.rb
containing something like this:
Twitter.configure do |config|
config.consumer_key = 'REPLACEME'
config.consumer_secret = 'REPLACEME'
config.oauth_token = "REPLACEME"
config.oauth_token_secret = "REPLACEME"
end
Finally:
% rails console
irb(main):010:0> JSON.dump(Twitter.user('pink').attrs)
SystemStackError: stack level too deep
from /Users/steve/.rbenv/versions/1.9.3-p327/lib/ruby/1.9.1/irb/workspace.rb:80
Maybe IRB bug!
Why are you doing JSON.dump
? If it's MultiJson bug, you should do MultiJson.dump
instead.
Ah, am I wrong in thinking that JSON is wrapping MultiJson in this case?
No. In that case JSON
is not wrapping anything. In case of the model, JSON
is actually ActiveSupport::JSON
, which partly wraps MultiJson
Yes, it's complicated :)
Same issue, in fact, even with multi_json 1.6.1:
irb(main):009:0> MultiJson.dump(Twitter.user('pink').attrs)
SystemStackError: stack level too deep
from /Users/steve/.rbenv/versions/1.9.3-p327/lib/ruby/1.9.1/irb/workspace.rb:80
Maybe IRB bug!
I'm thinking that Twitter might have just changed the structure of their responses, and so the Twitter gem objects have become unserializable...
Twitter.user('pink').attrs
should be a Hash, so it is serializable.
Ok, now what if you add json
gem into your Gemfile and run bundle install && bundle update json
Also, try ActiveSupport::JSON.encode(Twitter.user('pink').attrs)
Well I just tried a plain irb session, and loaded the 'multi_json' and 'twitter' gems manually: the result is that it works!
Can we then conclude that some Rails magic is at fault?
(BTW, the .attrs
expression doesn't yield a plain Hash: one of its attributes is a #<Twitter::Tweet:0x007f86e3827e60 ...
)
Looks like there's a corresponding twitter gem issue: sferik/twitter-ruby#368
I'll close this because there's now no clear indication that multi_json is at fault.
Thanks so much for your time and trouble helping me with this.
-Steve
Oh, I've used Twitter gem version 4.5.0 in my test and it actually outputs a simple Hash
Yeah, when I updated the gems for Rails 3.2.13, the new Twitter gem got pulled in too, then I failed to notice that in my testing. Feeling pretty stupid for not eliminating that sooner. Thanks again. :-)