Funnel all paths to the database through one single method implementation (the 'Executioner') so as to ensure all calls into Cassandra are wrapped in a common way (traced, measured, cached, etc.) in a single place.
This commit is contained in:
parent
6ad99fc459
commit
142688a215
6 changed files with 179 additions and 188 deletions
|
@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
import net.helenus.core.AbstractSessionOperations;
|
||||
|
||||
public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>>
|
||||
extends AbstractStatementOperation<E, O> {
|
||||
extends AbstractStatementOperation<E, O> implements Transformational<E> {
|
||||
|
||||
public abstract E transform(ResultSet resultSet);
|
||||
|
||||
|
@ -41,16 +41,12 @@ public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>>
|
|||
}
|
||||
|
||||
public E sync() {
|
||||
ResultSet resultSet =
|
||||
sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly();
|
||||
E result = transform(resultSet);
|
||||
if (cacheable()) {
|
||||
sessionOps.cache(getCacheKey(), result);
|
||||
}
|
||||
return result;
|
||||
return Executioner.INSTANCE.<E>sync(sessionOps, options(buildStatement()),
|
||||
traceContext, this, showValues);
|
||||
}
|
||||
|
||||
public CompletableFuture<E> async() {
|
||||
return CompletableFuture.supplyAsync(this::sync);
|
||||
return Executioner.INSTANCE.<E>async(sessionOps, options(buildStatement()),
|
||||
traceContext, this, showValues);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,18 @@
|
|||
*/
|
||||
package net.helenus.core.operation;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import com.datastax.driver.core.PreparedStatement;
|
||||
import com.datastax.driver.core.ResultSet;
|
||||
import com.datastax.driver.core.ResultSetFuture;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.util.Optional;
|
||||
import net.helenus.core.AbstractSessionOperations;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>>
|
||||
extends AbstractStatementOperation<E, O> {
|
||||
extends AbstractStatementOperation<E, O> implements Transformational<Optional<E>> {
|
||||
|
||||
public AbstractOptionalOperation(AbstractSessionOperations sessionOperations) {
|
||||
super(sessionOperations);
|
||||
|
@ -52,51 +51,13 @@ public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOpe
|
|||
}
|
||||
|
||||
public Optional<E> sync() {
|
||||
Tracer tracer = this.sessionOps.getZipkinTracer();
|
||||
final Span cassandraSpan =
|
||||
(tracer != null && traceContext != null) ? tracer.newChild(traceContext) : null;
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.name("cassandra");
|
||||
cassandraSpan.start();
|
||||
|
||||
return Executioner.INSTANCE.<Optional<E>>sync(sessionOps, options(buildStatement()),
|
||||
traceContext, this, showValues);
|
||||
}
|
||||
|
||||
ResultSet resultSet =
|
||||
sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly();
|
||||
Optional<E> result = transform(resultSet);
|
||||
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.finish();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ListenableFuture<Optional<E>> async() {
|
||||
final Tracer tracer = this.sessionOps.getZipkinTracer();
|
||||
final Span cassandraSpan =
|
||||
(tracer != null && traceContext != null) ? tracer.newChild(traceContext) : null;
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.name("cassandra");
|
||||
cassandraSpan.start();
|
||||
}
|
||||
|
||||
ResultSetFuture resultSetFuture =
|
||||
sessionOps.executeAsync(options(buildStatement()), showValues);
|
||||
ListenableFuture<Optional<E>> future =
|
||||
Futures.transform(
|
||||
resultSetFuture,
|
||||
new Function<ResultSet, Optional<E>>() {
|
||||
@Override
|
||||
public Optional<E> apply(ResultSet resultSet) {
|
||||
Optional<E> result = transform(resultSet);
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.finish();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
sessionOps.getExecutor());
|
||||
|
||||
return future;
|
||||
public CompletableFuture<Optional<E>> async() {
|
||||
return Executioner.INSTANCE.<Optional<E>>async(sessionOps, options(buildStatement()),
|
||||
traceContext, this, showValues);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,18 @@
|
|||
*/
|
||||
package net.helenus.core.operation;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import com.datastax.driver.core.PreparedStatement;
|
||||
import com.datastax.driver.core.ResultSet;
|
||||
import com.datastax.driver.core.ResultSetFuture;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
import net.helenus.core.AbstractSessionOperations;
|
||||
|
||||
public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>>
|
||||
extends AbstractStatementOperation<E, O> {
|
||||
extends AbstractStatementOperation<E, O> implements Transformational<Stream<E>> {
|
||||
|
||||
public AbstractStreamOperation(AbstractSessionOperations sessionOperations) {
|
||||
super(sessionOperations);
|
||||
|
@ -52,50 +51,12 @@ public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperati
|
|||
}
|
||||
|
||||
public Stream<E> sync() {
|
||||
Tracer tracer = this.sessionOps.getZipkinTracer();
|
||||
final Span cassandraSpan =
|
||||
(tracer != null && traceContext != null) ? tracer.newChild(traceContext) : null;
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.name("cassandra");
|
||||
cassandraSpan.start();
|
||||
return Executioner.INSTANCE.<Stream<E>>sync(sessionOps, options(buildStatement()),
|
||||
traceContext, this, showValues);
|
||||
}
|
||||
|
||||
ResultSet resultSet =
|
||||
sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly();
|
||||
Stream<E> result = transform(resultSet);
|
||||
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.finish();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ListenableFuture<Stream<E>> async() {
|
||||
Tracer tracer = this.sessionOps.getZipkinTracer();
|
||||
final Span cassandraSpan =
|
||||
(tracer != null && traceContext != null) ? tracer.newChild(traceContext) : null;
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.name("cassandra");
|
||||
cassandraSpan.start();
|
||||
}
|
||||
|
||||
ResultSetFuture resultSetFuture =
|
||||
sessionOps.executeAsync(options(buildStatement()), showValues);
|
||||
ListenableFuture<Stream<E>> future =
|
||||
Futures.transform(
|
||||
resultSetFuture,
|
||||
new Function<ResultSet, Stream<E>>() {
|
||||
@Override
|
||||
public Stream<E> apply(ResultSet resultSet) {
|
||||
Stream<E> result = transform(resultSet);
|
||||
if (cassandraSpan != null) {
|
||||
cassandraSpan.finish();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
sessionOps.getExecutor());
|
||||
return future;
|
||||
public CompletableFuture<Stream<E>> async() {
|
||||
return Executioner.INSTANCE.<Stream<E>>async(sessionOps, options(buildStatement()),
|
||||
traceContext, this, showValues);
|
||||
}
|
||||
}
|
||||
|
|
61
src/main/java/net/helenus/core/operation/Executioner.java
Normal file
61
src/main/java/net/helenus/core/operation/Executioner.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
package net.helenus.core.operation;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.propagation.TraceContext;
|
||||
import com.datastax.driver.core.ResultSet;
|
||||
import com.datastax.driver.core.ResultSetFuture;
|
||||
import com.datastax.driver.core.Statement;
|
||||
import net.helenus.core.AbstractSessionOperations;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public enum Executioner {
|
||||
INSTANCE;
|
||||
|
||||
<E> E sync(
|
||||
AbstractSessionOperations session,
|
||||
Statement statement,
|
||||
TraceContext traceContext,
|
||||
Transformational<E> delegate,
|
||||
boolean showValues) {
|
||||
try {
|
||||
return this.<E>async(session, statement, traceContext, delegate, showValues).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public <E> CompletableFuture<E> async(
|
||||
AbstractSessionOperations session,
|
||||
Statement statement,
|
||||
TraceContext traceContext,
|
||||
Transformational<E> delegate,
|
||||
boolean showValues) {
|
||||
ResultSetFuture futureResultSet = session.executeAsync(statement, showValues);
|
||||
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
Tracer tracer = session.getZipkinTracer();
|
||||
final Span span =
|
||||
(tracer != null && traceContext != null) ? tracer.newChild(traceContext) : null;
|
||||
try {
|
||||
if (span != null) {
|
||||
span.name("cassandra");
|
||||
span.start();
|
||||
}
|
||||
ResultSet resultSet = futureResultSet.get(); // TODO: timeout
|
||||
E result = delegate.transform(resultSet);
|
||||
|
||||
return result;
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (span != null) {
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package net.helenus.core.operation;
|
||||
|
||||
import com.datastax.driver.core.ResultSet;
|
||||
|
||||
public interface Transformational<E> {
|
||||
E transform(ResultSet resultSet);
|
||||
}
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package net.helenus.test.integration.core.tuple;
|
||||
|
||||
|
||||
|
||||
import net.helenus.core.Helenus;
|
||||
import net.helenus.core.HelenusSession;
|
||||
import net.helenus.core.Query;
|
||||
import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest;
|
||||
|
@ -24,8 +23,6 @@ import org.junit.Assert;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.helenus.core.Helenus;
|
||||
|
||||
public class InnerTupleTest extends AbstractEmbeddedCassandraTest {
|
||||
|
||||
static PhotoAlbum photoAlbum;
|
||||
|
@ -43,20 +40,20 @@ public class InnerTupleTest extends AbstractEmbeddedCassandraTest {
|
|||
System.out.println(photoAlbum);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCruid() {
|
||||
|
||||
Photo photo = new Photo() {
|
||||
Photo photo =
|
||||
new Photo() {
|
||||
|
||||
@Override
|
||||
public byte[] blob() {
|
||||
return "jpeg".getBytes();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
PhotoFolder folder = new PhotoFolder() {
|
||||
PhotoFolder folder =
|
||||
new PhotoFolder() {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
|
@ -67,19 +64,22 @@ public class InnerTupleTest extends AbstractEmbeddedCassandraTest {
|
|||
public Photo photo() {
|
||||
return photo;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// CREATE (C)
|
||||
|
||||
session.insert()
|
||||
.value(photoAlbum::id, 123)
|
||||
.value(photoAlbum::folder, folder)
|
||||
.sync();
|
||||
session.insert().value(photoAlbum::id, 123).value(photoAlbum::folder, folder).sync();
|
||||
|
||||
// READ (R)
|
||||
|
||||
PhotoFolder actual = session.select(photoAlbum::folder).where(photoAlbum::id, Query.eq(123)).sync().findFirst().get()._1;
|
||||
PhotoFolder actual =
|
||||
session
|
||||
.select(photoAlbum::folder)
|
||||
.where(photoAlbum::id, Query.eq(123))
|
||||
.sync()
|
||||
.findFirst()
|
||||
.get()
|
||||
._1;
|
||||
|
||||
Assert.assertEquals(folder.name(), actual.name());
|
||||
|
||||
|
@ -91,7 +91,8 @@ public class InnerTupleTest extends AbstractEmbeddedCassandraTest {
|
|||
// .where(photoAlbum::id, eq(123))
|
||||
// .sync();
|
||||
|
||||
PhotoFolder expected = new PhotoFolder() {
|
||||
PhotoFolder expected =
|
||||
new PhotoFolder() {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
|
@ -102,37 +103,41 @@ public class InnerTupleTest extends AbstractEmbeddedCassandraTest {
|
|||
public Photo photo() {
|
||||
return photo;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
session.update()
|
||||
.set(photoAlbum::folder, expected)
|
||||
.where(photoAlbum::id, Query.eq(123))
|
||||
.sync();
|
||||
session.update().set(photoAlbum::folder, expected).where(photoAlbum::id, Query.eq(123)).sync();
|
||||
|
||||
actual = session.select(photoAlbum::folder).where(photoAlbum::id, Query.eq(123)).sync().findFirst().get()._1;
|
||||
actual =
|
||||
session
|
||||
.select(photoAlbum::folder)
|
||||
.where(photoAlbum::id, Query.eq(123))
|
||||
.sync()
|
||||
.findFirst()
|
||||
.get()
|
||||
._1;
|
||||
|
||||
Assert.assertEquals(expected.name(), actual.name());
|
||||
|
||||
// INSERT (I)
|
||||
// let's insert null ;)
|
||||
|
||||
session.update()
|
||||
.set(photoAlbum::folder, null)
|
||||
.where(photoAlbum::id, Query.eq(123))
|
||||
.sync();
|
||||
session.update().set(photoAlbum::folder, null).where(photoAlbum::id, Query.eq(123)).sync();
|
||||
|
||||
actual = session.select(photoAlbum::folder).where(photoAlbum::id, Query.eq(123)).sync().findFirst().get()._1;
|
||||
actual =
|
||||
session
|
||||
.select(photoAlbum::folder)
|
||||
.where(photoAlbum::id, Query.eq(123))
|
||||
.sync()
|
||||
.findFirst()
|
||||
.get()
|
||||
._1;
|
||||
Assert.assertNull(actual);
|
||||
|
||||
// DELETE (D)
|
||||
session.delete().where(photoAlbum::id, Query.eq(123)).sync();
|
||||
|
||||
long cnt = session.select(photoAlbum::folder).where(photoAlbum::id, Query.eq(123)).sync().count();
|
||||
long cnt =
|
||||
session.select(photoAlbum::folder).where(photoAlbum::id, Query.eq(123)).sync().count();
|
||||
Assert.assertEquals(0, cnt);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue