Siyao Meng created HDDS-17389:
---------------------------------

             Summary: Recon export cleanup deletes every directory and .tar 
file in ozone.recon.export.directory, not only files created by exports
                 Key: HDDS-17389
                 URL: https://issues.apache.org/jira/browse/HDDS-17389
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: CR-1-export-startup-cleanup-only-owned-entries.patch, 
TestBugCR1ExportDirSweep.java

h3. Mechanism
The {{ExportJobManager}} constructor removes leftovers of earlier exports from 
{{ozone.recon.export.directory}}. It lists the directory and calls 
{{FileUtils.deleteQuietly}} (recursive) on every subdirectory and on every file 
ending in {{.tar}}, without looking at the name. The manager itself only 
creates {{<jobId>}} working directories (a random UUID) and 
{{export_<state>_<millis>.tar}} files, so everything else it deletes was put 
there by someone else.

The configured value is used as is. Nothing checks that the directory is 
dedicated to exports or differs from {{ozone.recon.db.dir}}, and neither 
{{ozone-default.xml}} nor {{ReconServerConfigKeys}} says that its contents are 
removed. SCM's container ID export ({{ExportFileManager}}) clears its export 
directory on startup more narrowly: it removes only {{export_<UUID>}} 
directories and {{*.tar.gz.tmp}} files.

h3. Trigger
# Set {{ozone.recon.export.directory}} to a directory that holds other data, 
for example the Recon DB directory itself or a shared scratch directory.
# Start Recon. The cleanup runs when the {{ExportJobManager}} singleton is 
created, which happens once per Recon process on first use of the containers 
API (argued from the Guice binding, not run in a Recon server).

The default ({{ozone.recon.export.directory}} empty, resolved to 
{{<ozone.recon.db.dir>/exports}}) is not affected.

h3. Impact
* With the export directory equal to {{ozone.recon.db.dir}}: Recon's container 
key DB, OM snapshot DB, Recon SCM DB and Derby SQL DB directories are deleted, 
and so is {{recon/}} with Recon's storage {{current/VERSION}} file (run against 
stand-in directories with those names, see reproduction). Because the sweep 
runs on the first containers API request, Recon has these databases open at 
that point, so they are deleted under a running process (argued from code, not 
run).
* With a shared directory: other applications' directories and any {{.tar}} 
file (for example backups) are deleted permanently (run). Other files are kept.
* It repeats on every Recon start.

h3. Reproduction
[^TestBugCR1ExportDirSweep.java] ({{hadoop-ozone/recon}}, package 
{{org.apache.hadoop.ozone.recon.api}}) builds {{ExportJobManager}} through its 
public constructor with real configuration keys, as Guice does; only 
{{ContainerHealthSchemaManager}}, which the cleanup does not use, is mocked. 
The first test points the export directory at the Recon DB directory holding 
{{recon-container-key.db_...}}, {{om.snapshot.db_...}}, {{recon-scm.db}}, 
{{ozone_recon_derby.db}} and {{recon/current/VERSION}}; after construction the 
directory is empty. The second uses a shared directory with 
{{other-app-cache/}}, {{operator-backup-2026-10-01.tar}} and {{notes.txt}}; 
only {{notes.txt}} is left. Both tests pass on unmodified source, 
deterministic; a passing test means the defect is present.

h3. Patch
[^CR-1-export-startup-cleanup-only-owned-entries.patch], against 
fb2d0310a9c6ea37c72cefb5cc1d5b4db35a7ab1. It also applies to master at 
9c3e5477ffb.

The cleanup now removes a directory only if its name is a valid UUID 
({{UUIDUtil.isValidUuidString}}, as in SCM's {{ExportFileManager}}) and a file 
only if its name has the form {{export_<state>_<millis>.tar}}, the names 
{{submitJob}} and {{executeExport}} create. The state part accepts any letters, 
because {{submitJob}} lower cases it with the JVM default locale (under a 
Turkish locale {{MISSING}} gets a dotless i). Leftovers of an interrupted 
export are still removed and other files are kept. A directory whose name is a 
UUID is still removed, because the job manager names its own working 
directories that way. In the default directory, which only holds export files, 
the behaviour is unchanged.

Covered in the existing {{TestExportJobManager}}: the new 
{{startupCleanupKeepsEntriesNotCreatedByExports}} checks that {{backup.tar}}, 
{{export_backup.tar}} and a DB directory survive, and 
{{startupCleanupRemovesLeftoverTarsAndDirs}} now uses the names the manager 
really creates (it used {{stale_export.tar}} and {{stale_job_id/}} before), 
including a tar named with the Turkish lower case of {{MISSING}}, and still 
expects them removed. The test builds that name with an explicit locale and 
does not change the JVM default. Without the change the new test fails because 
{{backup.tar}} is deleted. With it {{TestExportJobManager}}, {{TestExportJob}} 
and {{TestContainerEndpoint}} (54 tests) pass and checkstyle is clean. With the 
patch both tests of the reproduction fail, because all entries are kept.

Found by code review of the Recon unhealthy container export job manager, as 
part of the TLA+ verification effort under HDDS-15926, on commit 
fb2d0310a9c6ea37c72cefb5cc1d5b4db35a7ab1. Checked against HDDS issues and 
apache/ozone pull requests for duplicates before filing. The attached patch is 
a proposal for review. Generated with Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to