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()