RailsEventStore / rails_event_store

A Ruby implementation of an Event Store based on Active Record
http://railseventstore.org
MIT License
1.42k stars 122 forks source link

Server error, please check backend logs for details #1829

Closed casiodk closed 2 weeks ago

casiodk commented 3 weeks ago

I suddenly started getting this error in production - but only on the index page, if I go on a specific stream it works as intended.

Screenshot 2024-11-08 at 22 22 38

When I go to the specific event that is breaking the screen looks like this

Screenshot 2024-11-08 at 22 38 32

And the event look like this

=> #<Etfs::SIX::EtfBidUpdated:0x00007f48a03917d8
 @data={:etf_id=>1, :bid=>117.595, :previous_bid=>0.0, :bid_change_percentage=>Infinity},
 @event_id="xxx",
 @metadata=
  #<RubyEventStore::Metadata:0x00007f48a03917b0
   @h=
    {:locale=>"da",
     :event_version=>1,
     :correlation_id=>"xxx",
     :timestamp=>2024-11-08 22:00:48.014565 +0100,
     :valid_at=>2024-11-08 22:00:48.014565 +0100}>>

So I am guessing that the Infinity value in the data is breaking things?

lukaszreszke commented 2 weeks ago

Hi @casiodk. Thanks for reporting. We'll take a look at this

mostlyobvious commented 2 weeks ago

What's the Infinity that you're using here? It is not a Float::INIFINITY for sure.

irb(main):009:0> JSON.dump(Float::INFINITY)
=> "Infinity"

The problem lies within serializing event data to JSON for the Browser. If that's a custom type/ValueObject used in data, JSON won't be able to process it without additional mapping.

casiodk commented 2 weeks ago

I am using the YAML serializer, so it should be able to deserialize right?

Rails.configuration.to_prepare do
  Rails.configuration.key_repository = EncryptionKeyRepository.new
  Rails.configuration.event_store = RailsEventStore::Client.new(
    mapper: RubyEventStore::Mappers::EncryptionMapper.new(Rails.configuration.key_repository, serializer: RubyEventStore::Serializers::YAML),
    repository: RailsEventStoreActiveRecord::EventRepository.new(serializer: RubyEventStore::Serializers::YAML),
    dispatcher: RubyEventStore::ComposedDispatcher.new(
      RailsEventStore::AfterCommitAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: RubyEventStore::Serializers::YAML)),
      RubyEventStore::Dispatcher.new
    ),
    request_metadata: lambda do |env|
      request = ActionDispatch::Request.new(env)

      {
        request_uuid: request.uuid
      }
    end
  )

  EventSubscribers::Base.call
end
casiodk commented 2 weeks ago

I am also able to deserialize in the console without any problems

PRODUCTION [2] pry(main)> event = EventStore.find_event("b3fcc70a-7148-417a-9600-a4ab890c6a0b")
D, [2024-11-14T09:50:53.683378 #27432] DEBUG -- :   RubyEventStore::ActiveRecord::Event Load (0.7ms)  SELECT "event_store_events".* FROM "event_store_events" WHERE "event_store_events"."event_id" = $1 ORDER BY "event_store_events"."id" ASC LIMIT $2  [["event_id", "b3fcc70a-7148-417a-9600-a4ab890c6a0b"], ["LIMIT", 1]]
=> #<Etfs::SIX::EtfBidUpdated:0x00007fb4c115fc98
 @data={:etf_id=>1, :bid=>120.595, :previous_bid=>0.0, :bid_change_percentage=>Infinity},
 @event_id="b3fcc70a-7148-417a-9600-a4ab890c6a0b",
 @metadata=
  #<RubyEventStore::Metadata:0x00007fb4c115fb58
   @h={:locale=>"da", :event_version=>1, :correlation_id=>"4abe1ad7-5b83-4feb-a743-d6b044e114d2", :timestamp=>2024-11-08 22:00:48.014565 +0100, :valid_at=>2024-11-08 22:00:48.014565 +0100}>>
PRODUCTION [3] pry(main)> event.data[:bid_change_percentage]
=> Infinity
PRODUCTION [4] pry(main)> event.data[:bid_change_percentage].class
=> Float
fidel commented 2 weeks ago

Hello @casiodk, I had some time yesterday to investigate this scenario. While JSON.dump(1.0/0) dumps Float::INFINITY to "Infinity" string, it doesn't happen when it's a value in a hash:

irb(main):003> JSON.dump(1.0/0)
=> "Infinity"
irb(main):004> JSON.dump({ infinity: 1.0/0 })
=> "{\"infinity\":Infinity}"

In general, this behavior doesn't follow ECMA-404 nor RFC 8259 as

Numeric values that cannot be represented as sequences of digits (such as Infinity and NaN) are not permitted.

Infinity is obviously not an "Infinity" and JSON parser in our browser's frontend can't handle this as it not a correct JSON value. Fun fact, some tools actually parse that, but there's no standarized behavior

echo "{\"infinity\":Infinity}" | jq
{
  "infinity": 1.7976931348623157e+308
}

It also differs on which method from Ruby's JSON module you'll use:

irb(main):003> JSON.parse('[Infinity]')
/Users/fidel/.gem/ruby/3.3.6/gems/json-2.8.1/lib/json/common.rb:204:in `parse': unexpected token at 'Infinity]' (JSON::ParserError)
    from /Users/fidel/.gem/ruby/3.3.6/gems/json-2.8.1/lib/json/common.rb:204:in `parse'
    from (irb):3:in `<main>'
    from <internal:kernel>:187:in `loop'
    from /Users/fidel/.gem/ruby/3.3.6/gems/irb-1.14.1/exe/irb:9:in `<top (required)>'
    from /Users/fidel/.gem/ruby/3.3.6/bin/irb:25:in `load'
    from /Users/fidel/.gem/ruby/3.3.6/bin/irb:25:in `<main>'
irb(main):004> JSON.load('[Infinity]')
=> [Infinity]
irb(main):005> JSON.parse('[Infinity]', allow_nan: true)
=> [Infinity]

We'll try to fix this issue on our end.

fidel commented 2 weeks ago

@casiodk issue has been resolved in https://github.com/RailsEventStore/rails_event_store/commit/a2885d4f2ea630f635b33b1f87bf30491c39857b, not sure when we'll make a release, so you can stick to this particular ref in your Gemfile.

casiodk commented 2 weeks ago

Awesome, thanks a lot guys!!

fidel commented 2 weeks ago

You're welcome, at least we've learned something new 😉

casiodk commented 2 weeks ago

I tried to reference, but now I am getting this error and a blank page

Uncaught ReferenceError: Elm is not defined at bootstrap.js:5:13

casiodk commented 2 weeks ago

gem "rails_event_store", git: "https://github.com/RailsEventStore/rails_event_store", ref: "a2885d4f2ea630f635b33b1f87bf30491c39857b"

fidel commented 2 weeks ago

Yes, sorry, I forgot that we don't build js for every ref. This one https://github.com/RailsEventStore/rails_event_store/commit/8f108b278a7f5a9a17b7a63655bdfce9c976d82b will work

casiodk commented 2 weeks ago

Awesome! it works :)