/* Copyright 2012 Google, Inc.
*
* 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 org.arbeitspferde.groningen.generator;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import org.arbeitspferde.groningen.PipelineId;
import org.arbeitspferde.groningen.common.SubjectSettingsFileManager;
import org.arbeitspferde.groningen.config.GroningenConfig;
import org.arbeitspferde.groningen.config.PipelineIterationScoped;
import org.arbeitspferde.groningen.display.MonitorGroningen;
import org.arbeitspferde.groningen.experimentdb.CommandLine;
import org.arbeitspferde.groningen.experimentdb.Experiment;
import org.arbeitspferde.groningen.experimentdb.ExperimentDb;
import org.arbeitspferde.groningen.experimentdb.SubjectStateBridge;
import org.arbeitspferde.groningen.generator.SubjectShuffler.SubjectIterator;
import org.arbeitspferde.groningen.profiling.ProfilingRunnable;
import org.arbeitspferde.groningen.proto.ExpArgFile.ExperimentArgs;
import org.arbeitspferde.groningen.subject.Subject;
import org.arbeitspferde.groningen.utility.Clock;
import org.arbeitspferde.groningen.utility.Metric;
import org.arbeitspferde.groningen.utility.MetricExporter;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The Generator queries the {@link ExperimentDb} to build an experiment from the output of
* the {@link Hypothesizer}. The results are written to settings files so that restarting
* subjects can read the mutated JVM settings.
*/
@PipelineIterationScoped
public class Generator extends ProfilingRunnable {
/** Logger for this class */
private static final Logger log = Logger.getLogger(Generator.class.getCanonicalName());
/** The Expermental Database */
private final ExperimentDb experimentDb;
/** The serving address of this Groningen instance */
private final String servingAddress;
private final SubjectShuffler subjectShuffler;
private final SubjectSettingsFileManager subjectSettingsFileManager;
private final MetricExporter metricExporter;
/** Counts the number of times the generator failed to update the subject file */
private static final AtomicLong generatorFailures = new AtomicLong(0);
private final PipelineId pipelineId;
/** The delay period in millis betwen retries of failing Generator runs */
private static final long RUN_GENERATOR_RETRY_DELAY = 60000L;
/**
* Reset the JVM settings for a given subject to default values.
* This method is called by Executor.
* @param subject The subject whose subject to be reset.
*/
public void resetToDefault(SubjectStateBridge subject) {
subjectSettingsFileManager.delete(subject.getAssociatedSubject().getExpSettingsFile());
}
@Inject
public Generator(final PipelineId pipelineId,
final Clock clock, final MonitorGroningen monitor, final ExperimentDb e,
@Named("servingAddress") final String servingAddress, final SubjectShuffler subjectShuffler,
final SubjectSettingsFileManager subjectSettingsFileManager,
final MetricExporter metricExporter) {
super(clock, monitor);
experimentDb = e;
this.servingAddress = servingAddress;
this.subjectShuffler = subjectShuffler;
this.subjectSettingsFileManager = subjectSettingsFileManager;
this.metricExporter = metricExporter;
this.pipelineId = pipelineId;
}
@Override
public void profiledRun(final GroningenConfig config) throws RuntimeException {
final Experiment lastExperiment;
lastExperiment = experimentDb.getLastExperiment();
if (lastExperiment == null) {
log.warning("Experiments do not exist. Skipping Generator stage.");
} else {
try {
final SubjectIterator subjectIterator = subjectShuffler.createIterator();
for (final SubjectStateBridge bridge : lastExperiment.getSubjects()) {
if (subjectIterator.hasNext()) {
Subject subject = subjectIterator.next();
bridge.setAssociatedSubject(subject);
// Do not build and write experiment args for default subjects. Delete the settings
// file, so injector won't be able to read the new settings.
if (subject.isDefault()) {
subjectSettingsFileManager.delete(subject.getExpSettingsFile());
continue;
}
final ExperimentArgs experimentArgs = ExperimentArgs.newBuilder()
.setArgs(bridge.getCommandLine().toArgumentString())
.setMasterServingAddress(servingAddress)
.addAllTunedArg(CommandLine.getManagedArgs())
.setPipelineId(pipelineId.id())
.build();
subjectSettingsFileManager.write(experimentArgs, subject.getExpSettingsFile());
}
}
} catch (final Exception e) {
generatorFailures.incrementAndGet();
log.log(Level.WARNING, "Unexpected exception when processing subjects.", e);
throw new RuntimeException(e);
}
}
}
@Override
public void run(GroningenConfig config) {
boolean done = false;
do {
try {
super.run(config);
done = true;
} catch (final RuntimeException re) {
log.log(Level.WARNING, "Problems running the Generator. Retrying in 1 minute.", re);
try {
Thread.sleep(RUN_GENERATOR_RETRY_DELAY);
} catch (final InterruptedException ie) {
log.log(Level.WARNING, "Problems sleeping while retrying the Generator.", ie);
}
}
} while (!done);
}
@Override
public void startUp() {
log.info("Initializing Generator.");
/*
* TODO(team): Take care of making sure that this works and does not leak memory once
* multiple concurrent pipelines are supported.
*/
metricExporter.register(
"generator_failures",
"Counts the number of times the generator failed to update the subject file",
Metric.make(generatorFailures));
}
}