Skip to content

[fix](catalog) safely publish Hadoop properties - #66392

Open
Gabriel39 wants to merge 1 commit into
apache:branch-4.1from
Gabriel39:fix/paimon-hadoop-properties-concurrency
Open

[fix](catalog) safely publish Hadoop properties#66392
Gabriel39 wants to merge 1 commit into
apache:branch-4.1from
Gabriel39:fix/paimon-hadoop-properties-concurrency

Conversation

@Gabriel39

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Concurrent first consumers of a catalog's Hadoop properties could observe the cache before its initialization completed. One thread could copy the HashMap while the initializer was still populating it, causing a ConcurrentModificationException during external table sink binding.

What is changed?

  • Build the Hadoop property map locally and publish it through the volatile cache only after all entries and the filesystem cache key are ready.
  • Add a deterministic concurrency regression test that verifies readers cannot observe a partially initialized cache.

Validation

  • CatalogPropertyTest
  • PaimonWriteBindingTest
  • StoragePropertiesFsCacheFingerprintTest
  • FE Checkstyle

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@Gabriel39
Gabriel39 marked this pull request as ready for review August 3, 2026 13:32
@Gabriel39
Gabriel39 requested a review from yiguolei as a code owner August 3, 2026 13:32

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes.

The local result map and final volatile assignment fix the reported early-publication window, preserve successful aggregation/fingerprint semantics, and improve retry behavior after initialization failures. However, two production concurrency gaps remain: a concrete Paimon caller still mutates the published cache while other paths iterate or copy it, and the getter can return null when invalidation lands between its volatile reads. The new regression test also has a scheduling window that can pass against the pre-fix code.

Critical checkpoints:

  • Correctness and concurrency: two blocking issues are detailed inline.
  • Lifecycle and invalidation: aggregation, monitor reentrancy, reset ownership, and exception retry were traced; no other issue was found.
  • Compatibility, performance, and security: no additional regression was found by static review.
  • Tests: JUnit and Mockito compatibility plus bounded cleanup hold, but the old-code failure oracle needs strengthening.
  • User focus: no additional focus was provided; the full PR was reviewed.
  • Review constraints: no repository code-review skill or required AGENTS.md was present. Builds and tests were not run because the authoritative review prompt forbids them.
  • Completion: the three-round full and risk-focused review converged on this frozen payload with no unresolved candidate points.

hadoopProperties = new HashMap<>();
// Publish the volatile cache only after construction because readers skip this
// lock once it is non-null and must never observe a map still being mutated.
Map<String, String> result = new HashMap<>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Return a retained snapshot across invalidation

Please use the local-snapshot form for the whole double-checked getter and return that snapshot. The final return hadoopProperties is another volatile read: a concurrent addProperty/modifyCatalogProps can run after this method's first read, acquire this monitor, reset the field to null, and make this call return null (the same window exists just after initialization releases the monitor). Current consumers immediately iterate or copy the result, so that becomes an intermittent NPE during catalog updates.

}
StorageProperties.setCombinedFsCacheKey(hadoopProperties, storageMap.values());
StorageProperties.setCombinedFsCacheKey(result, storageMap.values());
hadoopProperties = result;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep the published cache immutable

The construction itself is safely published now, but this still exposes the same mutable HashMap after publication. PaimonTableValuedFunction keeps this exact map (PaimonTableValuedFunction.java:89-90) and appends Kerberos entries in appendHMSKerberosProps, while ExternalCatalog.buildConf iterates it and PaimonWriteBinding copies it. Those paths can still race into partial Kerberos state or ConcurrentModificationException, and the TVF-specific entries also leak into the catalog cache. Please keep the cached snapshot encapsulated or immutable and copy it before caller-specific augmentation.

Assert.assertTrue(iterationStarted.await(5, TimeUnit.SECONDS));

Future<Map<String, String>> concurrentReader = executor.submit(() -> {
readerStarted.countDown();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Make the old-code failure handshake deterministic

readerStarted only proves that the task began, not that it reached getHadoopProperties(): the worker can be descheduled immediately after this countdown. In that schedule the 200 ms get times out even on the old early-publication code; after allowIteration is released both futures return the complete map and the regression passes falsely. Please use a bounded handshake that observes the reader complete on the old path or actually become blocked at the getter or monitor before releasing the initializer.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 100.00% (4/4) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 100.00% (4/4) 🎉
Increment coverage report
Complete coverage report

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.

2 participants