protected <T> Future<List<FDBMetric.Value<T>>> readAllValues( Transaction tr, final BaseMetricImpl<T> metric) { tr.options().setAccessSystemKeys(); return tr.getRange( dataSubspace.pack( tupleFrom(metric.getType(), metric.getName(), address, DEFAULT_ID, 0)), dataSubspace.pack( tupleFrom(metric.getType(), metric.getName(), address, DEFAULT_ID, NLEVELS))) .asList() .map( new Function<List<KeyValue>, List<FDBMetric.Value<T>>>() { @Override public List<FDBMetric.Value<T>> apply(List<KeyValue> kvs) { List<FDBMetric.Value<T>> result = new ArrayList<>(); for (KeyValue kv : kvs) { result.addAll(metric.decodeValues(kv.getValue())); } // Merge all levels. Collections.sort( result, new Comparator<FDBMetric.Value<T>>() { @Override public int compare(FDBMetric.Value<T> v1, FDBMetric.Value<T> v2) { if (v1.time < v2.time) return -1; else if (v1.time > v2.time) return +1; else return 0; } }); return result; } }); }
// Change a metric's enabled flag, either from configuration storage of at // user's request. protected void setEnabled(BaseMetricImpl<?> metric, boolean enabled, boolean explicit) { if (metric.enabled == enabled) return; boolean notifyBackground = false; synchronized (metric) { metric.enabled = enabled; if (enabled) { // As though just changed to current value. metricChanged(metric); } if (explicit) { metric.confChanged = true; } } if (enabled && !anyEnabled) { anyEnabled = true; notifyBackground = true; // So goes to sleep instead of indefinite wait. } if (explicit) { metricsConfChanged = true; notifyBackground = true; } if (notifyBackground) { notifyBackground(); } }
protected <T> void metricChanged(BaseMetricImpl<T> metric) { long lastTime = metric.changeTime; // NOTE: changeTime is only accurate for metrics that are enabled, and // to guarantee that it always corresponds to the value, updates to // enabled metrics are synchronized. metric.changeTime = timebase + System.nanoTime(); metric.valueChanged = true; metricsDataChanged = true; int level; if ((lastTime == 0) || (metric.changeTime <= lastTime)) { level = 0; } else { // Longer duration -> higher level. double r = random.nextDouble(); if (r == 0) { level = NLEVELS - 1; } else { level = Math.min( NLEVELS - 1, (int) (Math.log((metric.changeTime - lastTime) / r) / METRIC_LEVEL_DIVISOR)); } } MetricLevel<T> metricLevel = metric.levels.get(level); MetricLevelValues values = metricLevel.values.peekLast(); if ((values == null) || values.isFull()) { if (logger.isTraceEnabled()) { logger.trace( "New level {} entry at {} for {}", new Object[] {level, metric.changeTime, metric}); } values = new MetricLevelValues(metric.changeTime, metric.encodeValue()); metricLevel.values.addLast(values); } else { if (logger.isTraceEnabled()) { logger.trace( "Adding to level {} entry at {} for {}", new Object[] {level, metric.changeTime, metric}); } values.append(metric.encodeValue(metricLevel)); } metricLevel.lastValue = metric.getObject(); metricLevel.lastTime = metric.changeTime; }
protected void updateEnabled(BaseMetricImpl<?> metric) { List<String> keys = Arrays.asList(metric.getType(), metric.getName(), address, DEFAULT_ID, ENABLED_OPTION); byte[] enabled = conf.get(keys); if (enabled == null) { metric.confChanged = true; // Write conf key for brand new metric. // Check wildcard enabling. keys.set(2, ""); enabled = conf.get(keys); if (enabled != null) { setEnabled(metric, enabled[0] != 0, false); } metricsConfChanged = true; notifyBackground(); } else { setEnabled(metric, enabled[0] != 0, false); } }
protected void writeMetrics() { boolean anyConfChanges = false; for (BaseMetricImpl<?> metric : metrics.values()) { if (!metric.confChanged && !metric.valueChanged) continue; synchronized (metric) { if (metric.confChanged) { anyConfChanges = true; pendingWrites.add( new KeyValue( confSubspace.pack( tupleFrom( metric.getType(), metric.getName(), address, DEFAULT_ID, ENABLED_OPTION)), metric.enabled ? ENABLED_TRUE : ENABLED_FALSE)); metric.confChanged = false; } if (metric.valueChanged) { pendingWrites.add( new KeyValue( dataSubspace.pack( tupleFrom(metric.getType(), metric.getName(), address, DEFAULT_ID)), metric.encodeValue())); metric.valueChanged = false; } for (int level = 0; level < NLEVELS; level++) { MetricLevel<?> metricLevel = metric.levels.get(level); while (true) { MetricLevelValues values = metricLevel.values.pollFirst(); if (values == null) break; pendingWrites.add( new KeyValue( dataSubspace.pack( tupleFrom( metric.getType(), metric.getName(), address, DEFAULT_ID, level, values.start)), values.bytes)); // Continue to fill (will overwrite key with longer value). if (metricLevel.values.isEmpty() && !values.isFull()) { metricLevel.values.addLast(values); break; } } } } } if (anyConfChanges) { // Signal a change in configuration. We will respond to this change, // too, but that seemd harmless and difficult to avoid in a general // way. byte[] bytes = new byte[16]; random.nextBytes(bytes); if (logger.isDebugEnabled()) { logger.debug("Writing {}: {}", METRIC_CONF_CHANGES_KEY, ByteArrayUtil.printable(bytes)); } pendingWrites.add(new KeyValue(confChangesSubspace.getKey(), bytes)); } if (!pendingWrites.isEmpty()) { getDatabase() .run( new Function<Transaction, Void>() { @Override public Void apply(Transaction tr) { tr.options().setAccessSystemKeys(); for (KeyValue kv : pendingWrites) { tr.set(kv.getKey(), kv.getValue()); } return null; } }); pendingWrites.clear(); } }
protected void addMetric(BaseMetricImpl<?> metric) { if (metrics.putIfAbsent(metric.getName(), metric) != null) { throw new IllegalArgumentException("There is already a metric named " + metric.getName()); } updateEnabled(metric); // Get initial enabled state from conf. }