Skip to content

Commit 19498b5

Browse files
pthirunclaude
andcommitted
[vpj] Address review feedback for kill-check monitor
- Reset pushJobKilledByController flag at start of runJobWithKillDetection() - Add @VisibleForTesting to package-private test-accessible methods - Add early return guard to prevent repeated kill logging/calls - Clarify response.isError() (HTTP) vs status.isError() (push status) - Escalate HTTP query failures from WARN to ERROR - Use ExecutionStatus.ERROR.toString() instead of string literal - Verify queryOverallJobStatus was invoked in kill detection test - Replace fragile skipVPJValidation + re-override with explicit stubs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c716a82 commit 19498b5

2 files changed

Lines changed: 34 additions & 8 deletions

File tree

clients/venice-push-job/src/main/java/com/linkedin/venice/hadoop/VenicePushJob.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import static com.linkedin.venice.vpj.VenicePushJobConstants.VENICE_DISCOVER_URL_PROP;
8484
import static com.linkedin.venice.vpj.VenicePushJobConstants.VENICE_STORE_NAME_PROP;
8585

86+
import com.google.common.annotations.VisibleForTesting;
8687
import com.linkedin.avroutil1.compatibility.AvroCompatibilityHelper;
8788
import com.linkedin.d2.balancer.D2Client;
8889
import com.linkedin.venice.PushJobCheckpoints;
@@ -1051,7 +1052,9 @@ private void setupJobTimeoutMonitor() {
10511052
* after data writing completes but before the monitor is cancelled. This is correct behavior — if the
10521053
* push was killed, any data written is wasted, and we should still fail the job.
10531054
*/
1055+
@VisibleForTesting
10541056
void runJobWithKillDetection() {
1057+
pushJobKilledByController = false;
10551058
startPushJobKillCheckMonitor();
10561059
try {
10571060
runJobAndUpdateStatus();
@@ -1066,6 +1069,7 @@ void runJobWithKillDetection() {
10661069
* This runs during the data writing phase to detect early kills (e.g., when a user push supersedes
10671070
* a repush) and abort the data writer job promptly instead of wasting resources.
10681071
*/
1072+
@VisibleForTesting
10691073
void startPushJobKillCheckMonitor() {
10701074
String topicToMonitor = getTopicToMonitor(pushJobSetting);
10711075
long intervalMs = pushJobSetting.pollJobStatusIntervalMs;
@@ -1076,12 +1080,17 @@ void startPushJobKillCheckMonitor() {
10761080
intervalMs);
10771081
pushJobKillCheckScheduledFuture = timeoutExecutor.scheduleWithFixedDelay(() -> {
10781082
try {
1083+
if (pushJobKilledByController) {
1084+
return;
1085+
}
10791086
JobStatusQueryResponse response = ControllerClient.retryableRequest(
10801087
controllerClient,
10811088
pushJobSetting.controllerStatusPollRetries,
10821089
client -> client.queryOverallJobStatus(topicToMonitor, Optional.empty(), null, false));
1090+
// response.isError() indicates an HTTP/transport error (failed to reach the controller),
1091+
// NOT that the push status is ERROR. Push status is checked separately below via status.isError().
10831092
if (response.isError()) {
1084-
LOGGER.warn(
1093+
LOGGER.error(
10851094
"Kill check monitor could not query job status for store: {}, version: {}. Error: {}",
10861095
pushJobSetting.storeName,
10871096
pushJobSetting.version,
@@ -1104,6 +1113,7 @@ void startPushJobKillCheckMonitor() {
11041113
}, intervalMs, intervalMs, TimeUnit.MILLISECONDS);
11051114
}
11061115

1116+
@VisibleForTesting
11071117
void stopPushJobKillCheckMonitor() {
11081118
if (pushJobKillCheckScheduledFuture != null) {
11091119
pushJobKillCheckScheduledFuture.cancel(false);

clients/venice-push-job/src/test/java/com/linkedin/venice/hadoop/VenicePushJobTest.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,7 +1933,7 @@ public void testPushJobKilledDuringDataWriting(Class<? extends DataWriterCompute
19331933

19341934
// Simulate controller returning ERROR status (push was killed)
19351935
JobStatusQueryResponse killResponse = mock(JobStatusQueryResponse.class);
1936-
doReturn("ERROR").when(killResponse).getStatus();
1936+
doReturn(ExecutionStatus.ERROR.toString()).when(killResponse).getStatus();
19371937
doReturn(false).when(killResponse).isError();
19381938
doReturn(killResponse).when(client).queryOverallJobStatus(anyString(), any(), any(), anyBoolean());
19391939

@@ -1972,12 +1972,27 @@ public void testPushJobKilledDuringDataWriting(Class<? extends DataWriterCompute
19721972
return null;
19731973
}).when(dataWriterJob).kill();
19741974

1975-
// N.B.: skipVPJValidation stubs runJobAndUpdateStatus() (among others), so we must re-override
1976-
// it afterward to let the real kill-detection flow execute. If skipVPJValidation's stubs change,
1977-
// this re-override may need updating.
1978-
skipVPJValidation(pushJob);
1979-
doCallRealMethod().when(pushJob).runJobWithKillDetection();
1980-
doCallRealMethod().when(pushJob).runJobAndUpdateStatus();
1975+
// Stub only the validation methods from skipVPJValidation that this test needs bypassed.
1976+
// We intentionally avoid skipVPJValidation() because it also stubs runJobAndUpdateStatus(),
1977+
// which we need to run for kill-detection to work.
1978+
doAnswer(invocation -> {
1979+
VeniceProperties properties = pushJob.getJobProperties();
1980+
PushJobSetting pjs = pushJob.getPushJobSetting();
1981+
if (!pjs.isSourceKafka) {
1982+
Schema schema = AvroSchemaParseUtils.parseSchemaFromJSONLooseValidation(SIMPLE_FILE_SCHEMA_STR);
1983+
pjs.keyField = properties.getString(KEY_FIELD_PROP, DEFAULT_KEY_FIELD_PROP);
1984+
pjs.valueField = properties.getString(VALUE_FIELD_PROP, DEFAULT_VALUE_FIELD_PROP);
1985+
pjs.inputDataSchema = schema;
1986+
pjs.valueSchema = schema.getField(pjs.valueField).schema();
1987+
pjs.inputDataSchemaString = SIMPLE_FILE_SCHEMA_STR;
1988+
pjs.keySchema = pjs.inputDataSchema.getField(pjs.keyField).schema();
1989+
pjs.keySchemaString = pjs.keySchema.toString();
1990+
pjs.valueSchemaString = pjs.valueSchema.toString();
1991+
}
1992+
return getMockInputDataInfoProvider();
1993+
}).when(pushJob).getInputDataInfoProvider();
1994+
doNothing().when(pushJob).validateKeySchema(any());
1995+
doNothing().when(pushJob).validateAndRetrieveValueSchemas(any(), any(), anyBoolean());
19811996

19821997
try {
19831998
pushJob.run();
@@ -1992,6 +2007,7 @@ public void testPushJobKilledDuringDataWriting(Class<? extends DataWriterCompute
19922007
assertEquals(dataWriterRunningLatch.getCount(), 0, "Data writer job should have started");
19932008
assertEquals(dataWriterKilledLatch.getCount(), 0, "Data writer job should have been killed");
19942009
verify(dataWriterJob, times(1)).kill();
2010+
verify(client, atLeastOnce()).queryOverallJobStatus(anyString(), any(), any(), anyBoolean());
19952011
}
19962012
}
19972013

0 commit comments

Comments
 (0)