Siyao Meng created HDDS-17065:
---------------------------------
Summary: Recon ContainerSizeCountTask stops for good after one
failed run, freezing the container size distribution until restart
Key: HDDS-17065
URL: https://issues.apache.org/jira/browse/HDDS-17065
Project: Apache Ozone
Issue Type: Bug
Reporter: Siyao Meng
Attachments:
CR-5-keep-container-size-count-task-running-and-recount-after-failed-run.patch,
TestBugCR5ContainerSizeCountTaskStopsAfterFailedRun.java
h3. Mechanism
{{ContainerSizeCountTask.run}} has its {{catch (Throwable)}} outside the
{{while (canRun())}} loop, and {{ReconScmTask.initializeAndRunTask}} passes
every exception up. When one run fails, the thread logs "Exception in Container
Size Distribution task Thread." at DEBUG and ends. The task's running flag
stays true, so {{isRunning()}} keeps reporting the task as running and
{{start()}} would not start it again; nothing else does either.
What fails in practice is the Recon SQL database: the {{truncate}} in
{{runTask}}, or {{findById}}, {{insert}} or {{update}} in {{writeCountsToDB}},
all of which throw a jOOQ {{DataAccessException}} when the database rejects the
call. Listing the containers is in memory, and errors for a single container
are already caught per container.
Letting the loop simply continue is not enough. {{processContainers}} updates
its in memory {{processedContainers}} before {{writeCountsToDB}}, and that
write is an insert followed by a separate update. After a failed write the next
run computes its delta from the already updated map, so the failed change is
never applied; if the insert went through and the update did not, the table
also keeps half of the change. Either way the counts stay wrong until Recon
restarts, which recounts from scratch.
h3. Trigger
Any failure of the Recon SQL database during one run of the task (default every
60s, {{ozone.recon.task.containercounttask.interval}}), for example an outage
of an external database configured with {{ozone.recon.sql.db.jdbc.url}}, or a
full disk under the embedded Derby database.
h3. Impact
* After the thread has ended, {{GET /api/v1/utilization/containerCount}} and
the "Container Size Distribution" chart on Recon's Insights page keep showing
the counts of the last successful run until Recon restarts. If the failed run
was the first one after startup, they show nothing, or the rows the previous
Recon process left, since the table persists across restarts. The task logs the
failure only at DEBUG. In a real database outage {{ReconTaskStatusUpdater}}
also logs an ERROR "Failed to update table for task" without the cause, but
nothing reports that the thread has ended. {{/api/v1/task/status}} shows the
task's last run as failed, with a timestamp that no longer advances.
* Only this statistic is affected; the endpoint is its only reader. SCM,
datanodes and data are not affected.
h3. Reproduction
[^TestBugCR5ContainerSizeCountTaskStopsAfterFailedRun.java] goes in
{{hadoop-ozone/recon}} (target path and Maven command in the class comment) and
passes on unmodified source when the bug is present. It runs the real
{{ContainerSizeCountTask}} on Recon's real SQL schema (embedded Derby) and
reads the result through the real {{UtilizationEndpoint}}; the container
manager is a mock and the DAO is a spy of the real DAO whose first write throws
{{DataAccessException}}.
* {{oneFailedRunStopsTheTask}} (200ms interval): the first run fails, then the
cluster gains a second container. After 12 intervals the task had listed the
containers once, the endpoint returned no rows for two containers,
{{isRunning()}} returned true, and the task logged nothing at INFO or above
(the failure is injected at the size count DAO only, so the task status writes
succeed).
* {{retryAfterPartialWriteKeepsWrongCounts}} shows why a plain retry is not
enough: runs are called directly, a container moves from the 2GB bin to the 4GB
bin, the new 4GB row is inserted and the update of the 2GB row fails. After the
next successful run the endpoint still returns one container in each bin for a
single 2.5GB container.
What was not exercised: a real outage of an external database or a full disk;
the failure is injected at the DAO.
h3. Patch
[^CR-5-keep-container-size-count-task-running-and-recount-after-failed-run.patch],
against 916aa1d9949cbb10f2750a40c8704340893131a2. It also applies to master at
14e0611fdd5.
{{ContainerSizeCountTask.run}} now catches a failed run inside the loop, logs
it at ERROR, records the failed run in the task status and waits the interval
before the next run. {{processContainers}} clears {{processedContainers}} when
the run fails, so the next run truncates the table and recounts all containers,
as Recon does at startup. A recount is used instead of rolling back the in
memory state because the table may already hold part of the failed write. The
recount costs what Recon already does on its first run after startup: a
truncate and one row per non empty size bin, on top of the pass over the
container list that every run makes. A reader may see an empty table for that
moment, as after a restart.
Covered by {{TestContainerSizeCountTask.testTaskKeepsRunningAfterFailedRun}}
(the first write fails, the container must still reach the table) and
{{TestContainerSizeCountTask.testCountsRecoverAfterFailedWrite}} (after a
partial write and one more run only the new bin may be counted). Without the
change the first times out and the second sees both bins. With it the size
count, endpoint, SCM sync helper and SCM facade tests pass, checkstyle is
clean, and the attached test no longer shows the bug.
The same death pattern in {{PipelineSyncTask}} is a separate issue with its own
patch; the two patches touch different files and apply together.
Found by code review of the datanode container report and Recon container
tracking paths, as part of the TLA+ verification effort under HDDS-15926, on
commit 916aa1d9949cbb10f2750a40c8704340893131a2. Checked against HDDS issues
and apache/ozone pull requests for duplicates before filing. Related but not
duplicates: HDDS-12227 (resolved, lowered this log line to DEBUG), HDDS-10293
(resolved, negative container size, caught per container), HDDS-7632 (resolved,
added the task). The attached patch is a proposal for review. Generated with
Specula (Claude Opus 5.5).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]