/** * Builds the 'time' portion of the row key * @param period The ProfilePeriod in which the ProfileMeasurement was taken. */ private static byte[] timeKey(ProfilePeriod period) { return timeKey(period.getPeriod()); }
/** * When this period ended in milliseconds since the epoch. */ public long getEndTimeMillis() { return getStartTimeMillis() + getDurationMillis(); }
@Override public String toString() { return "ProfilePeriod{" + "period=" + period + ", durationMillis=" + durationMillis + ", startTime=" + Instant.ofEpochMilli(getStartTimeMillis()).toString() + ", endTime=" + Instant.ofEpochMilli(getEndTimeMillis()).toString() + '}'; }
public static <T> List<T> visitPeriods(long startEpochMillis , long endEpochMillis , long duration , TimeUnit units , Optional<Predicate<ProfilePeriod>> inclusionPredicate , Function<ProfilePeriod,T> transformation ) { ProfilePeriod period = ProfilePeriod.fromTimestamp(startEpochMillis, duration, units); List<T> ret = new ArrayList<>(); while(period.getStartTimeMillis() <= endEpochMillis) { if(!inclusionPredicate.isPresent() || inclusionPredicate.get().test(period)) { ret.add(transformation.apply(period)); } period = period.next(); } return ret; } }
@Override public Object apply(List<Object> args, Context context) throws ParseException { // user must provide the stand-alone profiler StandAloneProfiler profiler = Util.getArg(0, StandAloneProfiler.class, args); if(profiler == null) { throw new IllegalArgumentException(format("expected the profiler returned by PROFILER_INIT, found null")); } // transform the profile measurements into maps to simplify manipulation in stellar List<Map<String, Object>> measurements = new ArrayList<>(); for(ProfileMeasurement m : profiler.flush()) { // create a map for the profile period Map<String, Object> period = new HashMap<>(); period.put("period", m.getPeriod().getPeriod()); period.put("start", m.getPeriod().getStartTimeMillis()); period.put("duration", m.getPeriod().getDurationMillis()); period.put("end", m.getPeriod().getEndTimeMillis()); // create a map for the measurement Map<String, Object> measurement = new HashMap<>(); measurement.put("profile", m.getProfileName()); measurement.put("entity", m.getEntity()); measurement.put("value", m.getProfileValue()); measurement.put("groups", m.getGroups()); measurement.put("period", period); measurements.add(measurement); } return measurements; } }
/** * Creates a message that will be emitted to Kafka. * * @param measurement The profile measurement used as a basis for the message. * @return A message that can be emitted to Kafka. */ private JSONObject createMessage(ProfileMeasurement measurement) { JSONObject message = new JSONObject(); message.put(PROFILE_FIELD, measurement.getDefinition().getProfile()); message.put(ENTITY_FIELD, measurement.getEntity()); message.put(PERIOD_ID_FIELD, measurement.getPeriod().getPeriod()); message.put(PERIOD_START_FIELD, measurement.getPeriod().getStartTimeMillis()); message.put(PERIOD_END_FIELD, measurement.getPeriod().getEndTimeMillis()); message.put(TIMESTAMP_FIELD, System.currentTimeMillis()); message.put(Constants.SENSOR_TYPE, sourceType); message.put(ALERT_FIELD, "true"); message.put(Constants.GUID, UUID.randomUUID().toString()); return message; }
public ProfileMeasurementAdapter(ProfileMeasurement measurement) { this.profileName = measurement.getProfileName(); this.entity = measurement.getEntity(); this.periodId = measurement.getPeriod().getPeriod(); this.durationMillis = measurement.getPeriod().getDurationMillis(); this.profileValue = SerDeUtils.toBytes(measurement.getProfileValue()); }
@Override public String call(MessageRoute route) { ProfilePeriod period = ProfilePeriod.fromTimestamp(route.getTimestamp(), periodDuration, periodDurationUnits); return route.getProfileDefinition().getProfile() + "-" + route.getEntity() + "-" + period.getPeriod(); } }
IntervalPredicate<ProfilePeriod> intervalSelector = new IntervalPredicate<>(period -> period.getStartTimeMillis() , w.toIntervals(now) , ProfilePeriod.class ); return ProfilePeriod.visitPeriods(start, end, tickDuration, tickUnit, Optional.of(intervalSelector), period -> period);
/** * Fetch the values stored in a profile based on a start and end timestamp. * * @param clazz The type of values stored by the profile. * @param profile The name of the profile. * @param entity The name of the entity. * @param groups The groups used to sort the profile data. * @param start The start time in epoch milliseconds. * @param end The end time in epoch milliseconds. * @param defaultValue The default value to specify. If empty, the result will be sparse. * @param <T> The type of values stored by the profile. * @return A list of values. */ @Override public <T> List<ProfileMeasurement> fetch(Class<T> clazz, String profile, String entity, List<Object> groups, long start, long end, Optional<T> defaultValue) { List<ProfilePeriod> periods = ProfilePeriod.visitPeriods( start, end, periodDurationMillis, TimeUnit.MILLISECONDS, Optional.empty(), period -> period); return fetch(clazz, profile, entity, groups, periods, defaultValue); }
/** * Returns the next ProfilePeriod in time. */ public ProfilePeriod next() { return fromPeriodId(period + 1, durationMillis, TimeUnit.MILLISECONDS); }
public ProfileMeasurement withPeriod(long whenMillis, long periodDuration, TimeUnit periodUnits) { this.withPeriod(ProfilePeriod.fromTimestamp(whenMillis, periodDuration, periodUnits)); return this; }
/** * Handles the {@code ProfileMeasurement}s that are created when a profile is flushed. * * @param measurements The measurements to handle. */ private void emitMeasurements(List<ProfileMeasurement> measurements) { // flush each profile for(ProfileMeasurement measurement: measurements) { // allow each 'emitter' to emit the measurement for (ProfileMeasurementEmitter emitter : emitters) { emitter.emit(measurement, collector); LOG.debug("Measurement emitted; stream={}, profile={}, entity={}, value={}, start={}, end={}, duration={}, period={}", emitter.getStreamId(), measurement.getProfileName(), measurement.getEntity(), measurement.getProfileValue(), measurement.getPeriod().getStartTimeMillis(), measurement.getPeriod().getEndTimeMillis(), measurement.getPeriod().getDurationMillis(), measurement.getPeriod().getPeriod()); } } LOG.debug("Emitted {} measurement(s).", measurements.size()); }
/** * Renders a view of the profile measurement. * @param measurement The profile measurement to render. */ private Map<String, Object> render(ProfileMeasurement measurement) { Map<String, Object> view = new HashMap<>(); view.put(PROFILE_KEY, measurement.getProfileName()); view.put(ENTITY_KEY, measurement.getEntity()); view.put(PERIOD_KEY, measurement.getPeriod().getPeriod()); view.put(PERIOD_START_KEY, measurement.getPeriod().getStartTimeMillis()); view.put(PERIOD_END_KEY, measurement.getPeriod().getEndTimeMillis()); view.put(VALUE_KEY, measurement.getProfileValue()); view.put(GROUPS_KEY, measurement.getGroups()); return view; }
/** * Builds a list of row keys necessary to retrieve profile measurements over * a time horizon. * * @param profile The name of the profile. * @param entity The name of the entity. * @param groups The group(s) used to sort the profile data. * @param start When the time horizon starts in epoch milliseconds. * @param end When the time horizon ends in epoch milliseconds. * @return All of the row keys necessary to retrieve the profile measurements. */ @Override public List<byte[]> rowKeys(String profile, String entity, List<Object> groups, long start, long end) { // be forgiving of out-of-order start and end times; order is critical to this algorithm long max = Math.max(start, end); start = Math.min(start, end); end = max; // find the starting period and advance until the end time is reached return ProfilePeriod.visitPeriods( start , end , periodDurationMillis , TimeUnit.MILLISECONDS , Optional.empty() , period -> rowKey(profile, entity, period, groups) ); }
public ProfileMeasurement toProfileMeasurement() { ProfilePeriod period = ProfilePeriod.fromPeriodId(periodId, durationMillis, TimeUnit.MILLISECONDS); ProfileMeasurement measurement = new ProfileMeasurement() .withProfileName(profileName) .withEntity(entity) .withPeriod(period) .withProfileValue(SerDeUtils.fromBytes(profileValue, Object.class)); return measurement; }
public Optional<ProfileMeasurement> flush() { Optional<ProfileMeasurement> result; ProfilePeriod period = ProfilePeriod.fromTimestamp(maxTimestamp, periodDurationMillis, TimeUnit.MILLISECONDS); try { state.put("profile", profileName); state.put("entity", entity); state.put("start", period.getStartTimeMillis()); state.put("end", period.getEndTimeMillis()); state.put("duration", period.getDurationMillis()); state.put("result", profileValue); entity, maxTimestamp, period.getPeriod(), period.getStartTimeMillis(), period.getEndTimeMillis(), period.getDurationMillis());
@Override public void emit(ProfileMeasurement measurement, OutputCollector collector) { // measurements are always emitted to hbase collector.emit(getStreamId(), new Values(measurement)); LOG.debug("Emitted measurement; stream={}, profile={}, entity={}, period={}, start={}, end={}", getStreamId(), measurement.getProfileName(), measurement.getEntity(), measurement.getPeriod().getPeriod(), measurement.getPeriod().getStartTimeMillis(), measurement.getPeriod().getEndTimeMillis()); }
/** * Build the row key. * @param profile The name of the profile. * @param entity The name of the entity. * @param period The period in which the measurement was taken. * @param groups The groups. * @return The HBase row key. */ public byte[] rowKey(String profile, String entity, ProfilePeriod period, List<Object> groups) { return rowKey(profile, entity, period.getPeriod(), groups); }
@Override public Object apply(List<Object> args, Context context) throws ParseException { Optional<Map> configOverridesMap = Optional.empty(); long durationAgo = Util.getArg(0, Long.class, args); String unitsName = Util.getArg(1, String.class, args); TimeUnit units = TimeUnit.valueOf(unitsName); if(args.size() > 2) { Map rawMap = Util.getArg(2, Map.class, args); configOverridesMap = rawMap == null || rawMap.isEmpty() ? Optional.empty() : Optional.of(rawMap); } Map<String, Object> effectiveConfigs = Util.getEffectiveConfig(context, configOverridesMap.orElse(null)); Long tickDuration = ProfilerClientConfig.PROFILER_PERIOD.get(effectiveConfigs, Long.class); TimeUnit tickUnit = TimeUnit.valueOf(ProfilerClientConfig.PROFILER_PERIOD_UNITS.get(effectiveConfigs, String.class)); long end = System.currentTimeMillis(); long start = end - units.toMillis(durationAgo); return ProfilePeriod.visitPeriods(start, end, tickDuration, tickUnit, Optional.empty(), period -> period); }