This is an automated email from the ASF dual-hosted git repository.
tbonelee pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new 2e804101df [ZEPPELIN-6546] Restore Math.max accumulation for
NoteJobInfo.unixTimeLastRun
2e804101df is described below
commit 2e804101dfe9a3a12da1919bc08c16d190fe82ed
Author: HwangRock <[email protected]>
AuthorDate: Sun Jul 19 03:03:32 2026 +0900
[ZEPPELIN-6546] Restore Math.max accumulation for
NoteJobInfo.unixTimeLastRun
### What is this PR for?
Job Manager reports a wrong "last run" time for any note whose most
recently executed paragraph is not the last paragraph in the note.
`NoteJobInfo(Note)` in `JobManagerService` overwrites `lastRunningUnixTime`
on every loop iteration, so the last paragraph always wins:
```java
lastRunningUnixTime = getUnixTimeLastRunParagraph(paragraph);
```
Consequences:
- Both UIs render and sort by `unixTimeLastRun` (`jobmanager.filter.js` in
zeppelin-web, `job-manager.component.ts` in zeppelin-web-angular), so the
displayed relative time and the Recently-Update sort order are wrong.
- The incremental path `getNoteJobInfoByUnixTime()` filters with
`unixTimeLastRun > lastUpdateServerUnixTime`, so a note that just ran can be
silently dropped from `LIST_UPDATE_NOTE_JOBS` pushes when an older paragraph
sits at the bottom.
This is a regression of ZEPPELIN-2860, which fixed exactly this in 2017
with `Math.max` (#2543). The ZEPPELIN-3737 refactor moved the logic from
NotebookServer into JobManagerService and dropped the `Math.max` (001c621c7).
This PR restores it.
Verified on a live server: a two-paragraph note whose first paragraph
finished today and whose second paragraph was created 8 days ago and never ran
showed "8 days ago" in Job Manager before the fix and the correct run time
after.
### Screenshots (if appropriate)
https://github.com/user-attachments/assets/4508d689-2d6a-4998-bbbc-326a6006a9d9
### What type of PR is it?
Bug Fix
### Todos
* [x] Restore `Math.max` accumulation in `NoteJobInfo`
* [x] Regression tests
### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-6546
### How should this be tested?
`mvn test -pl zeppelin-server -Dtest=JobManagerServiceTest` — the two new
tests pin `unixTimeLastRun` to the max paragraph timestamp via the
`getNoteJobInfoByUnixTime` filter boundary and fail without the fix.
Manually: create a note with two paragraphs, run only the first one, open
the Job Manager page. Before the fix the note shows the second paragraph's
creation date as its last run and sorts accordingly; after the fix it shows the
actual run time.
### Questions:
* Does the license files need to update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? No
Closes #5319 from HwangRock/ZEPPELIN-6546.
Signed-off-by: ChanHo Lee <[email protected]>
---
.../apache/zeppelin/service/JobManagerService.java | 2 +-
.../zeppelin/service/JobManagerServiceTest.java | 83 ++++++++++++++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/JobManagerService.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/JobManagerService.java
index 6b65ef58d6..e699d58f49 100644
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/JobManagerService.java
+++
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/JobManagerService.java
@@ -194,7 +194,7 @@ public class JobManagerService {
}
// get data for the job manager.
ParagraphJobInfo paragraphItem = new ParagraphJobInfo(paragraph);
- lastRunningUnixTime = getUnixTimeLastRunParagraph(paragraph);
+ lastRunningUnixTime = Math.max(lastRunningUnixTime,
getUnixTimeLastRunParagraph(paragraph));
paragraphs.add(paragraphItem);
}
diff --git
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/JobManagerServiceTest.java
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/JobManagerServiceTest.java
index e8d69d604b..b56c847308 100644
---
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/JobManagerServiceTest.java
+++
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/JobManagerServiceTest.java
@@ -18,6 +18,7 @@
package org.apache.zeppelin.service;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -28,10 +29,16 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
+import java.util.Collections;
+import java.util.Date;
import java.util.List;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.notebook.AuthorizationService;
+import org.apache.zeppelin.notebook.Note;
+import org.apache.zeppelin.notebook.NoteInfo;
import org.apache.zeppelin.notebook.Notebook;
+import org.apache.zeppelin.notebook.Paragraph;
+import org.apache.zeppelin.scheduler.Job;
import org.apache.zeppelin.service.JobManagerService.NoteJobInfo;
import org.apache.zeppelin.service.exception.JobManagerForbiddenException;
import org.apache.zeppelin.user.AuthenticationInfo;
@@ -111,4 +118,80 @@ public class JobManagerServiceTest {
}
}
+ @Nested
+ class WhenJobManagerIsEnabled {
+
+ private static final long LAST_RUN_TIME = 200_000L;
+ private static final long NEVER_RUN_CREATED_TIME = 100_000L;
+
+ private Note mockNote;
+
+ @BeforeEach
+ void enableJobManager() throws IOException {
+ when(zConf.isJobManagerEnabled()).thenReturn(true);
+
+ mockNote = mock(Note.class);
+ when(mockNote.getId()).thenReturn("note1");
+ when(mockNote.getName()).thenReturn("note1");
+ when(mockNote.getConfig()).thenReturn(Collections.emptyMap());
+ when(mockNote.getDefaultInterpreterGroup()).thenReturn("spark");
+
+ Paragraph lastRunParagraph = mock(Paragraph.class);
+ when(lastRunParagraph.isTerminated()).thenReturn(true);
+ when(lastRunParagraph.getDateFinished()).thenReturn(new
Date(LAST_RUN_TIME));
+ when(lastRunParagraph.getStatus()).thenReturn(Job.Status.FINISHED);
+ when(lastRunParagraph.getId()).thenReturn("p1");
+ when(lastRunParagraph.getTitle()).thenReturn(null);
+
+ Paragraph neverRunParagraph = mock(Paragraph.class);
+ when(neverRunParagraph.isTerminated()).thenReturn(false);
+ when(neverRunParagraph.isRunning()).thenReturn(false);
+ when(neverRunParagraph.getDateCreated()).thenReturn(new
Date(NEVER_RUN_CREATED_TIME));
+ when(neverRunParagraph.getStatus()).thenReturn(Job.Status.READY);
+ when(neverRunParagraph.getId()).thenReturn("p2");
+ when(neverRunParagraph.getTitle()).thenReturn(null);
+
+ when(mockNote.getParagraphs()).thenReturn(List.of(lastRunParagraph,
neverRunParagraph));
+
+ when(mockNotebook.getNotesInfo()).thenReturn(List.of(new
NoteInfo("note1", "note1.zpln")));
+ when(mockAuthorizationService.isOwner(any(),
eq("note1"))).thenReturn(true);
+ when(mockNotebook.processNote(eq("note1"), any())).thenAnswer(invocation
-> {
+ Notebook.NoteProcessor<Object> noteProcessor =
invocation.getArgument(1);
+ return noteProcessor.process(mockNote);
+ });
+ }
+
+ @Test
+ void getNoteJobInfoByUnixTime_usesMaxParagraphTimestamp_notLastParagraph()
throws IOException {
+ ServiceCallback<List<NoteJobInfo>> callback = new
SimpleServiceCallback<>();
+
+ List<NoteJobInfo> result = jobManagerService.getNoteJobInfoByUnixTime(
+ (NEVER_RUN_CREATED_TIME + LAST_RUN_TIME) / 2,
+ serviceContext,
+ callback
+ );
+
+ assertEquals(1, result.size());
+ }
+
+ @Test
+ void getNoteJobInfoByUnixTime_boundaryIsExactlyMaxTimestamp() throws
IOException {
+ ServiceCallback<List<NoteJobInfo>> callback = new
SimpleServiceCallback<>();
+
+ List<NoteJobInfo> includedResult =
jobManagerService.getNoteJobInfoByUnixTime(
+ LAST_RUN_TIME - 1,
+ serviceContext,
+ callback
+ );
+ List<NoteJobInfo> excludedResult =
jobManagerService.getNoteJobInfoByUnixTime(
+ LAST_RUN_TIME,
+ serviceContext,
+ callback
+ );
+
+ assertEquals(1, includedResult.size());
+ assertTrue(excludedResult.isEmpty());
+ }
+ }
+
}