All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datarouter.job.service.JobStopperService Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.datarouter.job.service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.Future;
import java.util.function.Supplier;

import io.datarouter.instrumentation.changelog.ChangelogRecorder;
import io.datarouter.instrumentation.changelog.ChangelogRecorder.DatarouterChangelogDtoBuilder;
import io.datarouter.job.BaseJob;
import io.datarouter.job.detached.DetachedJobStopper.DetachedJobStopperSupplier;
import io.datarouter.job.job.DatarouterJobStopperJob;
import io.datarouter.job.lock.LocalTriggerLockService;
import io.datarouter.job.scheduler.JobScheduler;
import io.datarouter.job.scheduler.JobWrapper;
import io.datarouter.job.storage.stopjobrequest.StopJobRequest;
import io.datarouter.job.storage.stopjobrequest.StopJobRequestDao;
import io.datarouter.scanner.Scanner;
import io.datarouter.storage.config.properties.ServerName;
import io.datarouter.util.concurrent.ThreadTool;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

@Singleton
public class JobStopperService{

	@Inject
	private ChangelogRecorder changelogRecorder;
	@Inject
	private LocalTriggerLockService localTriggerLockService;
	@Inject
	private StopJobRequestDao stopJobRequestDao;
	@Inject
	private ServerName serverName;
	@Inject
	private DetachedJobStopperSupplier detachedJobStopperSupplier;

	/**
	 * generates requests to stop a job running on the specified servers in the cluster. the requests are not processed
	 * until {@link JobStopperService#stopRequestedLocalJobs} is called (see {@link DatarouterJobStopperJob})
	 * @param jobName the name of the job (class) to stop
	 * @param serverNames the servers on which to attempt to stop the job
	 * @param username the username that requested the stop
	 */
	public void requestStop(String jobName, List serverNames, String username){
		Instant jobTriggerDeadline = Instant.now();
		Instant requestExpiration = jobTriggerDeadline.plus(3L, ChronoUnit.MINUTES);
		Scanner.of(serverNames)
				.map(serverName -> new StopJobRequest(
						serverName,
						requestExpiration,
						jobTriggerDeadline,
						jobName,
						username))
				.flush(stopJobRequestDao::putMulti);
	}

	/**
	 * stop all jobs that are running on this server and for which a stop request was made
	 * @param shouldStop this method will cease processing and return once this returns true
	 */
	public void stopRequestedLocalJobs(Supplier shouldStop){
		stopJobRequestDao.scanUnexpiredRequestsForServer(serverName.get())
				.advanceUntil($ -> shouldStop.get())
				.forEach(this::stopRequestedLocalJob);
	}

	private void stopRequestedLocalJob(StopJobRequest stopJobRequest){
		var key = stopJobRequest.getKey();
		detachedJobStopperSupplier.get().stop(stopJobRequest);
		stopJobRequestDao.delete(key);
	}

	//TODO licenses
	public void stopLocalJob(String jobClassString, String username, Instant jobTriggerDeadline){
		Class jobClass = BaseJob.parseClass(jobClassString);
		JobWrapper jobWrapper = localTriggerLockService.getForClass(jobClass);
		//job isn't running anymore
		if(jobWrapper == null){
			return;
		}
		//the currently running job is not the one that the request was for
		if(jobWrapper.triggerTime.isAfter(jobTriggerDeadline)){
			return;
		}
		jobWrapper.requestStop();
		//wait for job to gracefully stop itself
		ThreadTool.sleepUnchecked(JobScheduler.JOB_STOP_GRACE_PERIOD_MS);
		Future future = jobWrapper.getFuture();
		if(!future.isDone()){
			future.cancel(true);
		}
		var changelog = new DatarouterChangelogDtoBuilder("Job", jobClassString, "interrupt", username).build();
		changelogRecorder.record(changelog);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy