diff --git a/src/main/java/caosdb/server/entity/container/TransactionContainer.java b/src/main/java/caosdb/server/entity/container/TransactionContainer.java index f0ac55fae7b57d24456728508baa7768b83a5711..73f25b8c41eedd46ecf1848ba422c505ff9a3e9d 100644 --- a/src/main/java/caosdb/server/entity/container/TransactionContainer.java +++ b/src/main/java/caosdb/server/entity/container/TransactionContainer.java @@ -35,6 +35,13 @@ import java.util.List; import org.apache.shiro.subject.Subject; import org.jdom2.Element; + +/** + * Utility and convenience class for Transaction objects. + * + * Not very specific to Transactions in particular (except that it provides acces to a + * TransactionBenchmark). + */ public class TransactionContainer extends Container<EntityInterface> implements ToElementable, JobTarget { diff --git a/src/main/java/caosdb/server/jobs/Job.java b/src/main/java/caosdb/server/jobs/Job.java index 110809e77ae0d40076642d953c15b0d9566eddd6..071a7446a4e86cb9aecce21073a2697189b15622 100644 --- a/src/main/java/caosdb/server/jobs/Job.java +++ b/src/main/java/caosdb/server/jobs/Job.java @@ -55,6 +55,14 @@ import org.apache.shiro.authz.Permission; import org.apache.shiro.subject.Subject; import org.reflections.Reflections; +/** + * A Job consists mainly of an Entity and a Transaction. + * + * The main purpose of Jobs is to be run(), possibly using stored Entity. + * + * All implementations can be found in the core sub package. + * + */ public abstract class Job extends AbstractObservable implements Observer { private Transaction<? extends TransactionContainer> transaction = null; private Mode mode = null; @@ -79,10 +87,18 @@ public abstract class Job extends AbstractObservable implements Observer { return getTransaction().getTransactor(); } + /** + * Run transaction.execute(t, <acess of this job's transaction>) + */ public <K extends BackendTransaction> K execute(final K t) { return getTransaction().execute(t, getTransaction().getAccess()); } + /** + * Create a new job and append it to this job's schedule. + * + * The mode and transaction of this job are used for the new one as well. + */ protected ScheduledJob appendJob(final EntityInterface entity, final Class<? extends Job> clazz) { try { final Job job = clazz.getDeclaredConstructor().newInstance(); @@ -211,8 +227,18 @@ public abstract class Job extends AbstractObservable implements Observer { } static HashMap<String, Class<? extends Job>> allClasses = null; + + // List with the core jobs which are annotated with loadAlways = true private static List<Class<? extends Job>> loadAlways; + /** + * Factory for creating a Job + * + * @param job A string identifying the job's actual class, must be from the `core` sub package. + * @param mode See the other getJob() method. + * @param entity See the other getJob() method. + * @param transaction See the other getJob() method. + */ public static Job getJob( final String job, final Mode mode, @@ -224,6 +250,9 @@ public abstract class Job extends AbstractObservable implements Observer { return getJob(jobClass, mode, entity, transaction); } + /** + * This method is practically only run once, to initialize the classes data structures. + */ private static void scanJobClasspath() { if (allClasses == null || loadAlways == null) { @@ -323,6 +352,14 @@ public abstract class Job extends AbstractObservable implements Observer { return jobs; } + /** + * Factory for creating a Job + * + * @param jobClass The job's actual class, typically from the `core` sub package. + * @param mode The core.Mode for the job. The actual meaning depends on the actual job class. + * @param entity The entity that the job operates on. + * @param transaction The transaction this job belongs to. (?) + */ private static Job getJob( Class<? extends Job> jobClass, Mode mode, @@ -442,6 +479,9 @@ public abstract class Job extends AbstractObservable implements Observer { return this.time; } + /** + * Schedule those jobs which are marked as loadAlways and which + */ public static List<Job> loadPermanentContainerJobs(Transaction<?> transaction) { final ArrayList<Job> jobs = new ArrayList<>(); // load permanent jobs diff --git a/src/main/java/caosdb/server/jobs/Schedule.java b/src/main/java/caosdb/server/jobs/Schedule.java index 4e7f005f3d2f790008a991a0138d62013f6a545a..f710f59200014c97c01a4723e2622246d67cfeea 100644 --- a/src/main/java/caosdb/server/jobs/Schedule.java +++ b/src/main/java/caosdb/server/jobs/Schedule.java @@ -26,6 +26,9 @@ import caosdb.server.entity.EntityInterface; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; +/** + * Light-weight wrapper around a Job, with running time information. + */ class ScheduledJob { long runtime = 0; @@ -46,10 +49,16 @@ class ScheduledJob { } } + /** + * Set the metadata to "started". + */ private void start() { this.startTime = System.currentTimeMillis(); } + /** + * Final update of runtime, add Measurement. + */ private void finish() { this.runtime += System.currentTimeMillis() - this.startTime; this.job @@ -62,6 +71,9 @@ class ScheduledJob { this.runtime += System.currentTimeMillis() - this.startTime; } + /** + * Continue after pause. + */ void unpause() { start(); } @@ -84,18 +96,30 @@ public class Schedule { private final CopyOnWriteArrayList<ScheduledJob> jobs = new CopyOnWriteArrayList<ScheduledJob>(); private ScheduledJob running = null; + /** + * Add all the Jobs. + */ public void addAll(final Collection<Job> jobs) { for (final Job j : jobs) { add(j); } } + /** + * Add a Job to the Schedule, and return the ScheduledJob wrapper around the Job. + */ public ScheduledJob add(final Job j) { ScheduledJob ret = new ScheduledJob(j); this.jobs.add(ret); return ret; } + /** + * Run all scheduled jobs if their time has come. + * + * This is the case if their execution time is equal to the given time. Additionally the jobs + * with earlier times are also run, iff the given execution time is at most POST_CHECK. + */ public void runJobs(final JobExecutionTime time) { for (final ScheduledJob scheduledJob : this.jobs) { if (scheduledJob.getExecutionTime().ordinal() == time.ordinal() @@ -106,6 +130,14 @@ public class Schedule { } } + /** + * Run the wrapped job, iff it was scheduled before. + * + * Also don't run the job if it should be skipped. Any other running jobs in this Schedule are + * paused (i.e. marked as not currently running) while the new job is running. + * + * The pausing has no thread-like effects, it only matters for benchmarking book keeping. + */ protected void runJob(final ScheduledJob scheduledJob) { if (!this.jobs.contains(scheduledJob)) { throw new RuntimeException("Job was not in schedule."); @@ -127,6 +159,9 @@ public class Schedule { } } + /** + * Run the job, iff it was scheduled before. + */ public void runJob(final Job j) { for (final ScheduledJob scheduledJob : this.jobs) { if (scheduledJob.job == j) { @@ -137,7 +172,11 @@ public class Schedule { throw new RuntimeException("Job was not in schedule."); } + /** + * Run all scheduled jobs which have the correct class and matching entity. + */ public void runJob(final EntityInterface entity, final Class<? extends Job> jobclass) { + // FIXME ca. 50% of all runtime is spent here as "self time" for (final ScheduledJob scheduledJob : this.jobs) { if (jobclass.isInstance(scheduledJob.job)) { if (scheduledJob.job.getEntity() == entity) { diff --git a/src/main/java/caosdb/server/transaction/Transaction.java b/src/main/java/caosdb/server/transaction/Transaction.java index 28b8276e2b93699f2315fb754303194208bb37e5..a63e29607f22020c7942c67efdaec5d3b90f50f0 100644 --- a/src/main/java/caosdb/server/transaction/Transaction.java +++ b/src/main/java/caosdb/server/transaction/Transaction.java @@ -84,6 +84,11 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra return this.container; } + /** + * Prepare the schedule for this transaction. + * + * This includes adding jobs which should always be run. + */ protected void makeSchedule() throws Exception { // load flag jobs final Job loadContainerFlags = Job.getJob("LoadContainerFlagJobs", Mode.MUST, null, this); @@ -123,12 +128,29 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra } } + /** + * Run jobs which were loaded into the scheduler. + * + * Some job times are always run and some are specific to inheriting transactions. Specifically, + * this method calls the following other methods which must be implemented: + * <ul> + * <li> init() + * <li> preCheck() + * <li> check() + * <li> postCheck() + * <li> preTransaction() + * <li> transaction() + * <li> postTransaction() + * <li> commit() + * <li> cleanUp(); + + */ @Override public final void execute() throws Exception { long t1 = System.currentTimeMillis(); try { init(); - this.schedule.runJobs(JobExecutionTime.INIT); + this.schedule.runJobs(JobExecutionTime.INIT); // Run all jobs up to INIT getContainer() .getTransactionBenchmark() .addMeasurement( @@ -136,14 +158,14 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra t1 = System.currentTimeMillis(); preCheck(); - this.schedule.runJobs(JobExecutionTime.PRE_CHECK); + this.schedule.runJobs(JobExecutionTime.PRE_CHECK); // Run all jobs up to PRE_CHECK getContainer() .getTransactionBenchmark() .addMeasurement( this.getClass().getSimpleName() + ".pre_check", System.currentTimeMillis() - t1); t1 = System.currentTimeMillis(); - check(); + check(); // By default: only runJobs(CHECK) getContainer() .getTransactionBenchmark() .addMeasurement( @@ -159,7 +181,7 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra t1 = System.currentTimeMillis(); preTransaction(); - this.schedule.runJobs(JobExecutionTime.PRE_TRANSACTION); + this.schedule.runJobs(JobExecutionTime.PRE_TRANSACTION); // Run ONLY jobs with PRE_TRANSACTION getContainer() .getTransactionBenchmark() .addMeasurement( @@ -174,7 +196,7 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra this.getClass().getSimpleName() + ".transaction", System.currentTimeMillis() - t1); t1 = System.currentTimeMillis(); - this.schedule.runJobs(JobExecutionTime.POST_TRANSACTION); + this.schedule.runJobs(JobExecutionTime.POST_TRANSACTION); // Run ONLY jobs with POST_TRANSACTION postTransaction(); getContainer() .getTransactionBenchmark()