From 0e79d16c8d9b35284f0039166ccade963dfeef55 Mon Sep 17 00:00:00 2001 From: jwatson Date: Wed, 15 Apr 2020 14:38:58 -0700 Subject: [PATCH 1/5] add the sampling probability to the decision, when appropriate. --- .../io/opentelemetry/sdk/trace/Samplers.java | 46 +++++++++++++++++-- .../opentelemetry/sdk/trace/SamplersTest.java | 7 ++- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java index f9f77bb6782..aa8f88439bf 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java @@ -16,6 +16,9 @@ package io.opentelemetry.sdk.trace; +import static io.opentelemetry.common.AttributeValue.doubleAttributeValue; +import static java.util.Collections.singletonMap; + import com.google.auto.value.AutoValue; import com.google.common.base.Preconditions; import io.opentelemetry.common.AttributeValue; @@ -162,13 +165,21 @@ static Probability create(double probability) { } else { idUpperBound = (long) (probability * Long.MAX_VALUE); } - return new AutoValue_Samplers_Probability(probability, idUpperBound); + return new AutoValue_Samplers_Probability( + probability, + idUpperBound, + ProbabilityDecision.create(/* decision= */ true, probability), + ProbabilityDecision.create(/* decision= */ false, probability)); } abstract double getProbability(); abstract long getIdUpperBound(); + abstract Decision getPositiveDecision(); + + abstract Decision getNegativeDecision(); + @Override public final Decision shouldSample( @Nullable SpanContext parentContext, @@ -198,8 +209,8 @@ public final Decision shouldSample( // This is considered a reasonable tradeoff for the simplicity/performance requirements (this // code is executed in-line for every Span creation). return Math.abs(traceId.getLowerLong()) < getIdUpperBound() - ? ALWAYS_ON_DECISION - : ALWAYS_OFF_DECISION; + ? getPositiveDecision() + : getNegativeDecision(); } @Override @@ -233,4 +244,33 @@ public Map attributes() { return Collections.emptyMap(); } } + + /** Probability-based sampling decision with a single attribute for the probability. */ + @Immutable + @AutoValue + static abstract class ProbabilityDecision implements Decision { + + ProbabilityDecision() {} + + /** + * Creates sampling decision without attributes. + * + * @param decision sampling decision + * @param probability the probability that was used for the decision. + */ + static ProbabilityDecision create(boolean decision, double probability) { + return new AutoValue_Samplers_ProbabilityDecision(decision, singletonMap + ("samplingProbability", doubleAttributeValue(probability))); + } + + @Override + public abstract boolean isSampled(); + + public abstract Map getAttributes(); + + @Override + public Map attributes() { + return getAttributes(); + } + } } diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java index 21f53d0d30e..46184597444 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java @@ -17,6 +17,7 @@ package io.opentelemetry.sdk.trace; import static com.google.common.truth.Truth.assertThat; +import static io.opentelemetry.common.AttributeValue.doubleAttributeValue; import com.google.common.truth.Truth; import io.opentelemetry.common.AttributeValue; @@ -267,7 +268,8 @@ public void probabilitySampler_SampleBasedOnTraceId() { Collections.emptyMap(), Collections.emptyList()); assertThat(decision1.isSampled()).isFalse(); - assertThat(decision1.attributes()).isEmpty(); + assertThat(decision1.attributes()) + .containsExactly("samplingProbability", doubleAttributeValue(0.0001)); // This traceId will be sampled by the Probability Sampler because the first 8 bytes as long // is less than probability * Long.MAX_VALUE; TraceId sampledtraceId = @@ -301,6 +303,7 @@ public void probabilitySampler_SampleBasedOnTraceId() { Collections.emptyMap(), Collections.emptyList()); assertThat(decision2.isSampled()).isTrue(); - assertThat(decision2.attributes()).isEmpty(); + assertThat(decision1.attributes()) + .containsExactly("samplingProbability", doubleAttributeValue(0.0001)); } } From 9b0e50210c9befe355da70cf5eea06e902fbc612 Mon Sep 17 00:00:00 2001 From: jwatson Date: Wed, 15 Apr 2020 15:02:07 -0700 Subject: [PATCH 2/5] formatting --- sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java index aa8f88439bf..cbe6391befd 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java @@ -248,7 +248,7 @@ public Map attributes() { /** Probability-based sampling decision with a single attribute for the probability. */ @Immutable @AutoValue - static abstract class ProbabilityDecision implements Decision { + abstract static class ProbabilityDecision implements Decision { ProbabilityDecision() {} @@ -259,8 +259,8 @@ static abstract class ProbabilityDecision implements Decision { * @param probability the probability that was used for the decision. */ static ProbabilityDecision create(boolean decision, double probability) { - return new AutoValue_Samplers_ProbabilityDecision(decision, singletonMap - ("samplingProbability", doubleAttributeValue(probability))); + return new AutoValue_Samplers_ProbabilityDecision( + decision, singletonMap("samplingProbability", doubleAttributeValue(probability))); } @Override From 204b4d33708a3b5e8b8d53e7d79f38b3fd9540e9 Mon Sep 17 00:00:00 2001 From: jwatson Date: Thu, 16 Apr 2020 08:26:04 -0700 Subject: [PATCH 3/5] normalize a method name and move attribute to semantic convention attributes --- .../trace/attributes/SemanticAttributes.java | 4 ++++ .../java/io/opentelemetry/sdk/trace/Sampler.java | 2 +- .../java/io/opentelemetry/sdk/trace/Samplers.java | 13 ++++++------- .../io/opentelemetry/sdk/trace/SpanBuilderSdk.java | 2 +- .../io/opentelemetry/sdk/trace/SamplersTest.java | 11 +++++++---- .../opentelemetry/sdk/trace/SpanBuilderSdkTest.java | 2 +- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java b/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java index 42244550d3d..65f0cb219db 100644 --- a/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java +++ b/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java @@ -125,5 +125,9 @@ public final class SemanticAttributes { /** JDBC substring like "mysql://db.example.com:3306" */ public static final StringAttributeSetter DB_URL = StringAttributeSetter.create("db.url"); + /** Probability value used by a probability-based Span sampling strategy. */ + public static final DoubleAttributeSetter SAMPLING_PROBABILITY = + DoubleAttributeSetter.create("sampling.probability"); + private SemanticAttributes() {} } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/Sampler.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/Sampler.java index be862472e72..be0d7fab2c5 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/Sampler.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/Sampler.java @@ -92,6 +92,6 @@ interface Decision { * span or when sampling decision {@link #isSampled()} changes from false to true. * @since 0.1.0 */ - Map attributes(); + Map getAttributes(); } } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java index cbe6391befd..331b32e535e 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java @@ -28,6 +28,7 @@ import io.opentelemetry.trace.SpanContext; import io.opentelemetry.trace.SpanId; import io.opentelemetry.trace.TraceId; +import io.opentelemetry.trace.attributes.SemanticAttributes; import java.util.Collections; import java.util.List; import java.util.Map; @@ -240,7 +241,7 @@ public boolean isSampled() { } @Override - public Map attributes() { + public Map getAttributes() { return Collections.emptyMap(); } } @@ -260,17 +261,15 @@ abstract static class ProbabilityDecision implements Decision { */ static ProbabilityDecision create(boolean decision, double probability) { return new AutoValue_Samplers_ProbabilityDecision( - decision, singletonMap("samplingProbability", doubleAttributeValue(probability))); + decision, + singletonMap( + SemanticAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(probability))); } @Override public abstract boolean isSampled(); - public abstract Map getAttributes(); - @Override - public Map attributes() { - return getAttributes(); - } + public abstract Map getAttributes(); } } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/SpanBuilderSdk.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/SpanBuilderSdk.java index cf3d9fef1b4..59b9a92d267 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/SpanBuilderSdk.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/SpanBuilderSdk.java @@ -230,7 +230,7 @@ public Span startSpan() { return DefaultSpan.create(spanContext); } - attributes.putAllAttributes(samplingDecision.attributes()); + attributes.putAllAttributes(samplingDecision.getAttributes()); return RecordEventsReadableSpan.startSpan( spanContext, diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java index 46184597444..37674d72d8e 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java @@ -30,6 +30,7 @@ import io.opentelemetry.trace.TraceFlags; import io.opentelemetry.trace.TraceId; import io.opentelemetry.trace.TraceState; +import io.opentelemetry.trace.attributes.SemanticAttributes; import java.util.Collections; import java.util.List; import org.junit.Rule; @@ -268,8 +269,9 @@ public void probabilitySampler_SampleBasedOnTraceId() { Collections.emptyMap(), Collections.emptyList()); assertThat(decision1.isSampled()).isFalse(); - assertThat(decision1.attributes()) - .containsExactly("samplingProbability", doubleAttributeValue(0.0001)); + assertThat(decision1.getAttributes()) + .containsExactly( + SemanticAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); // This traceId will be sampled by the Probability Sampler because the first 8 bytes as long // is less than probability * Long.MAX_VALUE; TraceId sampledtraceId = @@ -303,7 +305,8 @@ public void probabilitySampler_SampleBasedOnTraceId() { Collections.emptyMap(), Collections.emptyList()); assertThat(decision2.isSampled()).isTrue(); - assertThat(decision1.attributes()) - .containsExactly("samplingProbability", doubleAttributeValue(0.0001)); + assertThat(decision1.getAttributes()) + .containsExactly( + SemanticAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); } } diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/SpanBuilderSdkTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/SpanBuilderSdkTest.java index fc52f9973df..9b0cac9e592 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/SpanBuilderSdkTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/SpanBuilderSdkTest.java @@ -305,7 +305,7 @@ public boolean isSampled() { } @Override - public Map attributes() { + public Map getAttributes() { Map attributes = new LinkedHashMap<>(); attributes.put( samplerAttributeName, AttributeValue.stringAttributeValue("bar")); From 8ffc5903b256575e3f9d6f575844a47845cb5e74 Mon Sep 17 00:00:00 2001 From: jwatson Date: Mon, 20 Apr 2020 13:33:37 -0700 Subject: [PATCH 4/5] move the sampling attribute to its own class in the SDK --- .../trace/attributes/SemanticAttributes.java | 4 --- .../io/opentelemetry/sdk/trace/Samplers.java | 3 +- .../sdk/trace/SamplingAttributes.java | 36 +++++++++++++++++++ .../opentelemetry/sdk/trace/SamplersTest.java | 5 ++- 4 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java diff --git a/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java b/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java index 65f0cb219db..42244550d3d 100644 --- a/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java +++ b/api/src/main/java/io/opentelemetry/trace/attributes/SemanticAttributes.java @@ -125,9 +125,5 @@ public final class SemanticAttributes { /** JDBC substring like "mysql://db.example.com:3306" */ public static final StringAttributeSetter DB_URL = StringAttributeSetter.create("db.url"); - /** Probability value used by a probability-based Span sampling strategy. */ - public static final DoubleAttributeSetter SAMPLING_PROBABILITY = - DoubleAttributeSetter.create("sampling.probability"); - private SemanticAttributes() {} } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java index 331b32e535e..25c3fb0d8be 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java @@ -28,7 +28,6 @@ import io.opentelemetry.trace.SpanContext; import io.opentelemetry.trace.SpanId; import io.opentelemetry.trace.TraceId; -import io.opentelemetry.trace.attributes.SemanticAttributes; import java.util.Collections; import java.util.List; import java.util.Map; @@ -263,7 +262,7 @@ static ProbabilityDecision create(boolean decision, double probability) { return new AutoValue_Samplers_ProbabilityDecision( decision, singletonMap( - SemanticAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(probability))); + SamplingAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(probability))); } @Override diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java new file mode 100644 index 00000000000..331dc47fcae --- /dev/null +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020, OpenTelemetry Authors + * + * 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 io.opentelemetry.sdk.trace; + +import io.opentelemetry.trace.attributes.DoubleAttributeSetter; + +/** Attributes that are applied by {@link Sampler} instances. */ +public class SamplingAttributes { + + /** + * Probability value used by a probability-based Span sampling strategy. + * + *

Note: This will need to be updated if a specification for this value is merged which changes + * this proposed value. + * + *

See https://github.com/open-telemetry/opentelemetry-specification/pull/570 + */ + public static final DoubleAttributeSetter SAMPLING_PROBABILITY = + DoubleAttributeSetter.create("sampling.probability"); + + private SamplingAttributes() {} +} diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java index 37674d72d8e..eec4a2b1d65 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java @@ -30,7 +30,6 @@ import io.opentelemetry.trace.TraceFlags; import io.opentelemetry.trace.TraceId; import io.opentelemetry.trace.TraceState; -import io.opentelemetry.trace.attributes.SemanticAttributes; import java.util.Collections; import java.util.List; import org.junit.Rule; @@ -271,7 +270,7 @@ public void probabilitySampler_SampleBasedOnTraceId() { assertThat(decision1.isSampled()).isFalse(); assertThat(decision1.getAttributes()) .containsExactly( - SemanticAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); + SamplingAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); // This traceId will be sampled by the Probability Sampler because the first 8 bytes as long // is less than probability * Long.MAX_VALUE; TraceId sampledtraceId = @@ -307,6 +306,6 @@ public void probabilitySampler_SampleBasedOnTraceId() { assertThat(decision2.isSampled()).isTrue(); assertThat(decision1.getAttributes()) .containsExactly( - SemanticAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); + SamplingAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); } } From 94ab8f525ed0b3947e615e97c54c1d191c779c79 Mon Sep 17 00:00:00 2001 From: jwatson Date: Mon, 20 Apr 2020 16:31:32 -0700 Subject: [PATCH 5/5] Move the sampling priority attribute to a non-public spot --- .../io/opentelemetry/sdk/trace/Samplers.java | 17 +++++++-- .../sdk/trace/SamplingAttributes.java | 36 ------------------- .../opentelemetry/sdk/trace/SamplersTest.java | 6 ++-- 3 files changed, 16 insertions(+), 43 deletions(-) delete mode 100644 sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java index 25c3fb0d8be..a0c7c2cdf7a 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/Samplers.java @@ -28,6 +28,7 @@ import io.opentelemetry.trace.SpanContext; import io.opentelemetry.trace.SpanId; import io.opentelemetry.trace.TraceId; +import io.opentelemetry.trace.attributes.DoubleAttributeSetter; import java.util.Collections; import java.util.List; import java.util.Map; @@ -41,6 +42,7 @@ */ @Immutable public final class Samplers { + private static final Sampler ALWAYS_ON = new AlwaysOnSampler(); private static final Sampler ALWAYS_OFF = new AlwaysOffSampler(); private static final Decision ALWAYS_ON_DECISION = new SimpleDecision(/* decision= */ true); @@ -245,6 +247,17 @@ public Map getAttributes() { } } + /** + * Probability value used by a probability-based Span sampling strategy. + * + *

Note: This will need to be updated if a specification for this value is merged which changes + * this proposed value. Also, once it's in the spec, we should move it somewhere more visible. + * + *

See https://github.com/open-telemetry/opentelemetry-specification/pull/570 + */ + static final DoubleAttributeSetter SAMPLING_PROBABILITY = + DoubleAttributeSetter.create("sampling.probability"); + /** Probability-based sampling decision with a single attribute for the probability. */ @Immutable @AutoValue @@ -260,9 +273,7 @@ abstract static class ProbabilityDecision implements Decision { */ static ProbabilityDecision create(boolean decision, double probability) { return new AutoValue_Samplers_ProbabilityDecision( - decision, - singletonMap( - SamplingAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(probability))); + decision, singletonMap(SAMPLING_PROBABILITY.key(), doubleAttributeValue(probability))); } @Override diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java deleted file mode 100644 index 331dc47fcae..00000000000 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/SamplingAttributes.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020, OpenTelemetry Authors - * - * 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 io.opentelemetry.sdk.trace; - -import io.opentelemetry.trace.attributes.DoubleAttributeSetter; - -/** Attributes that are applied by {@link Sampler} instances. */ -public class SamplingAttributes { - - /** - * Probability value used by a probability-based Span sampling strategy. - * - *

Note: This will need to be updated if a specification for this value is merged which changes - * this proposed value. - * - *

See https://github.com/open-telemetry/opentelemetry-specification/pull/570 - */ - public static final DoubleAttributeSetter SAMPLING_PROBABILITY = - DoubleAttributeSetter.create("sampling.probability"); - - private SamplingAttributes() {} -} diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java index eec4a2b1d65..fb8c1812eaf 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/SamplersTest.java @@ -269,8 +269,7 @@ public void probabilitySampler_SampleBasedOnTraceId() { Collections.emptyList()); assertThat(decision1.isSampled()).isFalse(); assertThat(decision1.getAttributes()) - .containsExactly( - SamplingAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); + .containsExactly(Samplers.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); // This traceId will be sampled by the Probability Sampler because the first 8 bytes as long // is less than probability * Long.MAX_VALUE; TraceId sampledtraceId = @@ -305,7 +304,6 @@ public void probabilitySampler_SampleBasedOnTraceId() { Collections.emptyList()); assertThat(decision2.isSampled()).isTrue(); assertThat(decision1.getAttributes()) - .containsExactly( - SamplingAttributes.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); + .containsExactly(Samplers.SAMPLING_PROBABILITY.key(), doubleAttributeValue(0.0001)); } }