Skip to content

Commit 64a8b2b

Browse files
committed
fix(indexer): keep one flush timer per BufferedTask, recheck missing hot-contract days daily
BufferedTask: every idle re-check started new flush timer and overwrote stored reference without stopping old timer, so timers piled up with uptime (idle explorer at ~1.1 cores after 42 days, 0.04 after restart). Cancel stored timer before starting new one. Same cancel-then-reschedule order as Broadway RateLimiter. Hot contracts: day without indexed blocks no longer retried every 10 s with error log. Info log, recheck next UTC day within existing 30-day window, daily sequence continues. Other errors keep 10 s retry. Regression tests: existing flush test starts with live timer and checks cancellation; new hot-contracts test checks info log, no error. Upstream-ready branches on blockscout/blockscout 6275a9c: fix/buffered-task-single-flush-timer (fa32904a9, e8aef41ab), fix/hot-contracts-empty-day-retry (890008a67, 9f923a724).
1 parent 232da46 commit 64a8b2b

4 files changed

Lines changed: 35 additions & 3 deletions

File tree

‎apps/indexer/lib/indexer/buffered_task.ex‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,12 @@ defmodule Indexer.BufferedTask do
12851285
if state.flush_interval == :infinity do
12861286
state
12871287
else
1288+
# Keep one flush timer per worker. Overwriting a live reference leaves
1289+
# the previous timer running and starts another repeating flush chain.
1290+
if is_reference(state.flush_timer) do
1291+
Process.cancel_timer(state.flush_timer)
1292+
end
1293+
12881294
timer = Process.send_after(self(), :flush, state.flush_interval)
12891295
%__MODULE__{state | flush_timer: timer}
12901296
end

‎apps/indexer/lib/indexer/fetcher/stats/hot_smart_contracts.ex‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ defmodule Indexer.Fetcher.Stats.HotSmartContracts do
4444

4545
Logger.info("Hot contracts fetched for #{date}")
4646
else
47+
{:error, {:error, :not_found}} ->
48+
if Date.diff(Date.utc_today(), date) < @max_days_ago do
49+
schedule_message_on_the_next_day({:fetch_for_date, date, false})
50+
end
51+
52+
if new_day? do
53+
schedule_next_day_fetch()
54+
end
55+
56+
Logger.info("No indexed blocks for hot contracts on #{date}")
57+
4758
{:error, error} ->
4859
Process.send_after(self(), {:fetch_for_date, date, new_day?}, @retry_interval)
4960
Logger.error("Error fetching hot contracts for #{date}: #{inspect(error)}")

‎apps/indexer/test/indexer/buffered_task_test.exs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ defmodule Indexer.BufferedTaskTest do
239239

240240
test "with 0 size without maximum size schedules next flush and hibernates" do
241241
bound_queue = %BoundQueue{}
242+
old_timer = Process.send_after(self(), :flush, :timer.minutes(5))
242243

243244
refute BoundQueue.shrunk?(bound_queue)
244245

@@ -249,15 +250,16 @@ defmodule Indexer.BufferedTaskTest do
249250
callback_module: ShrinkableTask,
250251
callback_module_state: nil,
251252
bound_queue: bound_queue,
252-
flush_interval: 50,
253-
flush_timer: nil,
253+
flush_interval: :timer.minutes(5),
254+
flush_timer: old_timer,
254255
task_supervisor: BufferedTaskSup,
255256
max_batch_size: 1,
256257
max_concurrency: 1,
257258
poll: false
258259
})
259260

260-
refute flush_timer == nil
261+
assert Process.read_timer(old_timer) == false
262+
assert is_integer(Process.read_timer(flush_timer))
261263
end
262264

263265
test "with 0 size with maximum size calls init/2 to get work that was shed before scheduling next flush and hibernates" do

‎apps/indexer/test/indexer/fetcher/stats/hot_smart_contracts_test.exs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,19 @@ defmodule Indexer.Fetcher.Stats.HotSmartContractsTest do
356356
end
357357
end
358358

359+
test "logs an unavailable date without an error" do
360+
insert(:block, number: 0, timestamp: DateTime.add(DateTime.utc_now(), -40, :day))
361+
date = Date.add(Date.utc_today(), -1)
362+
363+
log =
364+
capture_log([level: :info], fn ->
365+
assert {:noreply, :ok} = HotSmartContracts.handle_info({:fetch_for_date, date, false}, :ok)
366+
end)
367+
368+
assert log =~ "No indexed blocks for hot contracts on #{date}"
369+
refute log =~ "Error fetching hot contracts"
370+
end
371+
359372
describe "handle_continue/2" do
360373
test "processes chain age check on startup" do
361374
# Create blocks older than 30 days

0 commit comments

Comments
 (0)