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

Eliminate protocol Jason.Encoder not implemented for... error #3290

Merged
merged 2 commits into from
Sep 10, 2020

Conversation

vbaranov
Copy link
Member

@vbaranov vbaranov commented Sep 10, 2020

Motivation

The errors of the next types periodically appear:
1.

2020-09-10T09:28:06.463 [error] GenServer #PID<0.2423.0> terminating
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for %Explorer.ExchangeRates.Token{available_supply: #Decimal<279261188.720888>, btc_value: #Decimal<0.000002396024029452239812522812910>, id: "poa-network", last_updated: ~U[2020-09-10 09:24:20.408Z], market_cap_usd: #Decimal<6824341>, name: "POA Network", symbol: "POA", total_supply: #Decimal<279261188.720888>, usd_value: #Decimal<0.02459655>, volume_24h_usd: #Decimal<551215>} of type Explorer.ExchangeRates.Token (a struct), Jason.Encoder protocol must always be explicitly implemented.

If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:

    @derive {Jason.Encoder, only: [....]}
    defstruct ...

It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:

    @derive Jason.Encoder
    defstruct ...

Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:

    Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
    Protocol.derive(Jason.Encoder, NameOfTheStruct)
. This protocol is implemented for the following type(s): BlockScoutWeb.API.RPC.EthRPCView, Explorer.Chain.BridgedToken, Explorer.Chain.Token, Explorer.Chain.Transaction, Explorer.Chain.Hash, Explorer.Chain.DecompiledSmartContract, Explorer.Chain.Address, Explorer.Chain.Data, Cldr.LanguageTag, Ecto.Schema.Metadata, Ecto.Association.NotLoaded, Any, Atom, BitString, Date, DateTime, Decimal, Float, Integer, Jason.Fragment, List, Map, NaiveDateTime, Time
    (jason 1.2.1) lib/jason.ex:199: Jason.encode_to_iodata!/2
    (phoenix 1.5.4) lib/phoenix/socket/serializers/v2_json_serializer.ex:28: Phoenix.Socket.V2.JSONSerializer.encode!/1
    (phoenix 1.5.4) lib/phoenix/channel/server.ex:241: Phoenix.Channel.Server.push/5
    (block_scout_web 0.0.1) lib/block_scout_web/channels/exchange_rate_channel.ex:14: BlockScoutWeb.ExchangeRateChannel.handle_out/3
    (phoenix 1.5.4) lib/phoenix/channel/server.ex:328: Phoenix.Channel.Server.handle_info/2
    (stdlib 3.9) gen_server.erl:637: :gen_server.try_dispatch/4
    (stdlib 3.9) gen_server.erl:711: :gen_server.handle_msg/6
    (stdlib 3.9) proc_lib.erl:259: :proc_lib.wake_up/3
Last message: %Phoenix.Socket.Broadcast{event: "new_rate", payload: %{exchange_rate: %Explorer.ExchangeRates.Token{available_supply: #Decimal<279261188.720888>, btc_value: #Decimal<0.000002396024029452239812522812910>, id: "poa-network", last_updated: ~U[2020-09-10 09:24:20.408Z], market_cap_usd: #Decimal<6824341>, name: "POA Network", symbol: "POA", total_supply: #Decimal<279261188.720888>, usd_value: #Decimal<0.02459655>, volume_24h_usd: #Decimal<551215>}, market_history_data: [%{closing_price: #Decimal<0.02459655>, date: ~D[2020-09-10]}, %{closing_price: #Decimal<0.0215>, date: ~D[2020-09-09]}, %{closing_price: #Decimal<0.02262>, date: ~D[2020-09-08]}, %{closing_price: #Decimal<0.0238>, date: ~D[2020-09-07]}, %{closing_price: #Decimal<0.02135>, date: ~D[2020-09-06]}, %{closing_price: #Decimal<0.02648>, date: ~D[2020-09-05]}, %{closing_price: #Decimal<0.02573>, date: ~D[2020-09-04]}, %{closing_price: #Decimal<0.03358>, date: ~D[2020-09-03]}, %{closing_price: #Decimal<0.03788>, date: ~D[2020-09-02]}, %{closing_price: #Decimal<0.04218>, date: ~D[2020-09-01]}, %{closing_price: #Decimal<0.04296>, date: ~D[2020-08-31]}, %{closing_price: #Decimal<0.04308>, date: ~D[2020-08-30]}, %{closing_price: #Decimal<0.04633>, date: ~D[2020-08-29]}, %{closing_price: #Decimal<0.04383>, date: ~D[2020-08-28]}, %{closing_price: #Decimal<0.05018>, date: ~D[2020-08-27]}, %{closing_price: #Decimal<0.03881>, date: ~D[2020-08-26]}, %{closing_price: #Decimal<0.04733>, date: ~D[2020-08-25]}, %{closing_price: #Decimal<0.04384>, date: ~D[2020-08-24]}, %{closing_price: #Decimal<0.05309>, date: ~D[2020-08-23]}, %{closing_price: #Decimal<0.04946>, date: ~D[2020-08-22]}, %{closing_price: #Decimal<0.06786>, date: ~D[2020-08-21]}, %{closing_price: #Decimal<0.06792>, date: ~D[2020-08-20]}, %{closing_price: #Decimal<0.05675>, date: ~D[2020-08-19]}, %{closing_price: #Decimal<0.06902>, date: ~D[2020-08-18]}, %{closing_price: #Decimal<0.03638>, date: ~D[2020-08-17]}, %{closing_price: #Decimal<0.03464>, date: ~D[2020-08-16]}, %{closing_price: #Decimal<0.0366>, date: ~D[2020-08-15]}, %{closing_price: #Decimal<0.02767>, date: ~D[2020-08-14]}, %{closing_price: #Decimal<0.02493>, date: ~D[2020-08-13]}, %{closing_price: #Decimal<0.02109>, date: ~D[2020-08-12]}]}, topic: "exchange_rate:new_rate"}
State: %Phoenix.Socket{assigns: %{locale: "en"}, channel: BlockScoutWeb.ExchangeRateChannel, channel_pid: #PID<0.2423.0>, endpoint: BlockScoutWeb.Endpoint, handler: BlockScoutWeb.UserSocket, id: nil, join_ref: "6", joined: true, private: %{log_handle_in: :debug, log_join: :info}, pubsub_server: BlockScoutWeb.PubSub, ref: nil, serializer: Phoenix.Socket.V2.JSONSerializer, topic: "exchange_rate:new_rate", transport: :websocket, transport_pid: #PID<0.2417.0>}
2020-09-10T09:30:07.239 [error] GenServer BlockScoutWeb.RealtimeEventHandler terminating
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for %{__struct__: Explorer.Chain.Transaction.History.TransactionStats, date: ~D[2020-09-10], id: 4436, number_of_transactions: 6505} of type Explorer.Chain.Transaction.History.TransactionStats (a struct), Jason.Encoder protocol must always be explicitly implemented.

If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:

    @derive {Jason.Encoder, only: [....]}
    defstruct ...

It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:

    @derive Jason.Encoder
    defstruct ...

Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:

    Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
    Protocol.derive(Jason.Encoder, NameOfTheStruct)
. This protocol is implemented for the following type(s): BlockScoutWeb.API.RPC.EthRPCView, Explorer.Chain.BridgedToken, Explorer.Chain.Token, Explorer.Chain.Transaction, Explorer.Chain.Hash, Explorer.Chain.DecompiledSmartContract, Explorer.Chain.Address, Explorer.Chain.Data, Cldr.LanguageTag, Ecto.Schema.Metadata, Ecto.Association.NotLoaded, Any, Atom, BitString, Date, DateTime, Decimal, Float, Integer, Jason.Fragment, List, Map, NaiveDateTime, Time
    (jason 1.2.1) lib/jason.ex:199: Jason.encode_to_iodata!/2
    (phoenix 1.5.4) lib/phoenix/socket/serializers/v2_json_serializer.ex:9: Phoenix.Socket.V2.JSONSerializer.fastlane!/1
    (phoenix 1.5.4) lib/phoenix/channel/server.ex:102: anonymous fn/5 in Phoenix.Channel.Server.dispatch/3
    (elixir 1.10.3) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix 1.5.4) lib/phoenix/channel/server.ex:87: Phoenix.Channel.Server.dispatch/3
    (elixir 1.10.3) lib/registry.ex:468: Registry.dispatch/4
    (phoenix_pubsub 2.0.0) lib/phoenix/pubsub.ex:286: Phoenix.PubSub.dispatch/5
    (block_scout_web 0.0.1) lib/block_scout_web/realtime_event_handler.ex:35: BlockScoutWeb.RealtimeEventHandler.handle_info/2
Last message: {:chain_event, :transaction_stats}
State: []
2020-09-10T12:24:35.253 [error] GenServer BlockScoutWeb.RealtimeEventHandler terminating
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for #Explorer.Chain.Wei<0> of type Explorer.Chain.Wei (a struct), Jason.Encoder protocol must always be explicitly implemented.

If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:

    @derive {Jason.Encoder, only: [....]}
    defstruct ...

It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:

    @derive Jason.Encoder
    defstruct ...

Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:

    Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
    Protocol.derive(Jason.Encoder, NameOfTheStruct)
. This protocol is implemented for the following type(s): BlockScoutWeb.API.RPC.EthRPCView, Explorer.ExchangeRates.Token, Explorer.Chain.Transaction, Explorer.Chain.Transaction.History.TransactionStats, Explorer.Chain.BridgedToken, Explorer.Chain.Hash, Explorer.Chain.Data, Explorer.Chain.DecompiledSmartContract, Explorer.Chain.Address, Explorer.Chain.Token, Cldr.LanguageTag, Ecto.Association.NotLoaded, Ecto.Schema.Metadata, Jason.Fragment, List, Map, DateTime, Any, Decimal, Date, Float, Time, NaiveDateTime, Atom, Integer, BitString
    (jason 1.2.1) lib/jason.ex:199: Jason.encode_to_iodata!/2
    (phoenix 1.5.4) lib/phoenix/socket/serializers/v2_json_serializer.ex:9: Phoenix.Socket.V2.JSONSerializer.fastlane!/1
    (phoenix 1.5.4) lib/phoenix/channel/server.ex:102: anonymous fn/5 in Phoenix.Channel.Server.dispatch/3
    (elixir 1.10.3) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix 1.5.4) lib/phoenix/channel/server.ex:87: Phoenix.Channel.Server.dispatch/3
    (elixir 1.10.3) lib/registry.ex:493: Registry.dispatch_serial/4
    (elixir 1.10.3) lib/registry.ex:476: Registry.dispatch/4
    (phoenix_pubsub 2.0.0) lib/phoenix/pubsub.ex:286: Phoenix.PubSub.dispatch/5
Last message: {:chain_event, :transactions, :realtime, [%Explorer.Chain.Transaction{earliest_processing_start: ~U[2020-09-10 12:24:34.244928Z], inserted_at: ~U[2020-09-10 12:24:34.245665Z], cumulative_gas_used: nil, hash: %Explorer.Chain.Hash{byte_count: 32, bytes: <<11, 31, 192, 152, 111, 20, 91, 248, 185, 220, 193, 31, 136, 52, 148, 141, 231, 106, 87, 175, 67, 37, 131, 250, 80, 16, 237, 88, 211, 91, 226, 15>>}, r: #Decimal<109707930281350757237995081842377944010502090433086538622625154544755721418955>, __meta__: #Ecto.Schema.Metadata<:loaded, "transactions">, status: nil, created_contract_code_indexed_at: nil, block_hash: nil, gas_used: nil, uncles: #Ecto.Association.NotLoaded<association :uncles is not loaded>, internal_transactions: #Ecto.Association.NotLoaded<association :internal_transactions is not loaded>, index: nil, to_address: #Ecto.Association.NotLoaded<association :to_address is not loaded>, gas: #Decimal<200000>, to_address_hash: %Explorer.Chain.Hash{byte_count: 20, bytes: <<60, 212, 180, 217, 125, 202, 212, 238, 119, 43, 196, 240, 251, 14, 118, 5, 252, 134, 168, 91>>}, v: #Decimal<236>, created_contract_address: #Ecto.Association.NotLoaded<association :created_contract_address is not loaded>, gas_price: #Explorer.Chain.Wei<1000000000>, s: #Decimal<50373490975905187701609483132229362331749212624285429008801877903283509287232>, error: nil, token_transfers: #Ecto.Association.NotLoaded<association :token_transfers is not loaded>, created_contract_address_hash: nil, nonce: 724, from_address_hash: %Explorer.Chain.Hash{byte_count: 20, bytes: <<3, 228, 1, 58, 144, 47, 200, 180, 204, 131, 234, 65, 206, 61, 149, 13, 61, 216, ...>>}, revert_reason: nil, logs: #Ecto.Association.NotLoaded<association :logs is not loaded>, forks: #Ecto.Association.NotLoaded<association :forks is not loaded>, input: %Explorer.Chain.Data{bytes: <<169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...>>}, block_number: nil, updated_at: ~U[2020-09-10 12:24:34.245665Z], block: #Ecto.Association.NotLoaded<association :block is not loaded>, value: #Explorer.Chain.Wei<0>, from_address: #Ecto.Association.NotLoaded<association :from_address is not loaded>, old_block_hash: nil}]}
State: []v

Changelog

Explicitly enable Jason encoding for mentioned schemas in the errors:

  1. Explorer.Chain.Transaction.History.TransactionStats
  2. Explorer.ExchangeRates.Token
  3. Explorer.Chain.Wei

Upgrading

If you have any Incompatible Changes in the above Changelog, outline how users of prior versions can upgrade once this PR lands or when reviewers are testing locally. A common upgrading step is "Database reset and re-index required".

Checklist for your Pull Request (PR)

@coveralls
Copy link

coveralls commented Sep 10, 2020

Pull Request Test Coverage Report for Build dbe6856272d60afd146518174e6bae7fc5074ba9-PR-3290

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+20.7%) to 75.876%

Totals Coverage Status
Change from base Build 7a67da2968faecf3024f06d55aac0171c839f5a3: 20.7%
Covered Lines: 1928
Relevant Lines: 2541

💛 - Coveralls

@vbaranov vbaranov merged commit d75aab8 into master Sep 10, 2020
@vbaranov vbaranov deleted the vb-jason-error branch September 10, 2020 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants