[
https://issues.apache.org/jira/browse/MAPREDUCE-7539?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18098836#comment-18098836
]
ASF GitHub Bot commented on MAPREDUCE-7539:
-------------------------------------------
ferdelyi commented on code in PR #8556:
URL: https://github.com/apache/hadoop/pull/8556#discussion_r3644545516
##########
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/filecontroller/ifile/LogAggregationIndexedFileController.java:
##########
@@ -121,22 +121,41 @@ public class LogAggregationIndexedFileController
private int fsNumRetries = 3;
private long fsRetryInterval = 1000L;
private static final int VERSION = 1;
- private IndexedLogsMeta indexedLogsMeta = null;
- private IndexedPerAggregationLogMeta logsMetaInThisCycle;
- private long logAggregationTimeInThisCycle;
- private FSDataOutputStream fsDataOStream;
private Algorithm compressAlgo;
- private CachedIndexedLogsMeta cachedIndexedLogsMeta = null;
- private boolean logAggregationSuccessfullyInThisCyCle = false;
- private long currentOffSet = 0;
- private Path remoteLogCheckSumFile;
- private FileContext fc;
- private UserGroupInformation ugi;
- private byte[] uuid = null;
private final int UUID_LENGTH = 32;
private long logRollOverMaxFileSize;
private Clock sysClock;
+ /**
+ * All mutable state that belongs to a single write session
+ * (one {@link #initializeWriter} / {@link #write} / {@link #postWrite} /
+ * {@link #closeWriter} lifecycle). Bundling it here means the read path
+ * cannot accidentally touch write-path state, regardless of whether the
+ * controller instance is shared across applications.
+ */
+ private static final class WriteSession {
+ /** UUID derived from the application being written. */
+ private final byte[] uuid;
+ /** Accumulated log metadata for the current aggregated file. */
+ private IndexedLogsMeta indexedLogsMeta;
+ /** Log metadata accumulated within this single write cycle. */
+ private IndexedPerAggregationLogMeta logsMetaInThisCycle;
+ private long logAggregationTimeInThisCycle;
+ private boolean logAggregationSuccessfullyInThisCyCle = false;
+ private long currentOffSet = 0;
+ private Path remoteLogCheckSumFile;
+ private FileContext fc;
+ private UserGroupInformation ugi;
+ private FSDataOutputStream fsDataOStream;
+
+ WriteSession(byte[] uuid) {
+ this.uuid = uuid;
+ }
+ }
+
+ /** Non-null while a write session is in progress; null otherwise. */
+ private WriteSession writeSession = null;
Review Comment:
Thank you for your repeated review! It seems to be that the comment was
misleading and it's been updated now. Run a code review specifically for
concurrency hazards and this was the result of the analysis:
Two residual hazards
1. this.writeSession is an unsynchronized mutable field shared between the
write path and closeWriter
closeWriter reads writeSession to close the stream. If — hypothetically — a
second thread called initializeWriter while closeWriter was running
(overwriting writeSession), the first session's stream would not be closed. In
current usage this cannot happen (single-threaded write lifecycle), but the
field is not volatile and has no happens-before guarantee. If the JVM ever
reorders the this.writeSession = session assignment in initializeWriter
relative to the caller seeing it, the caller could use a partially-constructed
session. Again, not possible with the current single-threaded caller, but not
enforced by the type system.
2. this.compressAlgo, this.logRollOverMaxFileSize, this.sysClock,
this.fsNumRetries, this.fsRetryInterval are instance fields set in initInternal
and then read by the write path
These are set once during initialization and only read afterward, so they
are safe in practice. However, they are not final and not volatile, so there is
technically no Java Memory Model guarantee that a thread reading them will see
the initialized values unless there is a happens-before edge between
initInternal and the first use. In practice the framework always calls
initialize() before the controller is handed to any thread, so this is a
theoretical rather than practical issue.
---
Bottom line
The current code has no real concurrency bugs in any deployment scenario
that exists today. The two hazards above are theoretical — both stem from the
fact that the class was never designed for concurrent write access and still
isn't. The comment in initializeWriter now accurately documents this. If the
class were ever used in a context where concurrent writes were possible,
writeSession would need to be volatile (or the field replaced with a
thread-local, or the write methods synchronized), and the initialization fields
would need to be final. For the current use cases, no changes are needed.
> JobHistoryServer API fails to serve aggregated logs due to uuid mismatch
> ------------------------------------------------------------------------
>
> Key: MAPREDUCE-7539
> URL: https://issues.apache.org/jira/browse/MAPREDUCE-7539
> Project: Hadoop Map/Reduce
> Issue Type: Bug
> Components: jobhistoryserver
> Affects Versions: 3.5.0
> Reporter: Brian Goerlitz
> Assignee: Ferenc Erdelyi
> Priority: Major
> Labels: pull-request-available
>
> Due to Singleton annotations added in HADOOP-15984 for HSWebServices, the
> first time an ifile log is read via the {{/ws/v1/history/aggregatedlogs}}
> API, the UUID of the log is stored in the HSWebServices instance of the
> {{LogAggregationIndexedFileController}} and used for verification of all
> future log files. This results in failure to read any aggregated log files
> belonging to an app that is not the first one accessed after JHS restart.
> {noformat}
> 2026-06-08 20:16:40,368 WARN
> org.apache.hadoop.yarn.logaggregation.filecontroller.ifile.LogAggregationIndexedFileController:
> Can not get log meta from the log
> file:hdfs://nn:8020/tmp/logs/systest/bucket-logs-ifile/0002/application_1780935195539_0002/nm_8041
> The UUID from
> hdfs://nn:8020/tmp/logs/systest/bucket-logs-ifile/0002/application_1780935195539_0002/nm_8041
> is not correct. The offset of loaded UUID is 296605
> 2026-06-08 20:16:40,368 WARN
> org.apache.hadoop.yarn.webapp.GenericExceptionHandler: SERVICE_UNAVAILABLE
> javax.ws.rs.WebApplicationException: HTTP 500 Internal Server Error
> at
> org.apache.hadoop.yarn.server.webapp.LogServlet.getContainerLogMeta(LogServlet.java:134)
> at
> org.apache.hadoop.yarn.server.webapp.LogServlet.getContainerLogsInfo(LogServlet.java:325)
> at
> org.apache.hadoop.yarn.server.webapp.LogServlet.getLogsInfo(LogServlet.java:263)
> at
> org.apache.hadoop.mapreduce.v2.hs.webapp.HsWebServices.getAggregatedLogsMeta(HsWebServices.java:521)
> ...
> Caused by: org.apache.hadoop.yarn.webapp.NotFoundException: HTTP 404 Not Found
> at
> org.apache.hadoop.yarn.server.webapp.LogServlet.getContainerLogMeta(LogServlet.java:122)
> ... 86 more
> Caused by: java.lang.Exception: Can not get log meta for request.
> at
> org.apache.hadoop.yarn.webapp.NotFoundException.<init>(NotFoundException.java:45)
> ... 87 more
> {noformat}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]