@@ -99,6 +99,7 @@ defmodule Indexer.Fetcher.EmptyBlocksSanitizer do
9999 { non_empty_blocks , empty_blocks } = classify_blocks_from_result ( result )
100100 process_non_empty_blocks ( non_empty_blocks )
101101 process_empty_blocks ( empty_blocks )
102+ process_missing_blocks ( unprocessed_empty_blocks_list , non_empty_blocks , empty_blocks )
102103
103104 Logger . info ( "Batch of empty blocks is sanitized" ,
104105 fetcher: :empty_blocks_to_refetch
@@ -114,18 +115,51 @@ defmodule Indexer.Fetcher.EmptyBlocksSanitizer do
114115 end
115116
116117 defp classify_blocks_from_result ( result ) do
117- result
118- |> Enum . reduce ( { [ ] , [ ] } , fn % { id: _id , result: block } , { non_empty_blocks , empty_blocks } ->
119- transactions = Map . get ( block , "transactions" ) || [ ]
120-
121- if Enum . empty? ( transactions ) do
122- { non_empty_blocks , [ block_fields ( block , transactions ) | empty_blocks ] }
123- else
124- { [ block_fields ( block , transactions ) | non_empty_blocks ] , empty_blocks }
125- end
118+ # A spec-compliant JSON-RPC server returns `result: null` for blocks it
119+ # cannot find (e.g. pruned or reorged). Skip those without crashing — the
120+ # caller reconciles which requested blocks are missing and flags them for
121+ # refetch.
122+ Enum . reduce ( result , { [ ] , [ ] } , fn
123+ % { id: _id , result: nil } , acc ->
124+ acc
125+
126+ % { id: _id , result: block } , { non_empty_blocks , empty_blocks } ->
127+ transactions = Map . get ( block , "transactions" ) || [ ]
128+
129+ if Enum . empty? ( transactions ) do
130+ { non_empty_blocks , [ block_fields ( block , transactions ) | empty_blocks ] }
131+ else
132+ { [ block_fields ( block , transactions ) | non_empty_blocks ] , empty_blocks }
133+ end
126134 end )
127135 end
128136
137+ # Blocks the RPC returned nil for stay in `is_empty: nil, refetch_needed: false`,
138+ # so without intervention the sanitizer's query would re-select them every cycle.
139+ # Flag them `refetch_needed: true` to remove them from the query set and let the
140+ # regular refetch path handle them.
141+ defp process_missing_blocks ( requested , non_empty_blocks , empty_blocks ) do
142+ returned = MapSet . new ( non_empty_blocks ++ empty_blocks , & & 1 . number )
143+
144+ missing =
145+ requested
146+ |> Enum . map ( & & 1 . number )
147+ |> Enum . reject ( & MapSet . member? ( returned , & 1 ) )
148+
149+ case missing do
150+ [ ] ->
151+ :ok
152+
153+ numbers ->
154+ Logger . warning (
155+ "JSON-RPC returned nil for block numbers #{ inspect ( numbers ) } ; marking as refetch_needed" ,
156+ fetcher: :empty_blocks_to_refetch
157+ )
158+
159+ Block . set_refetch_needed ( numbers )
160+ end
161+ end
162+
129163 defp block_fields ( block , transactions ) do
130164 % {
131165 number: quantity_to_integer ( block [ "number" ] ) ,
0 commit comments