/** * {@inheritDoc} */ @Override public BucketList combineOldAndNewBuckets(BucketList oldBuckets, BucketList newBuckets) { BucketList allBuckets = new BucketList(); for (Bucket b : oldBuckets.getBuckets()) { Boolean changed = false; for (Bucket bb : newBuckets.getBuckets()) { if (bb.getLabel().equals(b.getLabel())) { allBuckets.addBucket(bb); changed = true; break; } } if (!changed) { allBuckets.addBucket(b); } } return allBuckets; } }
/** * Creating a separate copy here to avoid ConcurrentModificationException as BucketList gets modified * during assignment business logic execution. * * @param bucketList * @return New object/deep copy of BucketList. */ private BucketList makeCopy(BucketList bucketList) { List<Bucket> iBucketList = new ArrayList<>(bucketList.getBuckets()); BucketList newBucketList = new BucketList(); newBucketList.setBuckets(iBucketList); return newBucketList; } }
/** * Improved way of getting BucketList for given experiments */ @Override public Map<Experiment.ID, BucketList> getBucketList(Collection<Experiment.ID> experimentIds) { LOGGER.debug("Getting buckets list by experimentIDs {}", experimentIds); Map<Experiment.ID, BucketList> bucketMap = new HashMap<>(); try { Map<Experiment.ID, ListenableFuture<Result<com.intuit.wasabi.repository.cassandra.pojo.Bucket>>> bucketFutureMap = new HashMap<>(); experimentIds.forEach(experimentId -> { bucketFutureMap.put(experimentId, bucketAccessor.asyncGetBucketByExperimentId(experimentId.getRawID())); }); for (Experiment.ID expId : bucketFutureMap.keySet()) { bucketMap.put(expId, new BucketList()); ListenableFuture<Result<com.intuit.wasabi.repository.cassandra.pojo.Bucket>> bucketFuture = bucketFutureMap.get(expId); UninterruptibleUtil.getUninterruptibly(bucketFuture).all().forEach(bucketPojo -> { bucketMap.get(expId).addBucket(BucketHelper.makeBucket(bucketPojo)); } ); } } catch (Exception e) { LOGGER.error("getBucketList for {} failed", experimentIds, e); throw new RepositoryException("Could not fetch buckets for the list of experiments", e); } LOGGER.debug("Returning bucketMap {}", bucketMap); return bucketMap; }
/** * Get the bucket based on experiment id and bucket label * If metadata cache is enabled then fetch from cache else fetch from database. * * @param experimentID * @param bucketLabel * @return */ protected Optional<Bucket> getBucketByLabel(Experiment.ID experimentID, Bucket.Label bucketLabel) { Optional<Bucket> rBucket = Optional.empty(); BucketList bucketList = null; if (metadataCacheEnabled) { bucketList = metadataCache.getBucketList(experimentID); } else { bucketList = repository.getBucketList(experimentID); } if (isNull(bucketList)) return rBucket; for (Bucket bucket : bucketList.getBuckets()) { if (bucket.getLabel().equals(bucketLabel)) { return Optional.of(bucket); } } return rBucket; }
BucketList bucketList = new BucketList(); bucketList.setBuckets(buckets);
/** * @param expId * @return BucketList for given experiment. */ @Override public BucketList getBucketList(Experiment.ID expId) { BucketList bucketList = null; Cache cache = cacheManager.getCache(EXPERIMENT_ID_TO_BUCKET_CACHE.toString()); Element val = cache.get(expId); if (isNull(val)) { bucketList = experimentRepository.getBucketList(singleEntrySet(expId)).get(expId); cache.put(new Element(expId, bucketList)); } else { bucketList = (BucketList) val.getObjectValue(); } return isNull(bucketList) ? new BucketList() : makeCopy(bucketList); }
experiment.getApplicationName(), userName.toString(), experiment.getLabel(), bucketList.toString());
/** * 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 BucketList getBucketList(Experiment.ID experimentID) { LOGGER.debug("Getting buckets list by one experimentId {}", experimentID); BucketList bucketList = new BucketList(); try { Result<com.intuit.wasabi.repository.cassandra.pojo.Bucket> bucketPojos = bucketAccessor.getBucketByExperimentId(experimentID.getRawID()); for (com.intuit.wasabi.repository.cassandra.pojo.Bucket bucketPojo : bucketPojos .all()) { bucketList.addBucket(BucketHelper.makeBucket(bucketPojo)); } } catch (Exception e) { LOGGER.error("Getting bucket list by one experiment id {} failed", experimentID, e); throw new RepositoryException("Could not fetch buckets for experiment \"" + experimentID + "\" ", e); } LOGGER.debug("Returning buckets list by one experimentId {} bucket {}", new Object[]{experimentID, bucketList}); return bucketList; }
BucketList changeBucketList = new BucketList(); for (Bucket bucket : bucketList.getBuckets()) { for (Bucket b : oldBuckets.getBuckets()) { if (b.getLabel().equals(bucket.getLabel())) { oldBucket = b; changeBucketList.addBucket(bucket); allChanges.add(changeList); BucketList allBuckets = new BucketList(); if (!changeBucketList.getBuckets().isEmpty()) { allBuckets = buckets.combineOldAndNewBuckets(oldBuckets, changeBucketList); validator.validateExperimentBuckets(allBuckets.getBuckets()); cassandraRepository.logBucketChanges(experimentID, changeBucketList.getBuckets().get(i).getLabel(), allChanges.get(i)); eventLog.postEvent(new BucketChangeEvent(user, experiment, changeBucketList.getBuckets().get(i), bucketAuditInfo.getAttributeName(), bucketAuditInfo.getOldValue(), bucketAuditInfo.getNewValue()));
int bucketListSize = bucketList.getBuckets().size(); StringBuilder SQL = new StringBuilder("UPDATE bucket SET "); List<String> args = new ArrayList<>(bucketListSize); if (bucketList.getBuckets().get(i).getState() != null) { hasval = true; break; SQL.append("state = CASE label "); for (int i = 0; i < bucketListSize; i++) { Bucket b = bucketList.getBuckets().get(i); if (b.getState() != null) { SQL.append("WHEN ? then ? "); if (bucketList.getBuckets().get(i).getAllocationPercent() != null) { hasval = true; break; SQL.append("allocation_percent = CASE label "); for (int i = 0; i < bucketListSize; i++) { Bucket b = bucketList.getBuckets().get(i); if (b.getAllocationPercent() != null) { SQL.append("WHEN ? then ? "); if (bucketList.getBuckets().get(i).isControl() != null) { hasval = true; break; SQL.append("is_control = CASE label ");
bucketMap.put(expId, new BucketList()); ListenableFuture<Result<com.intuit.wasabi.repository.cassandra.pojo.Bucket>> bucketFuture = bucketFutureMap.get(expId); UninterruptibleUtil.getUninterruptibly(bucketFuture).all().forEach(bucketPojo -> { bucketMap.get(expId).addBucket(BucketHelper.makeBucket(bucketPojo));
/** * {@inheritDoc} */ @Override public BucketList adjustAllocationPercentages(Experiment experiment, Bucket newBucket) { double remainingAlloc = 1. - newBucket.getAllocationPercent(); BucketList bucketList = buckets.getBuckets(experiment.getID(), false /* don't check experiment again */); BucketList newBuckets = new BucketList(); for (Bucket bucket : bucketList.getBuckets()) { if (bucket.getLabel().equals(newBucket.getLabel())) { continue; } double newAlloc = roundToTwo(remainingAlloc * bucket.getAllocationPercent()); LOGGER.debug("Add Bucket: setting allocation percentage for bucket " + bucket.getLabel() + " in experiment " + experiment.getID() + " to: " + newAlloc); Bucket.Builder builder = Bucket.from(bucket).withAllocationPercent(newAlloc); Bucket updatedBucket = builder.build(); newBuckets.addBucket(updatedBucket); if (!Experiment.State.DRAFT.equals(experiment.getState()) && Double.compare(bucket.getAllocationPercent(), updatedBucket.getAllocationPercent()) != 0) { // this is a system event, so no user needed eventLog.postEvent(new BucketChangeEvent(experiment, updatedBucket, "allocation", String.valueOf(bucket.getAllocationPercent()), String.valueOf(updatedBucket.getAllocationPercent()))); } } return newBuckets; }
for (int i = 0; i < bucketList.getBuckets().size(); i++) { Bucket b = bucketList.getBuckets().get(i); CQL += "UPDATE bucket SET "; if (b.getState() != null) {
experimentID); BucketList returnBuckets = new BucketList(); .build(); returnBuckets.addBucket(returnBuck);
BucketList newBuckets = new BucketList(); for (Bucket buck : bucketList.getBuckets()) { if (!buck.getLabel().equals(bucket.getLabel()) && buck.getState() == Bucket.State.OPEN) { for (int i = 0; i < bucketList.getBuckets().size(); i++) { Bucket buck = bucketList.getBuckets().get(i); List<Bucket.BucketAuditInfo> changeList = new ArrayList<>(); builder.withControl(buck.isControl()); builder.withDescription(buck.getDescription()); newBuckets.addBucket(builder.build()); validator.validateExperimentBuckets(newBuckets.getBuckets()); for (int i = 0; i < bucketList.getBuckets().size(); i++) { cassandraRepository.logBucketChanges(experimentID, bucketList.getBuckets().get(i).getLabel(), allChanges.get(i)); for (Bucket.BucketAuditInfo bucketAuditInfo : allChanges.get(i)) { if (!Experiment.State.DRAFT.equals(experiment.getState()) && !(Objects.equals(bucketAuditInfo.getOldValue(), bucketAuditInfo.getNewValue()))) { EventLogEvent event = new BucketChangeEvent(user, experiment, bucketList.getBuckets().get(i), bucketAuditInfo.getAttributeName(), bucketAuditInfo.getOldValue(), bucketAuditInfo.getNewValue()); for (Bucket buck : updates.getBuckets()) { if (buck.getLabel().equals(bucket.getLabel())) {
if ((Objects.isNull(bucketList) || bucketList.getBuckets().isEmpty()) && !skipBucketRetrieval) { bucketList = getBucketList(experiment.getID());
experiment.setBuckets(bucketList.getBuckets()); experiment.setExclusionIdList(exclusionIdList);
/** * {@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()); } } }
boolean isBucketEmpty = false; String bucketLabel = userAssignments.row(experimentID).values().iterator().next(); for (Bucket b : bucketList.getBuckets()) { if (bucketLabel.equals(b.getLabel().toString())) { if (b.getState() == Bucket.State.EMPTY) {