diff --git a/driver-core/src/test/java/com/datastax/driver/core/tracing/BasicTracingTest.java b/driver-core/src/test/java/com/datastax/driver/core/tracing/BasicTracingTest.java new file mode 100644 index 00000000000..0ec977a8433 --- /dev/null +++ b/driver-core/src/test/java/com/datastax/driver/core/tracing/BasicTracingTest.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2021 ScyllaDB + * + * 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 com.datastax.driver.core.tracing; + +import static org.junit.Assert.assertNotNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.CCMTestsSupport; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.SimpleStatement; +import com.datastax.driver.core.policies.DefaultRetryPolicy; +import com.datastax.driver.core.policies.FallthroughRetryPolicy; +import com.datastax.driver.core.policies.NoSpeculativeExecutionPolicy; +import com.datastax.driver.core.policies.PagingOptimizingLoadBalancingPolicy; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutionException; +import org.testng.annotations.Test; + +public class BasicTracingTest extends CCMTestsSupport { + private static TestTracingInfoFactory testTracingInfoFactory; + private Session session; + + @Override + public void onTestContextInitialized() { + initializeTestTracing(); + session.execute("USE " + keyspace); + session.execute("DROP TABLE IF EXISTS t"); + session.execute("CREATE TABLE t (k int PRIMARY KEY, v int)"); + session.execute("CREATE TABLE blobs (k int PRIMARY KEY, v blob)"); + session.execute("INSERT INTO t(k, v) VALUES (2, 3)"); + session.execute("INSERT INTO t(k, v) VALUES (1, 7)"); + session.execute("INSERT INTO t(k, v) VALUES (5, 7)"); + session.execute("INSERT INTO t(k, v) VALUES (6, 7)"); + session.execute("INSERT INTO t(k, v) VALUES (7, 7)"); + session.execute("INSERT INTO t(k, v) VALUES (8, 7)"); + session.execute("INSERT INTO t(k, v) VALUES (9, 7)"); + session.execute("INSERT INTO t(k, v) VALUES (10, 7)"); + + Collection spans = testTracingInfoFactory.getSpans(); + spans.clear(); + } + + @Test(groups = "short") + public void simpleTracingTest() { + session.execute("INSERT INTO t(k, v) VALUES (4, 5)"); + + Collection spans = testTracingInfoFactory.getSpans(); + assertNotEquals(spans.size(), 0); + + TracingInfo rootSpan = getRoot(spans); + assertTrue(rootSpan instanceof TestTracingInfo); + TestTracingInfo root = (TestTracingInfo) rootSpan; + + assertTrue(root.isSpanStarted()); + assertTrue(root.isSpanFinished()); + assertEquals(root.getStatusCode(), TracingInfo.StatusCode.OK); + + spans.clear(); + } + + @Test(groups = "short") + public void tagsInsertTest() { + PreparedStatement prepared = session.prepare("INSERT INTO blobs(k, v) VALUES (?, ?)"); + + Collection prepareSpans = testTracingInfoFactory.getSpans(); + assertNotEquals(prepareSpans.size(), 0); + assertTrue(getRoot(prepareSpans) instanceof TestTracingInfo); + prepareSpans.clear(); + + BoundStatement bound = prepared.bind(1, ByteBuffer.wrap("\n\0\n".getBytes())); + session.execute(bound); + + Collection spans = testTracingInfoFactory.getSpans(); + assertNotEquals(spans.size(), 0); + + TracingInfo rootSpan = getRoot(spans); + assertTrue(rootSpan instanceof TestTracingInfo); + TestTracingInfo root = (TestTracingInfo) rootSpan; + + assertTrue(root.isSpanStarted()); + assertTrue(root.isSpanFinished()); + assertEquals(root.getStatusCode(), TracingInfo.StatusCode.OK); + + // these tags should be set for request span + assertEquals(root.getStatementType(), "prepared"); + assertNull(root.getBatchSize()); + assertEquals(root.getConsistencyLevel(), ConsistencyLevel.ONE); + assertNull(root.getRowsCount()); // no rows are returned in INSERT + assertTrue(root.getLoadBalancingPolicy() instanceof PagingOptimizingLoadBalancingPolicy); + assertTrue(root.getSpeculativeExecutionPolicy() instanceof NoSpeculativeExecutionPolicy); + assertTrue(root.getRetryPolicy() instanceof DefaultRetryPolicy); + assertNull(root.getFetchSize()); // fetch size was not explicitly set for this statement + assertNull(root.getHasMorePages()); // no paging are returned in INSERT + assertNull(root.getStatement()); // because of precision level NORMAL + // these are tags specific to bound statement + assertEquals(root.getKeyspace(), keyspace); + assertEquals(root.getBoundValues(), "k=1, v=0x0A000A"); // "\n\0\n" + assertEquals(root.getPartitionKey(), "k=1"); + assertEquals(root.getTable(), "blobs"); + + // these tags should not be set for request span + assertNull(root.getPeerName()); + assertNull(root.getPeerIP()); + assertNull(root.getPeerPort()); + assertNull(root.getAttemptCount()); + + ArrayList speculativeExecutions = getChildren(spans, root); + assertTrue(speculativeExecutions.size() > 0); + + for (TracingInfo speculativeExecutionSpan : speculativeExecutions) { + assertTrue(speculativeExecutionSpan instanceof TestTracingInfo); + TestTracingInfo tracingInfo = (TestTracingInfo) speculativeExecutionSpan; + + // these tags should not be set for speculative execution span + assertNull(tracingInfo.getStatementType()); + assertNull(tracingInfo.getBatchSize()); + assertNull(tracingInfo.getConsistencyLevel()); + assertNull(tracingInfo.getRowsCount()); + assertNull(tracingInfo.getLoadBalancingPolicy()); + assertNull(tracingInfo.getRetryPolicy()); + assertNull(tracingInfo.getFetchSize()); + assertNull(tracingInfo.getHasMorePages()); + assertNull(tracingInfo.getStatement()); + assertNull(tracingInfo.getPeerName()); + assertNull(tracingInfo.getPeerIP()); + assertNull(tracingInfo.getPeerPort()); + // these are tags specific to bound statement + assertNull(tracingInfo.getKeyspace()); + assertNull(tracingInfo.getPartitionKey()); + assertNull(tracingInfo.getTable()); + + // this tag should be set for speculative execution span + assertTrue(tracingInfo.getAttemptCount() >= 1); + } + + ArrayList attempts = new ArrayList(); + for (TracingInfo tracingInfo : speculativeExecutions) { + attempts.addAll(getChildren(spans, tracingInfo)); + } + assertTrue(attempts.size() > 0); + + for (TracingInfo attemptSpan : attempts) { + assertTrue(attemptSpan instanceof TestTracingInfo); + TestTracingInfo tracingInfo = (TestTracingInfo) attemptSpan; + + // these tags should not be set for attempt span + assertNull(tracingInfo.getStatementType()); + assertNull(tracingInfo.getBatchSize()); + assertNull(tracingInfo.getConsistencyLevel()); + assertNull(tracingInfo.getRowsCount()); + assertNull(tracingInfo.getLoadBalancingPolicy()); + assertNull(tracingInfo.getRetryPolicy()); + assertNull(tracingInfo.getFetchSize()); + assertNull(tracingInfo.getHasMorePages()); + assertNull(tracingInfo.getStatement()); + assertNull(tracingInfo.getAttemptCount()); + // these are tags specific to bound statement + assertNull(tracingInfo.getKeyspace()); + assertNull(tracingInfo.getPartitionKey()); + assertNull(tracingInfo.getTable()); + + // these tags should be set for attempt span + assertNotNull(tracingInfo.getPeerName()); + assertNotNull(tracingInfo.getPeerIP()); + assertNotNull(tracingInfo.getPeerPort()); + assertTrue(tracingInfo.getPeerPort() >= 0 && tracingInfo.getPeerPort() <= 65535); + } + + spans.clear(); + } + + @Test(groups = "short") + public void tagsSelectTest() { + SimpleStatement s = new SimpleStatement("SELECT k FROM t WHERE v = 7 ALLOW FILTERING"); + s.setFetchSize(2); + s.setIdempotent(true); + s.setRetryPolicy(FallthroughRetryPolicy.INSTANCE); + s.setConsistencyLevel(ConsistencyLevel.QUORUM); + + final Collection spans = testTracingInfoFactory.getSpans(); + class SpanChecks { + int totalRows = 0; + + void checkTotalCount() { + assertEquals(totalRows, 7); + } + + void checkAssertions(boolean hasMorePages) { + assertEquals(spans.size(), 3); + + TracingInfo rootSpan = getRoot(spans); + assertTrue(rootSpan instanceof TestTracingInfo); + TestTracingInfo root = (TestTracingInfo) rootSpan; + + assertTrue(root.isSpanStarted()); + assertTrue(root.isSpanFinished()); + assertEquals(root.getStatusCode(), TracingInfo.StatusCode.OK); + + // these tags should be set for request span + assertEquals(root.getStatementType(), "regular"); + assertNull(root.getBatchSize()); + assertEquals(root.getConsistencyLevel(), ConsistencyLevel.QUORUM); + assertNotNull(root.getRowsCount()); + totalRows += root.getRowsCount(); + assertTrue(root.getLoadBalancingPolicy() instanceof PagingOptimizingLoadBalancingPolicy); + assertTrue(root.getSpeculativeExecutionPolicy() instanceof NoSpeculativeExecutionPolicy); + assertTrue(root.getRetryPolicy() == FallthroughRetryPolicy.INSTANCE); + assertEquals(root.getFetchSize(), new Integer(2)); + assertEquals(root.getHasMorePages(), new Boolean(hasMorePages)); + assertNull(root.getStatement()); // because of precision level NORMAL + + // these are tags specific to bound statement + assertNull(root.getKeyspace()); + assertNull(root.getPartitionKey()); + assertNull(root.getTable()); + + // these tags should not be set for request span + assertNull(root.getPeerName()); + assertNull(root.getPeerIP()); + assertNull(root.getPeerPort()); + assertNull(root.getAttemptCount()); + + ArrayList speculativeExecutions = getChildren(spans, root); + assertTrue(speculativeExecutions.size() > 0); + + for (TracingInfo speculativeExecutionSpan : speculativeExecutions) { + assertTrue(speculativeExecutionSpan instanceof TestTracingInfo); + TestTracingInfo tracingInfo = (TestTracingInfo) speculativeExecutionSpan; + + // these tags should not be set for speculative execution span + assertNull(tracingInfo.getStatementType()); + assertNull(tracingInfo.getBatchSize()); + assertNull(tracingInfo.getConsistencyLevel()); + assertNull(tracingInfo.getRowsCount()); + assertNull(tracingInfo.getLoadBalancingPolicy()); + assertNull(tracingInfo.getRetryPolicy()); + assertNull(tracingInfo.getFetchSize()); + assertNull(tracingInfo.getHasMorePages()); + assertNull(tracingInfo.getStatement()); + assertNull(tracingInfo.getPeerName()); + assertNull(tracingInfo.getPeerIP()); + assertNull(tracingInfo.getPeerPort()); + // these are tags specific to bound statement + assertNull(tracingInfo.getKeyspace()); + assertNull(tracingInfo.getPartitionKey()); + assertNull(tracingInfo.getTable()); + + // this tag should be set for speculative execution span + assertTrue(tracingInfo.getAttemptCount() >= 1); + } + + ArrayList attempts = new ArrayList(); + for (TracingInfo tracingInfo : speculativeExecutions) { + attempts.addAll(getChildren(spans, tracingInfo)); + } + assertTrue(attempts.size() > 0); + + for (TracingInfo attemptSpan : attempts) { + assertTrue(attemptSpan instanceof TestTracingInfo); + TestTracingInfo tracingInfo = (TestTracingInfo) attemptSpan; + + // these tags should not be set for attempt span + assertNull(tracingInfo.getStatementType()); + assertNull(tracingInfo.getBatchSize()); + assertNull(tracingInfo.getConsistencyLevel()); + assertNull(tracingInfo.getRowsCount()); + assertNull(tracingInfo.getLoadBalancingPolicy()); + assertNull(tracingInfo.getRetryPolicy()); + assertNull(tracingInfo.getFetchSize()); + assertNull(tracingInfo.getHasMorePages()); + assertNull(tracingInfo.getStatement()); + assertNull(tracingInfo.getAttemptCount()); + // these are tags specific to bound statement + assertNull(tracingInfo.getKeyspace()); + assertNull(tracingInfo.getPartitionKey()); + assertNull(tracingInfo.getTable()); + + // these tags should be set for attempt span + assertNotNull(tracingInfo.getPeerName()); + assertNotNull(tracingInfo.getPeerIP()); + assertNotNull(tracingInfo.getPeerPort()); + assertTrue(tracingInfo.getPeerPort() >= 0 && tracingInfo.getPeerPort() <= 65535); + } + + spans.clear(); + } + } + + SpanChecks spanChecks = new SpanChecks(); + + try { + ResultSet rs = session.execute(s); + + while (!rs.isFullyFetched()) { + spanChecks.checkAssertions(true); + rs.fetchMoreResults().get(); + } + spanChecks.checkAssertions(false); + + } catch (InterruptedException e) { + assert false : "InterruptedException"; + } catch (ExecutionException e) { + assert false : "ExecutionException"; + } + spanChecks.checkTotalCount(); + } + + private void initializeTestTracing() { + testTracingInfoFactory = new TestTracingInfoFactory(PrecisionLevel.NORMAL); + cluster().setTracingInfoFactory(testTracingInfoFactory); + session = cluster().connect(); + } + + private TracingInfo getRoot(Collection spans) { + TracingInfo root = null; + for (TracingInfo tracingInfo : spans) { + if (tracingInfo instanceof TestTracingInfo + && ((TestTracingInfo) tracingInfo).getParent() == null) { + assertNull(root); // There should be only one root. + root = tracingInfo; + } + } + + return root; + } + + private ArrayList getChildren(Collection spans, TracingInfo parent) { + ArrayList children = new ArrayList(); + for (TracingInfo tracingInfo : spans) { + if (tracingInfo instanceof TestTracingInfo + && ((TestTracingInfo) tracingInfo).getParent() == parent) { + children.add(tracingInfo); + } + } + return children; + } +} diff --git a/driver-core/src/test/java/com/datastax/driver/core/tracing/TestTracingInfo.java b/driver-core/src/test/java/com/datastax/driver/core/tracing/TestTracingInfo.java new file mode 100644 index 00000000000..c5f8fc89c6b --- /dev/null +++ b/driver-core/src/test/java/com/datastax/driver/core/tracing/TestTracingInfo.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2021 ScyllaDB + * + * 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 com.datastax.driver.core.tracing; + +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.policies.LoadBalancingPolicy; +import com.datastax.driver.core.policies.RetryPolicy; +import com.datastax.driver.core.policies.SpeculativeExecutionPolicy; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; + +public class TestTracingInfo implements TracingInfo { + + private final PrecisionLevel precision; + private TracingInfo parent = null; + + private boolean spanStarted = false; + private boolean spanFinished = false; + private String spanName; + private ConsistencyLevel consistencyLevel; + private String statement; + private String statementType; + private Collection exceptions; + private StatusCode statusCode; + private String description; + private InetAddress peerIP; + private RetryPolicy retryPolicy; + private LoadBalancingPolicy loadBalancingPolicy; + private SpeculativeExecutionPolicy speculativeExecutionPolicy; + private Integer batchSize; + private Integer attemptCount; + private Integer shardID; + private String peerName; + private Integer peerPort; + private Integer fetchSize; + private Boolean hasMorePages; + private Integer rowsCount; + private String keyspace; + private String boundValues; + private String partitionKey; + private String table; + private String operationType; + private String replicas; + + public TestTracingInfo(PrecisionLevel precision) { + this.precision = precision; + } + + public TestTracingInfo(PrecisionLevel precision, TracingInfo parent) { + this(precision); + this.parent = parent; + } + + public PrecisionLevel getPrecision() { + return precision; + } + + @Override + public void setNameAndStartTime(String name) { + this.spanStarted = true; + this.spanName = name; + } + + @Override + public void setConsistencyLevel(ConsistencyLevel consistency) { + this.consistencyLevel = consistency; + } + + @Override + public void setStatementType(String statementType) { + this.statementType = statementType; + } + + @Override + public void setRetryPolicy(RetryPolicy retryPolicy) { + this.retryPolicy = retryPolicy; + } + + @Override + public void setLoadBalancingPolicy(LoadBalancingPolicy loadBalancingPolicy) { + this.loadBalancingPolicy = loadBalancingPolicy; + } + + @Override + public void setSpeculativeExecutionPolicy(SpeculativeExecutionPolicy speculativeExecutionPolicy) { + this.speculativeExecutionPolicy = speculativeExecutionPolicy; + } + + @Override + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + @Override + public void setAttemptCount(int attemptCount) { + this.attemptCount = attemptCount; + } + + @Override + public void setShardID(int shardID) { + this.shardID = shardID; + } + + @Override + public void setPeerName(String peerName) { + this.peerName = peerName; + } + + @Override + public void setPeerIP(InetAddress peerIP) { + this.peerIP = peerIP; + } + + @Override + public void setPeerPort(int peerPort) { + this.peerPort = peerPort; + } + + @Override + public void setFetchSize(int fetchSize) { + this.fetchSize = fetchSize; + } + + @Override + public void setHasMorePages(boolean hasMorePages) { + this.hasMorePages = hasMorePages; + } + + @Override + public void setRowsCount(int rowsCount) { + this.rowsCount = rowsCount; + } + + @Override + public void setStatement(String statement, int limit) { + if (currentPrecisionLevelIsAtLeast(PrecisionLevel.FULL)) { + if (statement.length() > limit) statement = statement.substring(0, limit); + this.statement = statement; + } + } + + @Override + public void setKeyspace(String keyspace) { + this.keyspace = keyspace; + } + + @Override + public void setBoundValues(String boundValues) { + this.boundValues = boundValues; + } + + @Override + public void setPartitionKey(String partitionKey) { + this.partitionKey = partitionKey; + } + + @Override + public void setTable(String table) { + this.table = table; + } + + @Override + public void setOperationType(String operationType) { + this.operationType = operationType; + } + + @Override + public void setReplicas(String replicas) { + this.replicas = replicas; + } + + @Override + public void recordException(Exception exception) { + if (this.exceptions == null) { + this.exceptions = new ArrayList(); + } + this.exceptions.add(exception); + } + + @Override + public void setStatus(StatusCode code) { + this.statusCode = code; + } + + @Override + public void setStatus(StatusCode code, String description) { + this.statusCode = code; + this.description = description; + } + + @Override + public void tracingFinished() { + this.spanFinished = true; + } + + private boolean currentPrecisionLevelIsAtLeast(PrecisionLevel requiredLevel) { + return requiredLevel.compareTo(precision) <= 0; + } + + public boolean isSpanStarted() { + return spanStarted; + } + + public boolean isSpanFinished() { + return spanFinished; + } + + public String getSpanName() { + return spanName; + } + + public ConsistencyLevel getConsistencyLevel() { + return consistencyLevel; + } + + public String getStatementType() { + return statementType; + } + + public RetryPolicy getRetryPolicy() { + return retryPolicy; + } + + public LoadBalancingPolicy getLoadBalancingPolicy() { + return loadBalancingPolicy; + } + + public SpeculativeExecutionPolicy getSpeculativeExecutionPolicy() { + return speculativeExecutionPolicy; + } + + public Integer getBatchSize() { + return batchSize; + } + + public Integer getAttemptCount() { + return attemptCount; + } + + public Integer getShardID() { + return shardID; + } + + public String getPeerName() { + return peerName; + } + + public InetAddress getPeerIP() { + return peerIP; + } + + public Integer getPeerPort() { + return peerPort; + } + + public Integer getFetchSize() { + return fetchSize; + } + + public Boolean getHasMorePages() { + return hasMorePages; + } + + public Integer getRowsCount() { + return rowsCount; + } + + public String getStatement() { + return statement; + } + + public String getKeyspace() { + return keyspace; + } + + public String getBoundValues() { + return boundValues; + } + + public String getPartitionKey() { + return partitionKey; + } + + public String getTable() { + return table; + } + + public String getOperationType() { + return operationType; + } + + public String getReplicas() { + return replicas; + } + + public StatusCode getStatusCode() { + return statusCode; + } + + public String getDescription() { + return description; + } + + public TracingInfo getParent() { + return parent; + } +} diff --git a/driver-core/src/test/java/com/datastax/driver/core/tracing/TestTracingInfoFactory.java b/driver-core/src/test/java/com/datastax/driver/core/tracing/TestTracingInfoFactory.java new file mode 100644 index 00000000000..236d0da4c7c --- /dev/null +++ b/driver-core/src/test/java/com/datastax/driver/core/tracing/TestTracingInfoFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 ScyllaDB + * + * 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 com.datastax.driver.core.tracing; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +public class TestTracingInfoFactory implements TracingInfoFactory { + private final PrecisionLevel precision; + private Collection spans = + Collections.synchronizedList(new ArrayList()); + + public TestTracingInfoFactory() { + this.precision = PrecisionLevel.NORMAL; + } + + public TestTracingInfoFactory(final PrecisionLevel precision) { + this.precision = precision; + } + + @Override + public TracingInfo buildTracingInfo() { + TracingInfo tracingInfo = new TestTracingInfo(precision); + spans.add(tracingInfo); + return tracingInfo; + } + + @Override + public TracingInfo buildTracingInfo(TracingInfo parent) { + TracingInfo tracingInfo; + + if (parent instanceof TestTracingInfo) { + final TestTracingInfo castedParent = (TestTracingInfo) parent; + tracingInfo = new TestTracingInfo(castedParent.getPrecision(), parent); + spans.add(tracingInfo); + return tracingInfo; + } + + tracingInfo = new NoopTracingInfoFactory().buildTracingInfo(); + spans.add(tracingInfo); + return tracingInfo; + } + + public Collection getSpans() { + return spans; + } +}