180180import java .util .Set ;
181181import java .util .concurrent .Executors ;
182182import java .util .concurrent .ScheduledExecutorService ;
183+ import java .util .concurrent .ScheduledFuture ;
183184import java .util .concurrent .TimeUnit ;
184185import java .util .stream .Collectors ;
185186import org .apache .avro .Schema ;
@@ -261,6 +262,8 @@ public class VenicePushJob implements AutoCloseable {
261262 private final PushJobHeartbeatSenderFactory pushJobHeartbeatSenderFactory ;
262263 private PushJobHeartbeatSender pushJobHeartbeatSender = null ;
263264 private volatile boolean pushJobStatusUploadDisabledHasBeenLogged = false ;
265+ private ScheduledFuture <?> pushJobKillCheckScheduledFuture ;
266+ private volatile boolean pushJobKilledByController = false ;
264267 private final ScheduledExecutorService timeoutExecutor ;
265268 private static final int VERSION_SWAP_BUFFER_TIME_MINUTES = 20 ;
266269
@@ -827,7 +830,13 @@ public void run() {
827830 LOGGER .info ("Incremental Push Version: {}" , pushJobSetting .incrementalPushVersion );
828831 getVeniceWriter (pushJobSetting )
829832 .broadcastStartOfIncrementalPush (pushJobSetting .incrementalPushVersion , new HashMap <>());
830- runJobAndUpdateStatus ();
833+ startPushJobKillCheckMonitor ();
834+ try {
835+ runJobAndUpdateStatus ();
836+ } finally {
837+ stopPushJobKillCheckMonitor ();
838+ }
839+ throwIfPushJobKilledByController ();
831840 getVeniceWriter (pushJobSetting )
832841 .broadcastEndOfIncrementalPush (pushJobSetting .incrementalPushVersion , Collections .emptyMap ());
833842 } else {
@@ -849,7 +858,13 @@ public void run() {
849858 * {@link createNewStoreVersion(PushJobSetting, long, ControllerClient, String, VeniceProperties)}
850859 */
851860 }
852- runJobAndUpdateStatus ();
861+ startPushJobKillCheckMonitor ();
862+ try {
863+ runJobAndUpdateStatus ();
864+ } finally {
865+ stopPushJobKillCheckMonitor ();
866+ }
867+ throwIfPushJobKilledByController ();
853868
854869 if (!pushJobSetting .suppressEndOfPushMessage ) {
855870 if (pushJobSetting .sendControlMessagesDirectly ) {
@@ -994,6 +1009,59 @@ private void setupJobTimeoutMonitor() {
9941009 }, timeoutMs , TimeUnit .MILLISECONDS );
9951010 }
9961011
1012+ /**
1013+ * Schedules a periodic task that checks whether the push job has been killed by the controller.
1014+ * This runs during the data writing phase to detect early kills (e.g., when a user push supersedes
1015+ * a repush) and abort the data writer job promptly instead of wasting resources.
1016+ */
1017+ void startPushJobKillCheckMonitor () {
1018+ String topicToMonitor = getTopicToMonitor (pushJobSetting );
1019+ long intervalMs = pushJobSetting .pollJobStatusIntervalMs ;
1020+ LOGGER .info ("Starting push job kill check monitor for topic: {} with interval: {} ms" , topicToMonitor , intervalMs );
1021+ pushJobKillCheckScheduledFuture = timeoutExecutor .scheduleAtFixedRate (() -> {
1022+ try {
1023+ JobStatusQueryResponse response = ControllerClient .retryableRequest (
1024+ controllerClient ,
1025+ pushJobSetting .controllerStatusPollRetries ,
1026+ client -> client .queryOverallJobStatus (topicToMonitor , Optional .empty (), null , false ));
1027+ if (response .isError ()) {
1028+ LOGGER .warn (
1029+ "Kill check monitor could not query job status for topic: {}. Error: {}" ,
1030+ topicToMonitor ,
1031+ response .getError ());
1032+ return ;
1033+ }
1034+ ExecutionStatus status = getExecutionStatusFromControllerResponse (response );
1035+ if (status .isTerminal () && status .isError ()) {
1036+ LOGGER .error (
1037+ "Kill check monitor detected that push job for topic: {} has been killed. Status: {}" ,
1038+ topicToMonitor ,
1039+ status );
1040+ pushJobKilledByController = true ;
1041+ killDataWriterJob ();
1042+ }
1043+ } catch (Exception e ) {
1044+ LOGGER .warn ("Kill check monitor encountered an error while checking job status" , e );
1045+ }
1046+ }, intervalMs , intervalMs , TimeUnit .MILLISECONDS );
1047+ }
1048+
1049+ void stopPushJobKillCheckMonitor () {
1050+ if (pushJobKillCheckScheduledFuture != null ) {
1051+ pushJobKillCheckScheduledFuture .cancel (false );
1052+ pushJobKillCheckScheduledFuture = null ;
1053+ LOGGER .info ("Stopped push job kill check monitor" );
1054+ }
1055+ }
1056+
1057+ private void throwIfPushJobKilledByController () {
1058+ if (pushJobKilledByController ) {
1059+ throw new VeniceException (
1060+ "Push job for store " + pushJobSetting .storeName + " (topic: " + pushJobSetting .topic
1061+ + ") was killed by the controller during the data writing phase." );
1062+ }
1063+ }
1064+
9971065 private void buildHDFSSchemaDir () throws IOException {
9981066 // Build the full path for HDFSRmdSchemaSource:
9991067 // RMD schemas: <job_temp_dir>/rmd_schemas
0 commit comments