Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.dynamic.policy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;

/**
* Holds the latest validated policy snapshot and reports whether an update changed effective
* configuration.
*/
public final class PolicyStore {

private List<TelemetryPolicy> policies = Collections.emptyList();

/**
* Replaces the stored policies when the new snapshot is not equal to the current one.
*
* <p>Input lists are normalized to a set of distinct policies ({@link TelemetryPolicy#equals
* value equality}): duplicates are dropped and only the first occurrence of each policy is kept
* (insertion order). Change detection uses set equality, so list order does not matter. That
* matches telemetry policy semantics where the effective result does not depend on processing
* order (see the telemetry policy OTEP, commutativity / no user-defined ordering between
* policies).
*
* @return {@code true} if the store was updated, {@code false} if the snapshot was unchanged
*/
public synchronized boolean updatePolicies(List<TelemetryPolicy> newPolicies) {
Objects.requireNonNull(newPolicies, "newPolicies cannot be null");
LinkedHashSet<TelemetryPolicy> newPolicySet = new LinkedHashSet<>(newPolicies);
if (new LinkedHashSet<>(policies).equals(newPolicySet)) {
return false;
}
policies = new ArrayList<>(newPolicySet);
return true;
Comment thread
jackshirazi marked this conversation as resolved.
}

public synchronized List<TelemetryPolicy> getPolicies() {
return Collections.unmodifiableList(new ArrayList<>(policies));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
* <p>Direct instantiation of this base class is intentionally supported for type-only policy
* signals (for example, to indicate policy removal/reset without policy-specific values).
*
* <p><b>Subclasses:</b> {@link #equals(Object)} on this class returns {@code false} when either
* operand is a typed subclass (not {@code TelemetryPolicy} itself), so a type-only instance never
* equals a value-carrying subclass with the same {@link #getType() type}. Each concrete subclass
* <b>must</b> override both {@code equals} and {@code hashCode} consistently with its fields, and
* obey the {@code equals}/{@code hashCode} contract. That is required for consumers such as {@link
* io.opentelemetry.contrib.dynamic.policy.PolicyStore} that deduplicate and detect changes using
* {@link Object#equals(Object)}.
*
* @see io.opentelemetry.contrib.dynamic.policy
*/
public class TelemetryPolicy {
Expand Down Expand Up @@ -50,4 +58,29 @@ public TelemetryPolicy(String type) {
public String getType() {
return type;
}

/**
* Type-only policies ({@link TelemetryPolicy} instances) do not equal typed subclasses that share
* the same {@link #getType() type} string. Subclasses must override {@code equals} (and {@code
Comment thread
jackshirazi marked this conversation as resolved.
* hashCode}) for value-based equality; see the class Javadoc.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TelemetryPolicy)) {
return false;
}
TelemetryPolicy that = (TelemetryPolicy) obj;
if (that.getClass() != TelemetryPolicy.class || getClass() != TelemetryPolicy.class) {
return false;
}
return type.equals(that.type);
}
Comment thread
jackshirazi marked this conversation as resolved.

@Override
public int hashCode() {
return type.hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,21 @@ public TraceSamplingRatePolicy(double probability) {
public double getProbability() {
return probability;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TraceSamplingRatePolicy)) {
return false;
}
TraceSamplingRatePolicy that = (TraceSamplingRatePolicy) obj;
return Double.compare(probability, that.probability) == 0;
}

@Override
public int hashCode() {
return Double.hashCode(probability);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.dynamic.policy;

import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.contrib.dynamic.policy.tracesampling.TraceSamplingRatePolicy;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;

class PolicyStoreTest {

@Test
void updatePoliciesReturnsTrueOnFirstSet() {
PolicyStore store = new PolicyStore();
List<TelemetryPolicy> policies = singletonList(new TraceSamplingRatePolicy(0.5));

assertThat(store.updatePolicies(policies)).isTrue();
assertThat(store.getPolicies()).isEqualTo(policies);
}

@Test
void updatePoliciesReturnsFalseWhenEqualContent() {
PolicyStore store = new PolicyStore();
assertThat(store.updatePolicies(singletonList(new TraceSamplingRatePolicy(0.5)))).isTrue();
assertThat(store.updatePolicies(singletonList(new TraceSamplingRatePolicy(0.5)))).isFalse();
}

@Test
void updatePoliciesReturnsTrueWhenProbabilityChanges() {
PolicyStore store = new PolicyStore();
assertThat(store.updatePolicies(singletonList(new TraceSamplingRatePolicy(0.25)))).isTrue();
assertThat(store.updatePolicies(singletonList(new TraceSamplingRatePolicy(0.75)))).isTrue();
assertThat(store.getPolicies()).containsExactly(new TraceSamplingRatePolicy(0.75));
}

@Test
void updatePoliciesReturnsFalseWhenOnlyOrderDiffers() {
PolicyStore store = new PolicyStore();
List<TelemetryPolicy> first =
Arrays.asList(new TraceSamplingRatePolicy(0.1), new TraceSamplingRatePolicy(0.2));
List<TelemetryPolicy> reordered =
Arrays.asList(new TraceSamplingRatePolicy(0.2), new TraceSamplingRatePolicy(0.1));

assertThat(store.updatePolicies(first)).isTrue();
assertThat(store.updatePolicies(reordered)).isFalse();
assertThat(store.getPolicies()).isEqualTo(first);
}

@Test
void updatePoliciesIgnoresDuplicatePoliciesInInput() {
PolicyStore store = new PolicyStore();
TraceSamplingRatePolicy p = new TraceSamplingRatePolicy(0.5);
assertThat(store.updatePolicies(Arrays.asList(p, new TraceSamplingRatePolicy(0.5)))).isTrue();
assertThat(store.getPolicies()).containsExactly(p);
assertThat(store.updatePolicies(singletonList(new TraceSamplingRatePolicy(0.5)))).isFalse();
}

@Test
void getPoliciesReturnsEmptyWhenNeverUpdated() {
assertThat(new PolicyStore().getPolicies()).isEqualTo(Collections.emptyList());
}
}
Loading