/** * {@inheritDoc} */ @Override public void checkStateTransition(Experiment.ID experimentID, Experiment.State currentState, Experiment.State desiredState) { if (desiredState != null && !currentState.equals(desiredState)) { // Throw an exception if the StateTransition is invalid validator.validateStateTransition(currentState, desiredState); /* If moving from a DRAFT state to a RUNNING state a sanity-check is required on the experiment buckets. Fetch the bucket information if the experiment will be (or remain) in an active state (running, paused) because that info is used to create the KV-store entry. * */ if (currentState.equals(DRAFT) && desiredState.equals(RUNNING)) { // Throw an exception if the sanity-check fails BucketList bucketList = buckets.getBuckets(experimentID, false /* don't check experiment again */); validator.validateExperimentBuckets(bucketList.getBuckets()); } } }
Experiment getExperimentIfExists(final Experiment.ID experimentID) { Experiment experiment = experiments.getExperiment(experimentID); if (experiment == null) { throw new ExperimentNotFoundException(experimentID); } return experiment; }
/** * Get list of page experiments associated to given application and page. * <p> * Use metadata cache if it is enabled or use repository to fetch from Database * * @param applicationName * @param pageName * @return */ private List<PageExperiment> getExperiments(Application.Name applicationName, Page.Name pageName) { if (metadataCacheEnabled) { return metadataCache.getPageExperiments(applicationName, pageName); } else { return pages.getExperimentsWithoutLabels(applicationName, pageName); } }
/** * {@inheritDoc} */ @Override public Bucket createBucket(Experiment.ID experimentID, Bucket newBucket, UserInfo user) { Experiment experiment = experiments.getExperiment(experimentID); if (experiment == null) { throw new ExperimentNotFoundException(experimentID); } validateExperimentState(experiment); checkBucketConstraint(experiment, newBucket); LOGGER.debug("Add Bucket: adding new bucket to running experiment" + experiment.getID()); cassandraRepository.createBucket(newBucket); try { databaseRepository.createBucket(newBucket); } catch (RepositoryException e) { cassandraRepository.deleteBucket(newBucket.getExperimentID(), newBucket.getLabel()); throw e; } //if we just created an experiment in a running experiment, update the remaining allocation percentages if (!Experiment.State.DRAFT.equals(experiment.getState())) { eventLog.postEvent(new BucketCreateEvent(user, experiment, newBucket)); BucketList updates = buckets.adjustAllocationPercentages(experiment, newBucket); buckets.updateBucketAllocBatch(experimentID, updates); } return getBucket(experimentID, newBucket.getLabel()); }
private Application.Name getApplicationNameForModifyingPages(Experiment.ID experimentID) { Experiment experiment = experiments.getExperiment(experimentID); // Throw an exception if the experiment is not found if (experiment == null) { throw new ExperimentNotFoundException(experimentID); } Application.Name applicationName = experiment.getApplicationName(); // Throw an exception if the experiment is in a TERMINATED state if (experiment.getState() == TERMINATED) { throw new InvalidExperimentStateException("Experiment must be in DRAFT, RUNNING or PAUSED states\"" + experimentID); } // Throw an exception if the experiment's end time has passed if (experiment.getEndTime().before(NOW)) { throw new IllegalArgumentException("Cannot modify pages of the experiment \"" + experimentID + "\" that has passed its end time"); } return applicationName; }
/** * {@inheritDoc} */ @Override public void deleteExclusion(Experiment.ID expID_1, Experiment.ID expID_2, UserInfo user) { Experiment exp_1 = experiments.getExperiment(expID_1); Experiment exp_2 = experiments.getExperiment(expID_2); // Check that expID_1 is a valid experiment if (exp_1 == null) { throw new ExperimentNotFoundException(expID_1); } // Check that expID_2 is a valid experiment if (exp_2 == null) { throw new ExperimentNotFoundException(expID_2); } mutexRepository.deleteExclusion(expID_1, expID_2); eventLog.postEvent(new ExperimentChangeEvent(user, exp_1, "mutex", exp_2.getLabel().toString(), null)); LOGGER.info("event=EXPERIMENT_METADATA_CHANGE, message=MUTUAL_EXCLUSION_DELETED, applicationName={}, configuration=[experiment1={}, experiment2={}]", exp_1.getApplicationName(), exp_1.getLabel(), exp_2.getLabel()); }
/** * {@inheritDoc} */ @Override public void deletePage(Experiment.ID experimentID, Page.Name pageName, UserInfo user) { Application.Name applicationName = getApplicationNameForModifyingPages(experimentID); pagesRepository.deletePage(applicationName, experimentID, pageName); Experiment experiment = experiments.getExperiment(experimentID); if (experiment != null) { eventLog.postEvent(new ExperimentChangeEvent(user, experiment, "pages", pageName.toString(), null)); } LOGGER.info("event=EXPERIMENT_METADATA_CHANGE, message=PAGE_REMOVED, applicationName={}, experimentName={}, configuration=[pageName={}, userName={}]", experiment.getApplicationName(), experiment.getLabel(), pageName, user.getUsername()); }
/** * Gets the experiment for a given ID and checks if the user has permission. * If the user has READ permissions, the experiment is returned. Otherwise the authorization throws an exception. * <p> * Throws an exception if the experiment is not found. * * @param experimentID the experiment ID * @param username the username * @return the experiment */ /*test*/ Experiment getAuthorizedExperimentOrThrow(Experiment.ID experimentID, Username username) { Experiment experiment; if ((experiment = experiments.getExperiment(experimentID)) == null) { throw new ExperimentNotFoundException(experimentID); } authorization.checkUserPermissions(username, experiment.getApplicationName(), READ); // Hack: We need to make experiments compatible, converting CassandraExperiment experiment = Experiment.from(experiment).build(); return experiment; }
/** * {@inheritDoc} */ @Override public ExperimentList getExclusions(Experiment.ID experimentID) { // Throw an exception if the input experiment is not valid final Experiment expID = experiments.getExperiment(experimentID); if (expID.getID() == null) { throw new ExperimentNotFoundException(experimentID); } return mutexRepository.getExclusions(experimentID); }
Experiment getAuthorizedExperimentById(final String authorizationHeader, final Experiment.ID experimentId) { UserInfo.Username userName = authorization.getUser(authorizationHeader); Experiment experiment = experiments.getExperiment(experimentId); if (experiment == null) { throw new ExperimentNotFoundException(experimentId); } authorization.checkUserPermissions(userName, experiment.getApplicationName(), READ); return experiment; }
/** * Queries the database to get additional information to the buckets for the provided * experiment, like the label and allocation percentage. For the analytics data per bucket * see {@link #getAnalyticData(List, Parameters)}. * * @param exp the experiment that should be enriched with bucket data * @return the same ExperimentDetail object but with additional information */ private ExperimentDetail getBucketData(ExperimentDetail exp) { List<Bucket> buckList = buckets.getBuckets(exp.getId(), false).getBuckets(); exp.addBuckets(buckList); return exp; }
/** * {@inheritDoc} */ @Override public PrioritizedExperimentList getPriorities(Application.Name applicationName, boolean verifyPriorityList) { if (verifyPriorityList) { List<Experiment.ID> priorityList = prioritiesRepository.getPriorityList(applicationName); List<Experiment> experimentList = experiments.getExperiments(applicationName); if ((priorityList != null ? priorityList.size() : 0) != experimentList.size()) { prioritiesRepository.createPriorities(applicationName, cleanPriorityList(applicationName, priorityList)); } } return prioritiesRepository.getPriorities(applicationName); }
/** * {@inheritDoc} */ @Override public BucketList updateBucketAllocBatch(Experiment.ID experimentID, BucketList bucketList) { LOGGER.debug("Add Bucket: saving new allocation percentages for experiment" + experimentID); // Update both repositories cassandraRepository.updateBucketBatch(experimentID, bucketList); databaseRepository.updateBucketBatch(experimentID, bucketList); return buckets.getBuckets(experimentID, false /* don't check experiment again */); }
List<Experiment> getExperimentsByName(final boolean checkAuthHeader, final String authorizationHeader, final Application.Name applicationName) { if (checkAuthHeader || authorizationHeader != null) { authorization.checkUserPermissions(authorization.getUser(authorizationHeader), applicationName, READ); } List<Experiment> experimentList = this.experiments.getExperiments(applicationName); if (experimentList == null) { throw new ApplicationNotFoundException(applicationName); } return experimentList; }
/** * {@inheritDoc} */ @Override protected void configure() { LOGGER.debug("installing module: {}", AnalyticsModule.class.getSimpleName()); install(new ExperimentsModule()); install(new CassandraRepositoryModule()); install(new DatabaseAnalyticsModule()); bind(Analytics.class).to(AnalyticsImpl.class).in(SINGLETON); bind(AnalysisTools.class).to(AnalysisToolsImpl.class).in(SINGLETON); bind(ExperimentDetails.class).to(ExperimentDetailsImpl.class).in(SINGLETON); LOGGER.debug("installed module: {}", AnalyticsModule.class.getSimpleName()); } }
@Override protected void configure() { LOGGER.debug("installing module: {}", ExperimentsModule.class.getSimpleName()); install(new EventLogModule()); install(new CassandraRepositoryModule()); bind(Experiments.class).to(ExperimentsImpl.class).in(SINGLETON); bind(Buckets.class).to(BucketsImpl.class).in(SINGLETON); bind(Mutex.class).to(MutexImpl.class).in(SINGLETON); bind(Pages.class).to(PagesImpl.class).in(SINGLETON); bind(Priorities.class).to(PrioritiesImpl.class).in(SINGLETON); bind(Favorites.class).to(FavoritesImpl.class).in(SINGLETON); bind(ExperimentValidator.class).in(SINGLETON); LOGGER.debug("installed module: {}", ExperimentsModule.class.getSimpleName()); } }
/** * {@inheritDoc} */ @Override public void appendToPriorityList(Experiment.ID experimentID) { Application.Name applicationName = experiments.getExperiment(experimentID).getApplicationName(); List<Experiment.ID> priorityList = prioritiesRepository.getPriorityList(applicationName); if (priorityList == null) { priorityList = new ArrayList<>(1); } // TODO: This current logic does not modify the priority list if the given experimentID already exists in the // priority list. Check if it would be better to move the given experiment to the end of the list if it // already exists if (!priorityList.contains(experimentID)) { priorityList.add(experimentID); prioritiesRepository.createPriorities(applicationName, priorityList); } }
/** * {@inheritDoc} */ @Override public ExperimentPageList getExperimentPages(Experiment.ID experimentID) { Experiment experiment = experiments.getExperiment(experimentID); // Throw an exception if the experiment is not found if (experiment == null) { throw new ExperimentNotFoundException(experimentID); } return pagesRepository.getExperimentPages(experimentID); }
Experiment getAuthorizedExperimentByName(final String authorizationHeader, final Application.Name applicationName, final Experiment.Label experimentLabel) { authorization.checkUserPermissions(authorization.getUser(authorizationHeader), applicationName, READ); Experiment experiment = experiments.getExperiment(applicationName, experimentLabel); if (experiment == null) { throw new ExperimentNotFoundException(experimentLabel); } return experiment; }
/** * {@inheritDoc} */ @Override public void erasePageData(Application.Name applicationName, Experiment.ID experimentID, UserInfo user) { pagesRepository.erasePageData(applicationName, experimentID); Experiment experiment = experiments.getExperiment(experimentID); if (experiment != null) { eventLog.postEvent(new ExperimentChangeEvent(user, experiment, "pages", "all pages", null)); } }