You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by sl...@apache.org on 2026/10/06 02:20:28 UTC
(hadoop) branch trunk updated: MAPREDUCE-7547. HsWebApp should bind App request-scoped instead of singleton (#8790)
This is an automated email from the ASF dual-hosted git repository.
slfan1989 pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git
The following commit(s) were added to refs/heads/trunk by this push:
new 14b55ba5be0 MAPREDUCE-7547. HsWebApp should bind App request-scoped instead of singleton (#8790)
14b55ba5be0 is described below
commit 14b55ba5be09a19ca020932769fdd59022bc0002
Author: Jose Luis López López <jo...@gmail.com>
AuthorDate: Tue Oct 6 04:20:22 2026 +0200
MAPREDUCE-7547. HsWebApp should bind App request-scoped instead of singleton (#8790)
MAPREDUCE-7547. HsWebApp should bind App request-scoped instead of singleton
App carries per-request job/task state from the controller to the view.
Binding it as a singleton lets concurrent JobHistoryServer requests overwrite each other's state. Use request scope so the controller and view share one App within a request while keeping requests isolated.
Add regression tests for the App binding scope and attempts page rendering through a real HsWebApp. Set connection and read timeouts in the page test and disconnect in a finally block.
Co-authored-by: Claude Opus 5.5 <no...@anthropic.com>
Co-authored-by: Shilun Fan <sl...@apache.org>
Signed-off-by: Shilun Fan <sl...@apache.org>
---
.../hadoop/mapreduce/v2/hs/webapp/HsWebApp.java | 4 +-
.../mapreduce/v2/hs/webapp/TestHSWebApp.java | 86 ++++++++++++++++++++++
2 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java
index 5592d04bd9f..6f5876454a1 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java
@@ -31,7 +31,7 @@
import org.apache.hadoop.mapreduce.v2.hs.HistoryContext;
import org.apache.hadoop.yarn.webapp.WebApp;
-import com.google.inject.Singleton;
+import com.google.inject.servlet.RequestScoped;
public class HsWebApp extends WebApp implements AMParams {
@@ -43,7 +43,7 @@ public HsWebApp(HistoryContext history) {
@Override
public void setup() {
- bind(App.class).in(Singleton.class);
+ bind(App.class).in(RequestScoped.class);
bind(AppContext.class).toInstance(history);
bind(HistoryContext.class).toInstance(history);
route("/", HsController.class);
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java
index 4fef7f13965..ad2c7abb579 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java
@@ -28,30 +28,50 @@
import static org.apache.hadoop.yarn.webapp.YarnWebParams.ENTITY_STRING;
import static org.apache.hadoop.yarn.webapp.YarnWebParams.NM_NODENAME;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.verify;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.mapreduce.v2.api.records.JobId;
+import org.apache.hadoop.mapreduce.v2.api.records.TaskType;
import org.apache.hadoop.mapreduce.v2.app.AppContext;
import org.apache.hadoop.mapreduce.v2.app.MRApp;
import org.apache.hadoop.mapreduce.v2.app.MockAppContext;
import org.apache.hadoop.mapreduce.v2.app.MockJobs;
+import org.apache.hadoop.mapreduce.v2.app.job.Job;
+import org.apache.hadoop.mapreduce.v2.app.job.Task;
+import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt;
+import org.apache.hadoop.mapreduce.v2.app.webapp.App;
import org.apache.hadoop.mapreduce.v2.app.webapp.TestAMWebApp;
+import org.apache.hadoop.mapreduce.v2.hs.MockHistoryContext;
+import org.apache.hadoop.mapreduce.v2.util.MRApps.TaskAttemptStateUI;
import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.webapp.View;
+import org.apache.hadoop.yarn.webapp.WebApp;
+import org.apache.hadoop.yarn.webapp.WebApps;
import org.apache.hadoop.yarn.webapp.log.AggregatedLogsPage;
import org.apache.hadoop.yarn.webapp.test.WebAppTests;
import org.junit.jupiter.api.Test;
import com.google.inject.AbstractModule;
+import com.google.inject.Binding;
import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Scopes;
+import com.google.inject.servlet.RequestScoped;
+import com.google.inject.servlet.ServletScopes;
+import com.google.inject.spi.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -127,6 +147,72 @@ public void testTaskViewNaturalSortType() {
ctx, params);
}
+ /**
+ * App carries per-request job/task state from the controller to the view,
+ * so it must be shared within a request but never across requests.
+ */
+ @Test
+ public void testAppBindingIsRequestScoped() {
+ HsWebApp webApp = new HsWebApp(new MockHistoryContext(0, 1, 1, 1));
+ Binding<?> appBinding = Elements.getElements(webApp).stream()
+ .filter(e -> e instanceof Binding
+ && ((Binding<?>) e).getKey().equals(Key.get(App.class)))
+ .map(e -> (Binding<?>) e)
+ .findFirst()
+ .orElse(null);
+ assertNotNull(appBinding, "HsWebApp should bind App");
+ assertTrue(Scopes.isScoped(appBinding, ServletScopes.REQUEST,
+ RequestScoped.class), "App should be bound request-scoped");
+ }
+
+ /**
+ * Render the attempts page through a real HsWebApp, so the controller and
+ * the view get App from the webapp's own Guice bindings.
+ */
+ @Test
+ public void testAttemptsPageRender() throws Exception {
+ MockHistoryContext ctx = new MockHistoryContext(0, 1, 2, 2);
+ JobId jobId = ctx.getAllJobs().keySet().iterator().next();
+ Job job = ctx.getJob(jobId);
+ TaskAttempt attempt = null;
+ TaskAttemptStateUI state = null;
+ for (Task task : job.getTasks(TaskType.MAP).values()) {
+ for (TaskAttempt ta : task.getAttempts().values()) {
+ for (TaskAttemptStateUI ui : TaskAttemptStateUI.values()) {
+ if (state == null && ui.correspondsTo(ta.getState())) {
+ attempt = ta;
+ state = ui;
+ }
+ }
+ }
+ }
+ assertNotNull(attempt, "Mock job should have a map attempt to render");
+
+ WebApp webApp = WebApps.$for("jobhistory").at(0)
+ .start(new HsWebApp(ctx));
+ try {
+ URL url = new URL("http://localhost:"
+ + webApp.getListenerAddress().getPort() + "/jobhistory/attempts/"
+ + jobId + "/m/" + state);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setConnectTimeout(10_000);
+ conn.setReadTimeout(10_000);
+ try {
+ assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode(),
+ url.toString());
+ try (InputStream in = conn.getInputStream()) {
+ assertTrue(new String(in.readAllBytes(), StandardCharsets.UTF_8)
+ .contains(attempt.getID().toString()),
+ "Attempts page should list " + attempt.getID());
+ }
+ } finally {
+ conn.disconnect();
+ }
+ } finally {
+ webApp.stop();
+ }
+ }
+
@Test public void testAttemptsView() {
LOG.info("HsAttemptsPage");
AppContext appContext = new MockAppContext(0, 1, 1, 1);
---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org